Basic test of appcontroller
This patch adds basic test for AppController that was provided by developers. Test case: 1. Get AppController source on master node 2. Create AppController in k8s 3. Wait until pod with AppController is Running 4. Wait until required thirdparty resources are created 5. Create dependencies from test 6. Create definitions from test 7. Run AppController to create defined resources 8. Check if resources are created in expected order This test could be run without environment creation via oslo.config: TESTS_CONFIGS="/path/to/config.ini" Sample run of this test (${fuel_ccp_installer} is path to fuel-ccp-installer repo): KARGO_COMMIT="kubednsrc"\ AC_COMMIT="simple-test-case"\ TESTS_CONFIGS="/path/to/config.ini" py.test -k ac_linear_test Change-Id: If2970f8f25d9d42c519a4a4e307b47548b882cee
This commit is contained in:
parent
051e3d0143
commit
9748cca200
|
@ -68,7 +68,7 @@ def hardware(request, config):
|
|||
|
||||
manager = config.hardware.manager
|
||||
|
||||
if manager is None:
|
||||
if manager == 'empty':
|
||||
# No environment manager is used.
|
||||
# 'config' should contain config.underlay.ssh settings
|
||||
# 'config' should contain config.underlay.current_snapshot setting
|
||||
|
|
|
@ -45,6 +45,7 @@ from fuel_ccp_tests.managers.k8s.secrets import K8sSecretManager
|
|||
from fuel_ccp_tests.managers.k8s.serviceaccounts import \
|
||||
K8sServiceAccountManager
|
||||
from fuel_ccp_tests.managers.k8s.services import K8sServiceManager
|
||||
from fuel_ccp_tests.managers.k8s.replicasets import K8sReplicaSetManager
|
||||
|
||||
|
||||
class K8sCluster(object):
|
||||
|
@ -103,3 +104,6 @@ class K8sCluster(object):
|
|||
self._api, self._default_namespace)
|
||||
self.pvolumes = K8sPersistentVolumeManager(
|
||||
self._api, self._default_namespace)
|
||||
self.replicasets = K8sReplicaSetManager(
|
||||
self._eapi, self._default_namespace
|
||||
)
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# Copyright 2016 Mirantis, Inc.
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
from fuel_ccp_tests.managers.k8s.base import K8sBaseResource
|
||||
from fuel_ccp_tests.managers.k8s.base import K8sBaseManager
|
||||
|
||||
|
||||
class K8sReplicaSet(K8sBaseResource):
|
||||
"""docstring for K8sPod"""
|
||||
|
||||
def __repr__(self):
|
||||
return "<K8sPod: %s>" % self.name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.metadata.name
|
||||
|
||||
|
||||
class K8sReplicaSetManager(K8sBaseManager):
|
||||
"""docstring for ClassName"""
|
||||
|
||||
resource_class = K8sReplicaSet
|
||||
|
||||
def _get(self, name, namespace=None, **kwargs):
|
||||
namespace = namespace or self.namespace
|
||||
return self.api.read_namespaced_replica_set(
|
||||
name=name, namespace=namespace, **kwargs)
|
||||
|
||||
def _list(self, namespace=None, **kwargs):
|
||||
namespace = namespace or self.namespace
|
||||
return self.api.list_namespaced_replica_set(namespace=namespace,
|
||||
**kwargs)
|
||||
|
||||
def _create(self, body, namespace=None, **kwargs):
|
||||
namespace = namespace or self.namespace
|
||||
return self.api.create_namespaced_replica_set(
|
||||
body=body, namespace=namespace, **kwargs)
|
||||
|
||||
def _replace(self, body, name, namespace=None, **kwargs):
|
||||
namespace = namespace or self.namespace
|
||||
return self.api.replace_namespaced_replica_set(
|
||||
body=body, name=name, namespace=namespace, **kwargs)
|
||||
|
||||
def _delete(self, body, name, namespace=None, **kwargs):
|
||||
namespace = namespace or self.namespace
|
||||
return self.api.delete_namespaced_replica_set(
|
||||
body=body, name=name, namespace=namespace, **kwargs)
|
||||
|
||||
def _deletecollection(self, namespace=None, **kwargs):
|
||||
namespace = namespace or self.namespace
|
||||
return self.api.deletecollection_namespaced_replica_set(
|
||||
namespace=namespace, **kwargs)
|
||||
|
||||
def full_list(self, *args, **kwargs):
|
||||
lst = self._full_list(*args, **kwargs)
|
||||
return [self.resource_class(self, item) for item in lst.items]
|
||||
|
||||
def _full_list(self, **kwargs):
|
||||
return self.api.list_replica_set(**kwargs)
|
|
@ -143,11 +143,11 @@ class K8SManager(object):
|
|||
|
||||
registry_pod.wait_running()
|
||||
|
||||
def get_pod_phase(self, pod_name, namespace):
|
||||
def get_pod_phase(self, pod_name, namespace=None):
|
||||
return self.api.pods.get(
|
||||
name=pod_name, namespace=namespace).phase
|
||||
|
||||
def wait_pod_phase(self, pod_name, namespace, phase, timeout=60):
|
||||
def wait_pod_phase(self, pod_name, phase, namespace=None, timeout=60):
|
||||
"""Wait phase of pod_name from namespace while timeout
|
||||
|
||||
:param str: pod_name
|
||||
|
|
|
@ -92,9 +92,14 @@ BUILD_IMAGES = get_var_as_bool('BUILD_IMAGES', True)
|
|||
REGISTRY = os.environ.get('REGISTRY')
|
||||
IMAGES_NAMESPACE = os.environ.get('IMAGES_NAMESPACE', 'mcp')
|
||||
IMAGES_TAG = os.environ.get('IMAGES_TAG', 'latest')
|
||||
# For dnsmasq purposes
|
||||
UPSTREAM_DNS = os.environ.get('UPSTREAM_DNS', '8.8.8.8').split(',')
|
||||
# For resolv.conf entries
|
||||
NAMESERVERS = os.environ.get('NAMESERVERS', '8.8.8.8').split(',')
|
||||
SERVICE_PATH = os.environ.get('SERVICE_PATH')
|
||||
TEMPEST_SCRIPT_PATH = os.environ.get('TEMPEST_SCRIPT_PATH')
|
||||
SEARCH_DOMAINS = os.environ.get('SEARCH_DOMAINS',
|
||||
'ccp.svc.cluster.local').split(',')
|
||||
BUILDER_WORKERS = os.environ.get('BUILDER_WORKERS', '1')
|
||||
|
||||
FUEL_CCP_KEYSTONE_LOCAL_REPO = os.environ.get('FUEL_CCP_KEYSTONE_LOCAL_REPO',
|
||||
|
@ -128,3 +133,8 @@ NETCHECKER_SERVER_DIR = os.environ.get(
|
|||
NETCHECKER_AGENT_DIR = os.environ.get(
|
||||
'NETCHECKER_AGENT_DIR', os.path.join(os.getcwd(), 'mcp-netchecker-agent')
|
||||
)
|
||||
AC_COMMIT = os.environ.get("AC_COMMIT", "master")
|
||||
AC_REPO = os.environ.get("AC_REPO", "")
|
||||
AC_ZIP_URL = os.environ.get(
|
||||
"AC_ZIP_URL", "{repo}/archive/{commit}.zip".format(
|
||||
repo=AC_REPO, commit=AC_COMMIT))
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
# Copyright 2016 Mirantis, Inc.
|
||||
#
|
||||
# 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 pytest
|
||||
|
||||
from devops.helpers import helpers
|
||||
from k8sclient.client import rest
|
||||
import yaml
|
||||
|
||||
from fuel_ccp_tests import logger
|
||||
from fuel_ccp_tests import settings
|
||||
from fuel_ccp_tests.helpers import ext
|
||||
|
||||
LOG = logger.logger
|
||||
|
||||
|
||||
class AppControllerResoucesStatus(object):
|
||||
"""Helper class to check defined resources creation of AppController"""
|
||||
|
||||
resources = [
|
||||
"dependency.appcontroller.k8s1",
|
||||
"definition.appcontroller.k8s2"
|
||||
]
|
||||
|
||||
def __init__(self, k8sclient, kube_ssh, expected_result=None):
|
||||
"""
|
||||
:param k8sclient: fuel_ccp_tests.managers.k8s.cluster.K8sCluster
|
||||
:param kube_ssh: devops.helpers.ssh_client.SSHClient
|
||||
:param expected_result: list of strings in 'type/object_name' format
|
||||
"""
|
||||
super(AppControllerResoucesStatus, self).__init__()
|
||||
self.__k8s = k8sclient
|
||||
self.__managers = {
|
||||
"job": lambda: self.__k8s.jobs,
|
||||
"pod": lambda: self.__k8s.pods,
|
||||
"replicaset": lambda: self.__k8s.replicasets,
|
||||
"service": lambda: self.__k8s.services,
|
||||
}
|
||||
self.__ssh = kube_ssh
|
||||
self.__linear_order = []
|
||||
self.register_linear_objects(expected_result)
|
||||
|
||||
def register_linear_objects(self, list_resources=None):
|
||||
"""Method to register check actions for each type of resources
|
||||
|
||||
:param list_resources: list of strings "type/object"
|
||||
:raises: TypeError, KeyError, ValueError
|
||||
"""
|
||||
# The following object will return function (checker) to run
|
||||
def obj_exists(resource):
|
||||
resource_type, resource_name = resource.split('/')
|
||||
if resource_name is None:
|
||||
raise ValueError("Resource '{}' has wrong format".format(
|
||||
resource
|
||||
))
|
||||
return (
|
||||
lambda: self.__managers[resource_type]().get(
|
||||
name=resource_name) is not None)
|
||||
if list_resources:
|
||||
if not isinstance(list_resources, list):
|
||||
raise TypeError("list_resources must be a list instance!")
|
||||
result = []
|
||||
for resource in list_resources:
|
||||
try:
|
||||
res_func = obj_exists(resource)
|
||||
# To restore original name of object
|
||||
setattr(res_func, '__resource_repr', resource)
|
||||
except KeyError:
|
||||
raise KeyError(
|
||||
"Resource '{}' has unsupported type!".format(resource))
|
||||
result.append(res_func)
|
||||
self.__linear_order = result
|
||||
|
||||
def _linear_check(self, func):
|
||||
"""Action to run check
|
||||
|
||||
:param func: function to run
|
||||
"""
|
||||
# Each function must be created with this attribute
|
||||
resource_repr = getattr(func, '__resource_repr')
|
||||
# Additional flag to detect if object has already created
|
||||
must_created = getattr(func, '__to_be_created', None)
|
||||
try:
|
||||
result = func()
|
||||
if must_created is None:
|
||||
raise Exception("{} is already created!".resource_repr)
|
||||
LOG.info("{} is created".format(resource_repr))
|
||||
except rest.ApiException as err:
|
||||
LOG.debug(err)
|
||||
if must_created is None:
|
||||
setattr(func, '__to_be_created', True)
|
||||
LOG.info("{} should be created".format(resource_repr))
|
||||
result = False
|
||||
return result
|
||||
|
||||
def linear_check(self):
|
||||
"""Action to wait until all checks are done
|
||||
Each check has own timeout equals 300 secs"""
|
||||
for item in self.__linear_order:
|
||||
resource_repr = getattr(item, '__resource_repr')
|
||||
helpers.wait(
|
||||
lambda: self._linear_check(item), timeout=300, interval=2,
|
||||
timeout_msg="{} creation timeout reached".format(resource_repr)
|
||||
)
|
||||
|
||||
def thirdparty_resources(self):
|
||||
result = yaml.load(
|
||||
self.__ssh.check_call(
|
||||
"kubectl get thirdpartyresources -o yaml").stdout_str
|
||||
)
|
||||
return [item['metadata']['name'] for item in result['items']]
|
||||
|
||||
|
||||
@pytest.mark.AppController
|
||||
class TestAppController(object):
|
||||
|
||||
kube_settings = {
|
||||
"hyperkube_image_repo": settings.HYPERKUBE_IMAGE_REPO,
|
||||
"hyperkube_image_tag": settings.HYPERKUBE_IMAGE_TAG,
|
||||
"upstream_dns_servers": settings.UPSTREAM_DNS,
|
||||
"nameservers": settings.NAMESERVERS,
|
||||
"searchdomains": settings.SEARCH_DOMAINS,
|
||||
"use_hyperkube_cni": str("true"),
|
||||
}
|
||||
|
||||
@pytest.mark.ac_linear_test
|
||||
@pytest.mark.revert_snapshot(ext.SNAPSHOT.k8s_deployed)
|
||||
@pytest.mark.skipif(settings.AC_REPO == "",
|
||||
reason="ApplicationController repo is not set!")
|
||||
def test_linear(self, underlay, k8scluster):
|
||||
"""Linear test of AppController work
|
||||
|
||||
Scenario:
|
||||
1. Get AppController source on master node
|
||||
2. Create AppController in k8s
|
||||
3. Wait until pod with AppController is Running
|
||||
4. Wait until required thirdparty resources are created
|
||||
5. Create dependencies from test
|
||||
6. Create definitions from test
|
||||
7. Run AppController to create defined resources
|
||||
8. Check if resources are created in expected order
|
||||
"""
|
||||
node_name = underlay.node_names()[0]
|
||||
remote = underlay.remote(node_name=underlay.node_names()[0])
|
||||
cmd_ac_run = "kubectl exec -i k8s-appcontroller ac-run"
|
||||
|
||||
# Install additional software if it's needed
|
||||
underlay.sudo_check_call(
|
||||
cmd="which unzip || apt-get install unzip",
|
||||
node_name=node_name)
|
||||
|
||||
cmd = """wget -qO ac.zip {url} &&
|
||||
unzip -qq ac && mv k8s-AppController-{commit} k8s-AppController &&
|
||||
cd k8s-AppController && pwd""".format(
|
||||
url=settings.AC_ZIP_URL, commit=settings.AC_COMMIT)
|
||||
|
||||
LOG.info("1. Get AppController source on master node")
|
||||
ac_path = underlay.check_call(
|
||||
cmd, node_name=node_name).stdout_str
|
||||
|
||||
LOG.info("2. Create AppController in k8s")
|
||||
underlay.check_call(
|
||||
"kubectl create -f {}/manifests/appcontroller.yaml".format(
|
||||
ac_path),
|
||||
node_name=node_name)
|
||||
|
||||
LOG.info("3. Wait until pod with AppController is Running")
|
||||
k8scluster.wait_pod_phase(
|
||||
pod_name="k8s-appcontroller", phase="Running", timeout=300)
|
||||
|
||||
# Load expected order to perform future checks
|
||||
file_name = "{}/tests/linear/expected_order.yaml".format(ac_path)
|
||||
with remote.open(file_name) as f:
|
||||
expected_order = yaml.load(f.read())
|
||||
acr = AppControllerResoucesStatus(k8scluster.api, remote,
|
||||
expected_order)
|
||||
LOG.info("4. Wait until required thirdparty resources are created")
|
||||
helpers.wait(
|
||||
lambda: set(acr.thirdparty_resources()).issubset(acr.resources),
|
||||
timeout=120, interval=2, timeout_msg="Resources creation timeout"
|
||||
)
|
||||
|
||||
LOG.info("5. Create dependencies from test")
|
||||
cmd = "kubectl create -f {}/tests/linear/dependencies.yaml".format(
|
||||
ac_path)
|
||||
helpers.wait(
|
||||
lambda: underlay.check_call(
|
||||
cmd, node_name=node_name, expected=[0, 1]
|
||||
).exit_code == 0,
|
||||
timeout=30, interval=2, timeout_msg="Dependencies creation failed")
|
||||
|
||||
LOG.info("6. Create definitions from test")
|
||||
underlay.check_call(
|
||||
"kubectl create -f {}/tests/linear/definitions.yaml".format(
|
||||
ac_path),
|
||||
node_name=node_name)
|
||||
|
||||
LOG.info("7. Run AppController to create defined resources")
|
||||
underlay.check_call(cmd_ac_run, node_name=node_name)
|
||||
|
||||
LOG.info("8. Check if resources are created in expected order")
|
||||
acr.linear_check()
|
Loading…
Reference in New Issue