Remove the local tempest plugin
The plugin has been split into its own repository [1] in accordance with Queens Goal "Split Tempest Plugins into Separate Repos/Projects" [2]. This patch removes the local copy as well as the setuptools entry point. [1] https://github.com/openstack/zun-tempest-plugin [2] https://governance.openstack.org/tc/goals/queens/ split-tempest-plugins.html Implements: blueprint zun-split-tempest-plugins Change-Id: Ibfeb38b52c05a9f886dc7524d892842971cbd264
This commit is contained in:
parent
4d91ab84bb
commit
83b7fb110e
@ -32,6 +32,9 @@ set +o xtrace
|
||||
ZUN_REPO=${ZUN_REPO:-${GIT_BASE}/openstack/zun.git}
|
||||
ZUN_BRANCH=${ZUN_BRANCH:-master}
|
||||
ZUN_DIR=$DEST/zun
|
||||
ZUN_TEMPEST_PLUGIN_REPO=${ZUN_REPO:-${GIT_BASE}/openstack/zun-tempest-plugin.git}
|
||||
ZUN_TEMPEST_PLUGIN_BRANCH=${ZUN_BRANCH:-master}
|
||||
ZUN_TEMPEST_PLUGIN_DIR=$DEST/zun-tempest-plugin
|
||||
|
||||
GITREPO["python-zunclient"]=${ZUNCLIENT_REPO:-${GIT_BASE}/openstack/python-zunclient.git}
|
||||
GITBRANCH["python-zunclient"]=${ZUNCLIENT_BRANCH:-master}
|
||||
@ -296,6 +299,9 @@ function install_zunclient {
|
||||
function install_zun {
|
||||
git_clone $ZUN_REPO $ZUN_DIR $ZUN_BRANCH
|
||||
setup_develop $ZUN_DIR
|
||||
|
||||
git_clone $ZUN_TEMPEST_PLUGIN_REPO $ZUN_TEMPEST_PLUGIN_DIR $ZUN_TEMPEST_PLUGIN_BRANCH
|
||||
setup_develop $ZUN_TEMPEST_PLUGIN_DIR
|
||||
}
|
||||
|
||||
function install_etcd_server {
|
||||
|
@ -61,4 +61,4 @@ Navigate to tempest directory::
|
||||
|
||||
Run this command::
|
||||
|
||||
tox -eall-plugin -- zun.tests.tempest.api
|
||||
tox -eall-plugin -- zun_tempest_plugin.tests.tempest.api
|
||||
|
@ -85,9 +85,6 @@ zun.network.driver =
|
||||
zun.volume.driver =
|
||||
cinder = zun.volume.driver:Cinder
|
||||
|
||||
tempest.test_plugins =
|
||||
zun_tests = zun.tests.tempest.plugin:ZunTempestPlugin
|
||||
|
||||
[extras]
|
||||
osprofiler =
|
||||
osprofiler>=1.4.0 # Apache-2.0
|
||||
|
@ -27,7 +27,7 @@ export DEVSTACK_LOCAL_CONFIG+=$'\n'"ZUN_USE_UWSGI=True"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"KURYR_CONFIG_DIR=/etc/kuryr-libnetwork"
|
||||
export DEVSTACK_GATE_TEMPEST=1
|
||||
export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
|
||||
export DEVSTACK_GATE_TEMPEST_REGEX="zun.tests.tempest.api"
|
||||
export DEVSTACK_GATE_TEMPEST_REGEX="zun_tempest_plugin.tests.tempest.api"
|
||||
|
||||
if [ "$driver" = "docker" ]; then
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"ZUN_DRIVER=docker"
|
||||
|
@ -1,69 +0,0 @@
|
||||
==============
|
||||
Tempest Plugin
|
||||
==============
|
||||
|
||||
This directory contains Tempest tests to cover Zun project.
|
||||
|
||||
|
||||
Tempest installation
|
||||
--------------------
|
||||
|
||||
To install Tempest you can issue the following commands::
|
||||
|
||||
$ git clone https://git.openstack.org/openstack/tempest/
|
||||
$ cd tempest/
|
||||
$ pip install .
|
||||
|
||||
The folder you are into now will be called ``<TEMPEST_DIR>`` from now onwards.
|
||||
|
||||
Please note that although it is fully working outside a virtual environment, it
|
||||
is recommended to install within a `venv`.
|
||||
|
||||
Zun Tempest testing setup
|
||||
-------------------------
|
||||
|
||||
Before using zun tempest plugin, you need to install zun first::
|
||||
|
||||
$ pip install -e <ZUN_SRC_DIR>
|
||||
|
||||
To list all Zun tempest cases, go to tempest directory, then run::
|
||||
|
||||
$ testr list-tests zun
|
||||
|
||||
Need to adopt tempest.conf, an example as follows::
|
||||
|
||||
$ cat /etc/tempest/tempest.conf
|
||||
|
||||
[auth]
|
||||
use_dynamic_credentials=True
|
||||
admin_username=admin
|
||||
admin_password=123
|
||||
admin_project_name=admin
|
||||
|
||||
[identity]
|
||||
disable_ssl_certificate_validation=True
|
||||
uri=http://127.0.0.1:5000/v2.0/
|
||||
auth_version=v2
|
||||
region=RegionOne
|
||||
|
||||
[identity-feature-enabled]
|
||||
api_v2 = true
|
||||
api_v3 = false
|
||||
trust = false
|
||||
|
||||
[oslo_concurrency]
|
||||
lock_path = /tmp/
|
||||
|
||||
[container_management]
|
||||
catalog_type = container
|
||||
|
||||
[debug]
|
||||
trace_requests=true
|
||||
|
||||
To run only these tests in tempest, go to tempest directory, then run::
|
||||
|
||||
$ tempest run zun
|
||||
|
||||
To run a single test case, go to tempest directory, then run with test case name, e.g.::
|
||||
|
||||
$ tempest run --regex zun.tests.tempest.api.test_containers.TestContainer.test_create_list_delete
|
@ -1,287 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import contextlib
|
||||
import sys
|
||||
|
||||
from docker import errors as docker_errors
|
||||
import six
|
||||
from six.moves.urllib import parse
|
||||
from tempest import config
|
||||
from tempest.lib.common import rest_client
|
||||
from tempest.lib.services.image.v2 import images_client
|
||||
from tempest.lib.services.network import ports_client
|
||||
from tempest.lib.services.network import security_groups_client
|
||||
from tempest import manager
|
||||
|
||||
from zun.common import exception
|
||||
import zun.conf
|
||||
from zun.container.docker import utils as docker_utils
|
||||
from zun.tests.tempest.api.models import container_model
|
||||
from zun.tests.tempest.api.models import service_model
|
||||
from zun.tests.tempest import utils
|
||||
|
||||
ZUN_CONF = zun.conf.CONF
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class Manager(manager.Manager):
|
||||
|
||||
def __init__(self, credentials=None, service=None):
|
||||
super(Manager, self).__init__(credentials=credentials)
|
||||
|
||||
self.images_client = images_client.ImagesClient(
|
||||
self.auth_provider, 'image', CONF.identity.region)
|
||||
self.ports_client = ports_client.PortsClient(
|
||||
self.auth_provider, 'network', CONF.identity.region)
|
||||
self.sgs_client = security_groups_client.SecurityGroupsClient(
|
||||
self.auth_provider, 'network', CONF.identity.region)
|
||||
self.container_client = ZunClient(self.auth_provider)
|
||||
|
||||
|
||||
class ZunClient(rest_client.RestClient):
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
super(ZunClient, self).__init__(
|
||||
auth_provider=auth_provider,
|
||||
service=CONF.container_management.catalog_type,
|
||||
region=CONF.identity.region,
|
||||
disable_ssl_certificate_validation=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, resp, body, model_type):
|
||||
return resp, model_type.from_json(body)
|
||||
|
||||
@classmethod
|
||||
def containers_uri(cls, params=None):
|
||||
url = "/containers/"
|
||||
if params:
|
||||
url = cls.add_params(url, params)
|
||||
return url
|
||||
|
||||
@classmethod
|
||||
def container_uri(cls, container_id, action=None, params=None):
|
||||
"""Construct container uri
|
||||
|
||||
"""
|
||||
url = None
|
||||
if action is None:
|
||||
url = "{0}/{1}".format(cls.containers_uri(), container_id)
|
||||
else:
|
||||
url = "{0}/{1}/{2}".format(cls.containers_uri(), container_id,
|
||||
action)
|
||||
|
||||
if params:
|
||||
url = cls.add_params(url, params)
|
||||
|
||||
return url
|
||||
|
||||
@classmethod
|
||||
def add_params(cls, url, params):
|
||||
"""add_params adds dict values (params) to url as query parameters
|
||||
|
||||
:param url: base URL for the request
|
||||
:param params: dict with var:val pairs to add as parameters to URL
|
||||
:returns: url string
|
||||
"""
|
||||
url_parts = list(parse.urlparse(url))
|
||||
query = dict(parse.parse_qsl(url_parts[4]))
|
||||
query.update(params)
|
||||
url_parts[4] = parse.urlencode(query)
|
||||
return parse.urlunparse(url_parts)
|
||||
|
||||
@classmethod
|
||||
def services_uri(cls):
|
||||
url = "/services/"
|
||||
return url
|
||||
|
||||
def post_container(self, model, **kwargs):
|
||||
"""Makes POST /container request
|
||||
|
||||
"""
|
||||
resp, body = self.post(
|
||||
self.containers_uri(),
|
||||
body=model.to_json(), **kwargs)
|
||||
return self.deserialize(resp, body, container_model.ContainerEntity)
|
||||
|
||||
def run_container(self, model, **kwargs):
|
||||
resp, body = self.post(
|
||||
self.containers_uri(params={'run': True}),
|
||||
body=model.to_json(), **kwargs)
|
||||
return self.deserialize(resp, body, container_model.ContainerEntity)
|
||||
|
||||
def get_container(self, container_id):
|
||||
resp, body = self.get(self.container_uri(container_id))
|
||||
return self.deserialize(resp, body, container_model.ContainerEntity)
|
||||
|
||||
def list_containers(self, **kwargs):
|
||||
resp, body = self.get(self.containers_uri(), **kwargs)
|
||||
return self.deserialize(resp, body,
|
||||
container_model.ContainerCollection)
|
||||
|
||||
def delete_container(self, container_id, params=None, **kwargs):
|
||||
return self.delete(
|
||||
self.container_uri(container_id, params=params), **kwargs)
|
||||
|
||||
def commit_container(self, container_id, params=None, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='commit', params=params),
|
||||
None, **kwargs)
|
||||
|
||||
def start_container(self, container_id, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='start'), None, **kwargs)
|
||||
|
||||
def stop_container(self, container_id, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='stop'), None, *kwargs)
|
||||
|
||||
def pause_container(self, container_id, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='pause'), None, **kwargs)
|
||||
|
||||
def unpause_container(self, container_id, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='unpause'), None, **kwargs)
|
||||
|
||||
def kill_container(self, container_id, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='kill'), None, **kwargs)
|
||||
|
||||
def reboot_container(self, container_id, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='reboot'), None, **kwargs)
|
||||
|
||||
def exec_container(self, container_id, command, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='execute'),
|
||||
'{"command": "%s"}' % command, **kwargs)
|
||||
|
||||
def logs_container(self, container_id, **kwargs):
|
||||
return self.get(
|
||||
self.container_uri(container_id, action='logs'), None, **kwargs)
|
||||
|
||||
def update_container(self, container_id, model, **kwargs):
|
||||
resp, body = self.patch(
|
||||
self.container_uri(container_id), body=model.to_json(), **kwargs)
|
||||
return self.deserialize(resp, body, container_model.ContainerEntity)
|
||||
|
||||
def rename_container(self, container_id, model, **kwargs):
|
||||
resp, body = self.post(
|
||||
self.container_uri(container_id, action='rename'),
|
||||
body=model.to_json(), **kwargs)
|
||||
return self.deserialize(resp, body, container_model.ContainerEntity)
|
||||
|
||||
def top_container(self, container_id, **kwargs):
|
||||
return self.get(
|
||||
self.container_uri(container_id, action='top'), None, **kwargs)
|
||||
|
||||
def stats_container(self, container_id, **kwargs):
|
||||
return self.get(
|
||||
self.container_uri(container_id, action='stats'), None, **kwargs)
|
||||
|
||||
def add_security_group(self, container_id, model, **kwargs):
|
||||
return self.post(
|
||||
self.container_uri(container_id, action='add_security_group'),
|
||||
body=model.to_json(), **kwargs)
|
||||
|
||||
def list_services(self, **kwargs):
|
||||
resp, body = self.get(self.services_uri(), **kwargs)
|
||||
return self.deserialize(resp, body,
|
||||
service_model.ServiceCollection)
|
||||
|
||||
def ensure_container_in_desired_state(self, container_id, status):
|
||||
def is_container_in_desired_state():
|
||||
_, container = self.get_container(container_id)
|
||||
if container.status == status:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
utils.wait_for_condition(is_container_in_desired_state)
|
||||
|
||||
def ensure_container_deleted(self, container_id):
|
||||
def is_container_deleted():
|
||||
_, model = self.list_containers()
|
||||
container_ids = [c['uuid'] for c in model.containers]
|
||||
if container_id in container_ids:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
utils.wait_for_condition(is_container_deleted)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def docker_client(docker_auth_url):
|
||||
client_kwargs = dict()
|
||||
if not ZUN_CONF.docker.api_insecure:
|
||||
client_kwargs['ca_cert'] = CONF.docker.ca_file
|
||||
client_kwargs['client_key'] = CONF.docker.key_file
|
||||
client_kwargs['client_cert'] = CONF.docker.key_file
|
||||
|
||||
try:
|
||||
yield docker_utils.DockerHTTPClient(
|
||||
docker_auth_url,
|
||||
ZUN_CONF.docker.docker_remote_api_version,
|
||||
ZUN_CONF.docker.default_timeout,
|
||||
**client_kwargs
|
||||
)
|
||||
except docker_errors.APIError as e:
|
||||
desired_exc = exception.DockerError(error_msg=six.text_type(e))
|
||||
six.reraise(type(desired_exc), desired_exc, sys.exc_info()[2])
|
||||
|
||||
|
||||
class DockerClient(object):
|
||||
|
||||
def get_container(self, container_id,
|
||||
docker_auth_url=ZUN_CONF.docker.api_url):
|
||||
with docker_client(docker_auth_url) as docker:
|
||||
for info in docker.list_instances(inspect=True):
|
||||
if container_id in info['Name']:
|
||||
return info
|
||||
return None
|
||||
|
||||
def ensure_container_pid_changed(
|
||||
self, container_id, pid,
|
||||
docker_auth_url=ZUN_CONF.docker.api_url):
|
||||
def is_pid_changed():
|
||||
container = self.get_container(container_id,
|
||||
docker_auth_url=docker_auth_url)
|
||||
new_pid = container.get('State').get('Pid')
|
||||
if pid != new_pid:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
utils.wait_for_condition(is_pid_changed)
|
||||
|
||||
def pull_image(
|
||||
self, repo, tag=None,
|
||||
docker_auth_url=ZUN_CONF.docker.api_url):
|
||||
with docker_client(docker_auth_url) as docker:
|
||||
docker.pull(repo, tag=tag)
|
||||
|
||||
def get_image(self, name, docker_auth_url=ZUN_CONF.docker.api_url):
|
||||
with docker_client(docker_auth_url) as docker:
|
||||
return docker.get_image(name)
|
||||
|
||||
def delete_image(self, name, docker_auth_url=ZUN_CONF.docker.api_url):
|
||||
with docker_client(docker_auth_url) as docker:
|
||||
return docker.remove_image(name)
|
||||
|
||||
def list_networks(self, name,
|
||||
docker_auth_url=ZUN_CONF.docker.api_url):
|
||||
with docker_client(docker_auth_url) as docker:
|
||||
return docker.networks(names=[name])
|
||||
|
||||
def remove_network(self, name,
|
||||
docker_auth_url=ZUN_CONF.docker.api_url):
|
||||
with docker_client(docker_auth_url) as docker:
|
||||
return docker.remove_network(name)
|
@ -1,70 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
|
||||
class BaseModel(object):
|
||||
"""Superclass responsible for converting json data to/from model"""
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str):
|
||||
return cls.from_dict(json.loads(json_str))
|
||||
|
||||
def to_json(self):
|
||||
return json.dump_as_bytes(self.to_dict())
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
model = cls()
|
||||
for key in data:
|
||||
setattr(model, key, data.get(key))
|
||||
return model
|
||||
|
||||
def to_dict(self):
|
||||
result = {}
|
||||
for key in self.__dict__:
|
||||
result[key] = getattr(self, key)
|
||||
if isinstance(result[key], BaseModel):
|
||||
result[key] = result[key].to_dict()
|
||||
return result
|
||||
|
||||
def __str__(self):
|
||||
return "%s" % self.to_dict()
|
||||
|
||||
|
||||
class EntityModel(BaseModel):
|
||||
"""Superclass resposible from converting dict to instance of model"""
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
model = super(EntityModel, cls).from_dict(data)
|
||||
if hasattr(model, cls.ENTITY_NAME):
|
||||
val = getattr(model, cls.ENTITY_NAME)
|
||||
setattr(model, cls.ENTITY_NAME, cls.MODEL_TYPE.from_dict(val))
|
||||
return model
|
||||
|
||||
|
||||
class CollectionModel(BaseModel):
|
||||
"""Superclass resposible from converting dict to list of models"""
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
model = super(CollectionModel, cls).from_dict(data)
|
||||
|
||||
collection = []
|
||||
if hasattr(model, cls.COLLECTION_NAME):
|
||||
for d in getattr(model, cls.COLLECTION_NAME):
|
||||
collection.append(cls.MODEL_TYPE.from_dict(d))
|
||||
setattr(model, cls.COLLECTION_NAME, collection)
|
||||
|
||||
return model
|
@ -1,105 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import random
|
||||
import socket
|
||||
import string
|
||||
import struct
|
||||
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from zun.tests.tempest.api.models import container_model
|
||||
|
||||
|
||||
def random_int(min_int=1, max_int=100):
|
||||
return random.randrange(min_int, max_int)
|
||||
|
||||
|
||||
def gen_random_port():
|
||||
return random_int(49152, 65535)
|
||||
|
||||
|
||||
def gen_docker_volume_size(min_int=3, max_int=5):
|
||||
return random_int(min_int, max_int)
|
||||
|
||||
|
||||
def gen_fake_ssh_pubkey():
|
||||
chars = "".join(
|
||||
random.choice(string.ascii_uppercase +
|
||||
string.ascii_letters + string.digits + '/+=')
|
||||
for _ in range(372))
|
||||
return "ssh-rsa " + chars
|
||||
|
||||
|
||||
def gen_random_ip():
|
||||
return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
||||
|
||||
|
||||
def gen_url(scheme="http", domain="example.com", port=80):
|
||||
return "%s://%s:%s" % (scheme, domain, port)
|
||||
|
||||
|
||||
def container_data(default_data=None, **kwargs):
|
||||
if default_data is None:
|
||||
default_data = {
|
||||
'name': data_utils.rand_name('container'),
|
||||
'image': 'cirros:latest',
|
||||
'command': 'sleep 10000',
|
||||
'cpu': 0.1,
|
||||
'memory': '100',
|
||||
'environment': {},
|
||||
'labels': {},
|
||||
'image_driver': 'docker',
|
||||
'image_pull_policy': 'always',
|
||||
'restart_policy': {'Name': 'no'},
|
||||
'workdir': '/',
|
||||
'interactive': False,
|
||||
'security_groups': ['default'],
|
||||
}
|
||||
|
||||
default_data.update(kwargs)
|
||||
model = container_model.ContainerEntity.from_dict(default_data)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def container_patch_data(**kwargs):
|
||||
data = {
|
||||
'cpu': 0.2,
|
||||
'memory': '512',
|
||||
}
|
||||
|
||||
data.update(kwargs)
|
||||
model = container_model.ContainerPatchEntity.from_dict(data)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def container_rename_data(**kwargs):
|
||||
data = {
|
||||
'name': 'new_name',
|
||||
}
|
||||
|
||||
data.update(kwargs)
|
||||
model = container_model.ContainerPatchEntity.from_dict(data)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def container_add_sg_data(**kwargs):
|
||||
data = {
|
||||
'name': 'sg_name',
|
||||
}
|
||||
|
||||
data.update(kwargs)
|
||||
model = container_model.ContainerPatchEntity.from_dict(data)
|
||||
|
||||
return model
|
@ -1,41 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from zun.tests.tempest.api.common import base_model
|
||||
|
||||
|
||||
class ContainerData(base_model.BaseModel):
|
||||
"""Data that encapsulates container attributes"""
|
||||
pass
|
||||
|
||||
|
||||
class ContainerEntity(base_model.EntityModel):
|
||||
"""Entity Model that represents a single instance of ContainerData"""
|
||||
ENTITY_NAME = 'container'
|
||||
MODEL_TYPE = ContainerData
|
||||
|
||||
|
||||
class ContainerCollection(base_model.CollectionModel):
|
||||
"""Collection Model that represents a list of ContainerData objects"""
|
||||
COLLECTION_NAME = 'containerlists'
|
||||
MODEL_TYPE = ContainerData
|
||||
|
||||
|
||||
class ContainerPatchData(base_model.BaseModel):
|
||||
"""Data that encapsulates container update attributes"""
|
||||
pass
|
||||
|
||||
|
||||
class ContainerPatchEntity(base_model.EntityModel):
|
||||
"""Entity Model that represents a single instance of ContainerPatchData"""
|
||||
ENTITY_NAME = 'containerpatch'
|
||||
MODEL_TYPE = ContainerPatchData
|
@ -1,30 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from zun.tests.tempest.api.common import base_model
|
||||
|
||||
|
||||
class ServiceData(base_model.BaseModel):
|
||||
"""Data that encapsulates service attributes"""
|
||||
pass
|
||||
|
||||
|
||||
class ServiceEntity(base_model.EntityModel):
|
||||
"""Entity Model that represents a single instance of ServiceData"""
|
||||
ENTITY_NAME = 'service'
|
||||
MODEL_TYPE = ServiceData
|
||||
|
||||
|
||||
class ServiceCollection(base_model.CollectionModel):
|
||||
"""Collection Model that represents a list of ServiceData objects"""
|
||||
COLLECTION_NAME = 'servicelists'
|
||||
MODEL_TYPE = ServiceData
|
@ -1,511 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_utils import encodeutils
|
||||
from tempest.lib import decorators
|
||||
|
||||
from zun.tests.tempest.api import clients
|
||||
from zun.tests.tempest.api.common import datagen
|
||||
from zun.tests.tempest import base
|
||||
from zun.tests.tempest import utils
|
||||
|
||||
|
||||
class TestContainer(base.BaseZunTest):
|
||||
|
||||
@classmethod
|
||||
def get_client_manager(cls, credential_type=None, roles=None,
|
||||
force_new=None):
|
||||
manager = super(TestContainer, cls).get_client_manager(
|
||||
credential_type=credential_type,
|
||||
roles=roles,
|
||||
force_new=force_new
|
||||
)
|
||||
return clients.Manager(manager.credentials)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(TestContainer, cls).setup_clients()
|
||||
cls.container_client = cls.os_primary.container_client
|
||||
cls.docker_client = clients.DockerClient()
|
||||
cls.images_client = cls.os_primary.images_client
|
||||
cls.ports_client = cls.os_primary.ports_client
|
||||
cls.sgs_client = cls.os_primary.sgs_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestContainer, cls).resource_setup()
|
||||
|
||||
def setUp(self):
|
||||
super(TestContainer, self).setUp()
|
||||
self.containers = []
|
||||
|
||||
def tearDown(self):
|
||||
hosts = []
|
||||
_, model = self.container_client.list_containers()
|
||||
for c in model.containers:
|
||||
if c['uuid'] in self.containers:
|
||||
if c['host'] and c['host'] not in hosts:
|
||||
hosts.append(c['host'])
|
||||
self.container_client.delete_container(c['uuid'],
|
||||
params={'force': True})
|
||||
self.container_client.ensure_container_deleted(c['uuid'])
|
||||
|
||||
# cleanup the network resources
|
||||
project_id = self.container_client.tenant_id
|
||||
for host in hosts:
|
||||
# NOTE(kiennt): Default docker remote url
|
||||
# Remove networks in all hosts
|
||||
docker_base_url = self._get_docker_url(host)
|
||||
networks = self.docker_client.list_networks(project_id,
|
||||
docker_base_url)
|
||||
for network in networks:
|
||||
self.docker_client.remove_network(network['Id'],
|
||||
docker_base_url)
|
||||
|
||||
super(TestContainer, self).tearDown()
|
||||
|
||||
@decorators.idempotent_id('b8946b8c-57d5-4fdc-a09a-001d6b552725')
|
||||
def test_create_container(self):
|
||||
self._create_container()
|
||||
|
||||
@decorators.idempotent_id('b3e307d4-844b-4a57-8c60-8fb3f57aea7c')
|
||||
def test_list_containers(self):
|
||||
_, container = self._create_container()
|
||||
resp, model = self.container_client.list_containers()
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertGreater(len(model.containers), 0)
|
||||
self.assertIn(
|
||||
container.uuid,
|
||||
list([c['uuid'] for c in model.containers]))
|
||||
|
||||
@decorators.idempotent_id('0dd13c28-c5ff-4b9e-b73b-61185b410de4')
|
||||
def test_get_container(self):
|
||||
_, container = self._create_container()
|
||||
resp, model = self.container_client.get_container(container.uuid)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(container.uuid, model.uuid)
|
||||
|
||||
@decorators.idempotent_id('cef53a56-22b7-4808-b01c-06b2b7126115')
|
||||
def test_delete_container(self):
|
||||
_, container = self._create_container()
|
||||
self._delete_container(container.uuid, container.host, True)
|
||||
|
||||
@decorators.idempotent_id('ef69c9e7-0ce0-4e14-b7ec-c1dc581a3927')
|
||||
def test_run_container(self):
|
||||
self._run_container()
|
||||
|
||||
@decorators.idempotent_id('a2152d78-b6a6-4f47-8767-d83d29c6fb19')
|
||||
def test_run_container_with_minimal_params(self):
|
||||
gen_model = datagen.container_data({'image': 'nginx'})
|
||||
self._run_container(gen_model=gen_model)
|
||||
|
||||
@decorators.idempotent_id('c32f93e3-da88-4c13-be38-25d2e662a28e')
|
||||
def test_run_container_with_image_driver_glance(self):
|
||||
image = None
|
||||
try:
|
||||
docker_base_url = self._get_docker_url()
|
||||
self.docker_client.pull_image(
|
||||
'cirros', docker_auth_url=docker_base_url)
|
||||
image_data = self.docker_client.get_image(
|
||||
'cirros', docker_base_url)
|
||||
image = self.images_client.create_image(
|
||||
name='cirros', disk_format='raw', container_format='docker')
|
||||
self.images_client.store_image_file(image['id'], image_data)
|
||||
# delete the local image that was previously pulled down
|
||||
self.docker_client.delete_image('cirros', docker_base_url)
|
||||
|
||||
_, model = self._run_container(
|
||||
image='cirros', image_driver='glance')
|
||||
finally:
|
||||
if image:
|
||||
try:
|
||||
self.images_client.delete_image(image['id'])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@decorators.idempotent_id('b70bedbc-5ba2-400c-8f5f-0cf05ca17151')
|
||||
def test_run_container_with_environment(self):
|
||||
_, model = self._run_container(environment={
|
||||
'key1': 'env1', 'key2': 'env2'})
|
||||
|
||||
container = self.docker_client.get_container(
|
||||
model.uuid,
|
||||
self._get_docker_url(model.host))
|
||||
env = container.get('Config').get('Env')
|
||||
self.assertTrue('key1=env1', env)
|
||||
self.assertTrue('key2=env2', env)
|
||||
|
||||
@decorators.idempotent_id('0e59d549-58ff-440f-8704-10e223c31cbc')
|
||||
def test_run_container_with_labels(self):
|
||||
_, model = self._run_container(labels={
|
||||
'key1': 'label1', 'key2': 'label2'})
|
||||
|
||||
container = self.docker_client.get_container(
|
||||
model.uuid,
|
||||
self._get_docker_url(model.host))
|
||||
labels = container.get('Config').get('Labels')
|
||||
self.assertTrue('key1=label1', labels)
|
||||
self.assertTrue('key2=label2', labels)
|
||||
|
||||
@decorators.idempotent_id('9fc7fec0-e1a9-4f65-a5a6-dba425c1607c')
|
||||
def test_run_container_with_restart_policy(self):
|
||||
_, model = self._run_container(restart_policy={
|
||||
'Name': 'on-failure', 'MaximumRetryCount': 2})
|
||||
|
||||
container = self.docker_client.get_container(
|
||||
model.uuid,
|
||||
self._get_docker_url(model.host))
|
||||
policy = container.get('HostConfig').get('RestartPolicy')
|
||||
self.assertEqual('on-failure', policy['Name'])
|
||||
self.assertTrue(2, policy['MaximumRetryCount'])
|
||||
|
||||
@decorators.idempotent_id('58585a4f-cdce-4dbd-9741-4416d1098f94')
|
||||
def test_run_container_with_interactive(self):
|
||||
_, model = self._run_container(interactive=True)
|
||||
|
||||
container = self.docker_client.get_container(
|
||||
model.uuid,
|
||||
self._get_docker_url(model.host))
|
||||
tty = container.get('Config').get('Tty')
|
||||
stdin_open = container.get('Config').get('OpenStdin')
|
||||
self.assertIs(True, tty)
|
||||
self.assertIs(True, stdin_open)
|
||||
|
||||
@decorators.idempotent_id('f181eeda-a9d1-4b2e-9746-d6634ca81e2f')
|
||||
def test_run_container_without_security_groups(self):
|
||||
gen_model = datagen.container_data()
|
||||
delattr(gen_model, 'security_groups')
|
||||
_, model = self._run_container(gen_model=gen_model)
|
||||
sgs = self._get_all_security_groups(model)
|
||||
self.assertEqual(1, len(sgs))
|
||||
self.assertEqual('default', sgs[0])
|
||||
|
||||
@decorators.idempotent_id('f181eeda-a9d1-4b2e-9746-d6634ca81e2f')
|
||||
def test_run_container_with_security_groups(self):
|
||||
sg_name = 'test_sg'
|
||||
self.sgs_client.create_security_group(name=sg_name)
|
||||
_, model = self._run_container(security_groups=[sg_name])
|
||||
sgs = self._get_all_security_groups(model)
|
||||
self.assertEqual(1, len(sgs))
|
||||
self.assertEqual(sg_name, sgs[0])
|
||||
|
||||
@decorators.idempotent_id('c3f02fa0-fdfb-49fc-95e2-6e4dc982f9be')
|
||||
def test_commit_container(self):
|
||||
"""Test container snapshot
|
||||
|
||||
This test does the following:
|
||||
1. Create a container
|
||||
2. Create and write to a file inside the container
|
||||
3. Commit the container and upload the snapshot to Glance
|
||||
4. Create another container from the snapshot image
|
||||
5. Verify the pre-created file is there
|
||||
"""
|
||||
# This command creates a file inside the container
|
||||
command = "/bin/sh -c 'echo hello > testfile;sleep 1000000'"
|
||||
_, model = self._run_container(command=command)
|
||||
|
||||
try:
|
||||
resp, _ = self.container_client.commit_container(
|
||||
model.uuid, params={'repository': 'myrepo'})
|
||||
self.assertEqual(202, resp.status)
|
||||
self._ensure_image_active('myrepo')
|
||||
|
||||
# This command outputs the content of pre-created file
|
||||
command = "/bin/sh -c 'cat testfile;sleep 1000000'"
|
||||
_, model = self._run_container(
|
||||
image="myrepo", image_driver="glance", command=command)
|
||||
resp, body = self.container_client.logs_container(model.uuid)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue('hello' in encodeutils.safe_decode(body))
|
||||
finally:
|
||||
try:
|
||||
response = self.images_client.list_images()
|
||||
for image in response['images']:
|
||||
if (image['name'] == 'myrepo' and
|
||||
image['container_format'] == 'docker'):
|
||||
self.images_client.delete_image(image['id'])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _ensure_image_active(self, image_name):
|
||||
def is_image_in_desired_state():
|
||||
response = self.images_client.list_images()
|
||||
for image in response['images']:
|
||||
if (image['name'] == image_name and
|
||||
image['container_format'] == 'docker' and
|
||||
image['status'] == 'active'):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
utils.wait_for_condition(is_image_in_desired_state)
|
||||
|
||||
@decorators.idempotent_id('3fa024ef-aba1-48fe-9682-0d6b7854faa3')
|
||||
def test_start_stop_container(self):
|
||||
_, model = self._run_container()
|
||||
|
||||
resp, _ = self.container_client.stop_container(model.uuid)
|
||||
self.assertEqual(202, resp.status)
|
||||
self.container_client.ensure_container_in_desired_state(
|
||||
model.uuid, 'Stopped')
|
||||
self.assertEqual('Stopped',
|
||||
self._get_container_state(model.uuid, model.host))
|
||||
|
||||
resp, _ = self.container_client.start_container(model.uuid)
|
||||
self.assertEqual(202, resp.status)
|
||||
self.container_client.ensure_container_in_desired_state(
|
||||
model.uuid, 'Running')
|
||||
self.assertEqual('Running',
|
||||
self._get_container_state(model.uuid, model.host))
|
||||
|
||||
@decorators.idempotent_id('b5f39756-8898-4e0e-a48b-dda0a06b66b6')
|
||||
def test_pause_unpause_container(self):
|
||||
_, model = self._run_container()
|
||||
|
||||
resp, _ = self.container_client.pause_container(model.uuid)
|
||||
self.assertEqual(202, resp.status)
|
||||
self.container_client.ensure_container_in_desired_state(
|
||||
model.uuid, 'Paused')
|
||||
self.assertEqual('Paused',
|
||||
self._get_container_state(model.uuid, model.host))
|
||||
|
||||
resp, _ = self.container_client.unpause_container(model.uuid)
|
||||
self.assertEqual(202, resp.status)
|
||||
self.container_client.ensure_container_in_desired_state(
|
||||
model.uuid, 'Running')
|
||||
self.assertEqual('Running',
|
||||
self._get_container_state(model.uuid, model.host))
|
||||
|
||||
@decorators.idempotent_id('6179a588-3d48-4372-9599-f228411d1449')
|
||||
def test_kill_container(self):
|
||||
_, model = self._run_container()
|
||||
|
||||
resp, _ = self.container_client.kill_container(model.uuid)
|
||||
self.assertEqual(202, resp.status)
|
||||
self.container_client.ensure_container_in_desired_state(
|
||||
model.uuid, 'Stopped')
|
||||
self.assertEqual('Stopped',
|
||||
self._get_container_state(model.uuid, model.host))
|
||||
|
||||
@decorators.idempotent_id('c2e54321-0a70-4331-ba62-9dcaa75ac250')
|
||||
def test_reboot_container(self):
|
||||
_, model = self._run_container()
|
||||
docker_base_url = self._get_docker_url(model.host)
|
||||
container = self.docker_client.get_container(model.uuid,
|
||||
docker_base_url)
|
||||
pid = container.get('State').get('Pid')
|
||||
|
||||
resp, _ = self.container_client.reboot_container(model.uuid)
|
||||
self.assertEqual(202, resp.status)
|
||||
self.docker_client.ensure_container_pid_changed(model.uuid, pid,
|
||||
docker_base_url)
|
||||
self.assertEqual('Running',
|
||||
self._get_container_state(model.uuid, model.host))
|
||||
# assert pid is changed
|
||||
container = self.docker_client.get_container(model.uuid,
|
||||
docker_base_url)
|
||||
self.assertNotEqual(pid, container.get('State').get('Pid'))
|
||||
|
||||
@decorators.idempotent_id('8a591ff8-6793-427f-82a6-e3921d8b4f81')
|
||||
def test_exec_container(self):
|
||||
_, model = self._run_container()
|
||||
resp, body = self.container_client.exec_container(model.uuid,
|
||||
command='echo hello')
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue('hello' in encodeutils.safe_decode(body))
|
||||
|
||||
@decorators.idempotent_id('a912ca23-14e7-442f-ab15-e05aaa315204')
|
||||
def test_logs_container(self):
|
||||
_, model = self._run_container(
|
||||
command="/bin/sh -c 'echo hello;sleep 1000000'")
|
||||
resp, body = self.container_client.logs_container(model.uuid)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue('hello' in encodeutils.safe_decode(body))
|
||||
|
||||
@decorators.idempotent_id('d383f359-3ebd-40ef-9dc5-d36922790230')
|
||||
def test_update_container(self):
|
||||
_, model = self._run_container(cpu=0.1, memory=100)
|
||||
self.assertEqual('100M', model.memory)
|
||||
self.assertEqual(0.1, model.cpu)
|
||||
docker_base_url = self._get_docker_url(model.host)
|
||||
container = self.docker_client.get_container(model.uuid,
|
||||
docker_base_url)
|
||||
self._assert_resource_constraints(container, cpu=0.1, memory=100)
|
||||
|
||||
gen_model = datagen.container_patch_data(cpu=0.2, memory=200)
|
||||
resp, model = self.container_client.update_container(model.uuid,
|
||||
gen_model)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual('200M', model.memory)
|
||||
self.assertEqual(0.2, model.cpu)
|
||||
container = self.docker_client.get_container(model.uuid,
|
||||
docker_base_url)
|
||||
self._assert_resource_constraints(container, cpu=0.2, memory=200)
|
||||
|
||||
@decorators.idempotent_id('b218bea7-f19b-499f-9819-c7021ffc59f4')
|
||||
def test_rename_container(self):
|
||||
_, model = self._run_container(name='container1')
|
||||
self.assertEqual('container1', model.name)
|
||||
gen_model = datagen.container_rename_data(name='container2')
|
||||
resp, model = self.container_client.rename_container(model.uuid,
|
||||
gen_model)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual('container2', model.name)
|
||||
|
||||
@decorators.idempotent_id('142b7716-0b21-41ed-b47d-a42fba75636b')
|
||||
def test_top_container(self):
|
||||
_, model = self._run_container(
|
||||
command="/bin/sh -c 'sleep 1000000'")
|
||||
resp, body = self.container_client.top_container(model.uuid)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue('sleep 1000000' in encodeutils.safe_decode(body))
|
||||
|
||||
@decorators.idempotent_id('09638306-b501-4803-aafa-7e8025632cef')
|
||||
def test_stats_container(self):
|
||||
_, model = self._run_container()
|
||||
resp, body = self.container_client.stats_container(model.uuid)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue('NET I/O(B)' in encodeutils.safe_decode(body))
|
||||
self.assertTrue('CONTAINER' in encodeutils.safe_decode(body))
|
||||
self.assertTrue('MEM LIMIT(MiB)' in encodeutils.safe_decode(body))
|
||||
self.assertTrue('CPU %' in encodeutils.safe_decode(body))
|
||||
self.assertTrue('MEM USAGE(MiB)' in encodeutils.safe_decode(body))
|
||||
self.assertTrue('MEM %' in encodeutils.safe_decode(body))
|
||||
self.assertTrue('BLOCK I/O(B)' in encodeutils.safe_decode(body))
|
||||
|
||||
@decorators.idempotent_id('b3b9cf17-82ad-4c1b-a4af-8210a778a33e')
|
||||
def test_add_sg_to_container(self):
|
||||
_, model = self._run_container()
|
||||
sgs = self._get_all_security_groups(model)
|
||||
self.assertEqual(1, len(sgs))
|
||||
self.assertEqual('default', sgs[0])
|
||||
|
||||
sg_name = 'test_add_sg'
|
||||
self.sgs_client.create_security_group(name=sg_name)
|
||||
gen_model = datagen.container_add_sg_data(name=sg_name)
|
||||
resp, body = self.container_client.add_security_group(
|
||||
model.uuid, gen_model)
|
||||
self.assertEqual(202, resp.status)
|
||||
|
||||
def assert_security_group_is_added():
|
||||
sgs = self._get_all_security_groups(model)
|
||||
if len(sgs) == 2:
|
||||
self.assertTrue('default' in sgs)
|
||||
self.assertTrue(sg_name in sgs)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
utils.wait_for_condition(assert_security_group_is_added)
|
||||
|
||||
def _assert_resource_constraints(self, container, cpu=None, memory=None):
|
||||
if cpu is not None:
|
||||
cpu_quota = container.get('HostConfig').get('CpuQuota')
|
||||
self.assertEqual(int(cpu * 100000), cpu_quota)
|
||||
cpu_period = container.get('HostConfig').get('CpuPeriod')
|
||||
self.assertEqual(100000, cpu_period)
|
||||
if memory is not None:
|
||||
docker_memory = container.get('HostConfig').get('Memory')
|
||||
self.assertEqual(memory * 1024 * 1024, docker_memory)
|
||||
|
||||
def _create_container(self, **kwargs):
|
||||
gen_model = datagen.container_data(**kwargs)
|
||||
resp, model = self.container_client.post_container(gen_model)
|
||||
self.containers.append(model.uuid)
|
||||
self.assertEqual(202, resp.status)
|
||||
# Wait for container to finish creation
|
||||
self.container_client.ensure_container_in_desired_state(
|
||||
model.uuid, 'Created')
|
||||
|
||||
# Assert the container is created
|
||||
resp, model = self.container_client.get_container(model.uuid)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual('Created', model.status)
|
||||
self.assertEqual('Created', self._get_container_state(model.uuid,
|
||||
model.host))
|
||||
return resp, model
|
||||
|
||||
def _run_container(self, gen_model=None, **kwargs):
|
||||
if gen_model is None:
|
||||
gen_model = datagen.container_data(**kwargs)
|
||||
resp, model = self.container_client.run_container(gen_model)
|
||||
self.containers.append(model.uuid)
|
||||
self.assertEqual(202, resp.status)
|
||||
# Wait for container to started
|
||||
self.container_client.ensure_container_in_desired_state(
|
||||
model.uuid, 'Running')
|
||||
|
||||
# Assert the container is started
|
||||
resp, model = self.container_client.get_container(model.uuid)
|
||||
self.assertEqual('Running', model.status)
|
||||
self.assertEqual('Running', self._get_container_state(model.uuid,
|
||||
model.host))
|
||||
self.assertIsNotNone(model.host)
|
||||
return resp, model
|
||||
|
||||
def _delete_container(self, container_id, container_host, force=False):
|
||||
resp, _ = self.container_client.delete_container(
|
||||
container_id, params={'force': force})
|
||||
self.assertEqual(204, resp.status)
|
||||
self.container_client.ensure_container_deleted(container_id)
|
||||
container = self.docker_client.get_container(
|
||||
container_id, self._get_docker_url(container_host))
|
||||
self.assertIsNone(container)
|
||||
|
||||
def _get_container_state(self, container_id, docker_host=None):
|
||||
if docker_host is not None:
|
||||
container = self.docker_client.get_container(
|
||||
container_id, self._get_docker_url(docker_host))
|
||||
else:
|
||||
container = self.docker_client.get_container(container_id)
|
||||
status = container.get('State')
|
||||
if status.get('Error') is True:
|
||||
return 'Error'
|
||||
elif status.get('Paused'):
|
||||
return 'Paused'
|
||||
elif status.get('Running'):
|
||||
return 'Running'
|
||||
elif status.get('Status') == 'created':
|
||||
return 'Created'
|
||||
else:
|
||||
return 'Stopped'
|
||||
|
||||
def _get_all_security_groups(self, container):
|
||||
# find all neutron ports of this container
|
||||
port_ids = set()
|
||||
for addrs_list in container.addresses.values():
|
||||
for addr in addrs_list:
|
||||
port_id = addr['port']
|
||||
port_ids.add(port_id)
|
||||
|
||||
# find all security groups of this container
|
||||
sg_ids = set()
|
||||
for port_id in port_ids:
|
||||
port = self.ports_client.show_port(port_id)
|
||||
for sg in port['port']['security_groups']:
|
||||
sg_ids.add(sg)
|
||||
|
||||
sg_names = []
|
||||
for sg_id in sg_ids:
|
||||
sg = self.sgs_client.show_security_group(sg_id)
|
||||
sg_names.append(sg['security_group']['name'])
|
||||
|
||||
return sg_names
|
||||
|
||||
def _get_docker_url(self, host='localhost', protocol='tcp', port='2375'):
|
||||
# NOTE(kiennt): By default, devstack-plugin-container will
|
||||
# set docker_api_url = {
|
||||
# "unix://$DOCKER_ENGINE_SOCKET_FILE",
|
||||
# "tcp://0.0.0.0:$DOCKER_ENGINE_PORT"
|
||||
# }
|
||||
base_url = '{}://{}:{}' . format(protocol, host, port)
|
||||
return base_url
|
@ -1,50 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from zun.tests.tempest.api import clients
|
||||
from zun.tests.tempest import base
|
||||
|
||||
|
||||
class TestService(base.BaseZunTest):
|
||||
|
||||
@classmethod
|
||||
def get_client_manager(cls, credential_type=None, roles=None,
|
||||
force_new=None):
|
||||
|
||||
manager = super(TestService, cls).get_client_manager(
|
||||
credential_type=credential_type,
|
||||
roles=roles,
|
||||
force_new=force_new
|
||||
)
|
||||
return clients.Manager(manager.credentials)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
|
||||
super(TestService, cls).setup_clients()
|
||||
cls.container_client = cls.os_primary.container_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
|
||||
super(TestService, cls).resource_setup()
|
||||
|
||||
# TODO(pksingh): currently functional test doesn't support
|
||||
# policy, will write another test after
|
||||
# implementing policy in functional tests
|
||||
@decorators.idempotent_id('a04f61f2-15ae-4200-83b7-1f311b101f36')
|
||||
def test_service_list(self):
|
||||
self.assertRaises(exceptions.Forbidden,
|
||||
self.container_client.list_services)
|
@ -1,35 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class BaseZunTest(test.BaseTestCase):
|
||||
|
||||
credentials = ['primary']
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseZunTest, cls).skip_checks()
|
||||
if not CONF.service_available.zun:
|
||||
skip_msg = 'Zun is disabled'
|
||||
raise cls.skipException(skip_msg)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseZunTest, cls).setup_clients()
|
||||
pass
|
@ -1,31 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
service_option = cfg.BoolOpt("zun",
|
||||
default=True,
|
||||
help="Whether or not zun is expected to be "
|
||||
"available")
|
||||
|
||||
container_management_group = cfg.OptGroup(
|
||||
name="container_management", title="Container Management Service Options")
|
||||
|
||||
ContainerManagementGroup = [
|
||||
cfg.StrOpt("catalog_type",
|
||||
default="container",
|
||||
help="Catalog type of the container management service."),
|
||||
cfg.IntOpt("wait_timeout",
|
||||
default=60,
|
||||
help="Waiting time for a specific status, in seconds.")
|
||||
]
|
@ -1,41 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import os
|
||||
|
||||
from tempest.test_discover import plugins
|
||||
|
||||
from zun.tests.tempest import config as config_zun
|
||||
|
||||
|
||||
class ZunTempestPlugin(plugins.TempestPlugin):
|
||||
def load_tests(self):
|
||||
base_path = os.path.split(os.path.dirname(
|
||||
os.path.abspath(__file__)))[0]
|
||||
base_path += '/../..'
|
||||
test_dir = "zun/tests/tempest"
|
||||
full_test_dir = os.path.join(base_path, test_dir)
|
||||
return full_test_dir, base_path
|
||||
|
||||
def register_opts(self, conf):
|
||||
conf.register_opt(config_zun.service_option,
|
||||
group='service_available')
|
||||
conf.register_group(config_zun.container_management_group)
|
||||
conf.register_opts(config_zun.ContainerManagementGroup,
|
||||
group='container_management')
|
||||
|
||||
def get_opt_lists(self):
|
||||
return [(config_zun.container_management_group.name,
|
||||
config_zun.ContainerManagementGroup),
|
||||
('service_available', [config_zun.service_option])]
|
@ -1,25 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import time
|
||||
|
||||
|
||||
def wait_for_condition(condition, interval=2, timeout=60):
|
||||
start_time = time.time()
|
||||
end_time = time.time() + timeout
|
||||
while time.time() < end_time:
|
||||
result = condition()
|
||||
if result:
|
||||
return result
|
||||
time.sleep(interval)
|
||||
raise Exception(("Timed out after %s seconds. Started on %s and ended "
|
||||
"on %s") % (timeout, start_time, end_time))
|
Loading…
Reference in New Issue
Block a user