Create an initial Automation Validation Testing framework
- Setting up a runtime environment - Add to Zuul 'check' and 'gate' pipeline - Validate concepts: 1) List a few unit tests successfully 2) Execute a few simple tests successfully (pass or fail) 3) Generate docsbuild successfully - Each UC will have their own test suite and test cases and will be handled seperately - add compute-utility deployment into the gate - add logging collector of primary - add false / positive test cases - add unit-test and feature-test gates Change-Id: I55a0dcf440e9694b041d5fb8eb75bd6f4adb8913
This commit is contained in:
parent
942c19243b
commit
b0e8e0f478
14
Makefile
14
Makefile
@ -34,6 +34,7 @@ COMMIT ?= $(shell git rev-parse HEAD)
|
|||||||
DISTRO_SUFFIX ?= $(DISTRO)
|
DISTRO_SUFFIX ?= $(DISTRO)
|
||||||
IMAGE = $(DOCKER_REGISTRY)/$(IMAGE_PREFIX)/$(IMAGE_NAME):$(IMAGE_TAG)$(IMAGE_TAG_SUFFIX)
|
IMAGE = $(DOCKER_REGISTRY)/$(IMAGE_PREFIX)/$(IMAGE_NAME):$(IMAGE_TAG)$(IMAGE_TAG_SUFFIX)
|
||||||
BASE_IMAGE ?=
|
BASE_IMAGE ?=
|
||||||
|
|
||||||
# TODO(roman_g): DISTRO_SUFFIX should be autogenerated
|
# TODO(roman_g): DISTRO_SUFFIX should be autogenerated
|
||||||
# from Dockerfile extensions, see $(suffix ) Makefile function
|
# from Dockerfile extensions, see $(suffix ) Makefile function
|
||||||
ifeq "$(DISTRO_SUFFIX)" ""
|
ifeq "$(DISTRO_SUFFIX)" ""
|
||||||
@ -129,8 +130,14 @@ run_images:
|
|||||||
run_$(IMAGE_NAME):
|
run_$(IMAGE_NAME):
|
||||||
@echo "Not implemented." >&2; exit 2
|
@echo "Not implemented." >&2; exit 2
|
||||||
|
|
||||||
tests:
|
unit_tests:
|
||||||
@echo "Not implemented." >&2; exit 2
|
@echo "Run Unit Validation Testing"
|
||||||
|
./tools/run_avt.sh unit_tests
|
||||||
|
|
||||||
|
feature_tests:
|
||||||
|
@echo "Run Feature Validation Testing"
|
||||||
|
./tools/run_avt.sh feature_tests
|
||||||
|
tox -e docs
|
||||||
|
|
||||||
format:
|
format:
|
||||||
@echo "Not implemented." >&2; exit 2
|
@echo "Not implemented." >&2; exit 2
|
||||||
@ -176,5 +183,4 @@ endif
|
|||||||
|
|
||||||
.PHONY: $(CHARTS) $(IMAGES) all build chartbanner charts check-docker clean \
|
.PHONY: $(CHARTS) $(IMAGES) all build chartbanner charts check-docker clean \
|
||||||
docs dry-run-% dry-run format helm-init-% helm-install helm-lint-% \
|
docs dry-run-% dry-run format helm-init-% helm-install helm-lint-% \
|
||||||
helm-lint helm-serve images info lint run_$(IMAGE_NAME) run_images \
|
helm-lint helm-serve images info lint run_$(IMAGE_NAME) run_images
|
||||||
tests
|
|
||||||
|
4
docs/requirements.txt
Normal file
4
docs/requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
sphinx>=1.6.2
|
||||||
|
sphinx_rtd_theme==0.2.4
|
||||||
|
falcon==1.2.0
|
||||||
|
oslo.config==6.6.2
|
137
docs/source/conf.py
Normal file
137
docs/source/conf.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#
|
||||||
|
# needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.todo',
|
||||||
|
'sphinx.ext.viewcode'
|
||||||
|
]
|
||||||
|
|
||||||
|
# The suffix(es) of source filenames.
|
||||||
|
# You can specify multiple suffix as a list of string:
|
||||||
|
#
|
||||||
|
# source_suffix = ['.rst', '.md']
|
||||||
|
source_suffix = '.md'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'Porthole'
|
||||||
|
copyright = u'2020, Porthole Authors'
|
||||||
|
author = u'Porthole Authors'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = u'0.1.0'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = u'0.1.0'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#
|
||||||
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
|
# Usually you set "language" from the command line for these cases.
|
||||||
|
language = None
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
|
exclude_patterns = []
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||||
|
todo_include_todos = False
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ----------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
import sphinx_rtd_theme
|
||||||
|
html_theme = "sphinx_rtd_theme"
|
||||||
|
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#
|
||||||
|
# html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
# NOTE(mark-burnett): Currently, we don't have any static files and the
|
||||||
|
# non-existence of this directory causes a sphinx exception.
|
||||||
|
#html_static_path = ['_static']
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTMLHelp output ------------------------------------------
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'portholedoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#
|
||||||
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#
|
||||||
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#
|
||||||
|
# 'preamble': '',
|
||||||
|
|
||||||
|
# Latex figure (float) alignment
|
||||||
|
#
|
||||||
|
# 'figure_align': 'htbp',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title,
|
||||||
|
# author, documentclass [howto, manual, or own class]).
|
||||||
|
latex_documents = [
|
||||||
|
(master_doc, 'porthole.tex', u'Porthole Documentation',
|
||||||
|
u'Porthole Authors', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output ---------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
(master_doc, 'Porthole', u'Porthole Documentation',
|
||||||
|
[author], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output -------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
(master_doc, 'Porthole', u'Porthole Documentation',
|
||||||
|
author, 'Porthole',
|
||||||
|
'Tool for bootstrapping a resilient Kubernetes cluster and managing its life-cycle.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
7
docs/source/index.md
Normal file
7
docs/source/index.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Utility Containers
|
||||||
|
|
||||||
|
Utility containers give Operations staff an interface to an Airship
|
||||||
|
environment that enables them to perform routine operations and
|
||||||
|
troubleshooting activities. Utility containers support Airship
|
||||||
|
environments without exposing secrets and credentials while at
|
||||||
|
the same time restricting access to the actual containers.
|
0
kube_utility_container/__init__.py
Normal file
0
kube_utility_container/__init__.py
Normal file
0
kube_utility_container/kubecfg/__init__.py
Normal file
0
kube_utility_container/kubecfg/__init__.py
Normal file
97
kube_utility_container/kubecfg/kube_cfg.py
Normal file
97
kube_utility_container/kubecfg/kube_cfg.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 kubeconfig import KubeConfig
|
||||||
|
|
||||||
|
class KubeCfg(KubeConfig):
|
||||||
|
"""This class inherits from the KubeConfig module. It overides the
|
||||||
|
|
||||||
|
set_credentials method to add the user exec parameters to the kube config
|
||||||
|
file that is generated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def set_credentials(
|
||||||
|
self,
|
||||||
|
name,
|
||||||
|
auth_provider=None,
|
||||||
|
auth_provider_args=None,
|
||||||
|
client_certificate=None,
|
||||||
|
client_key=None,
|
||||||
|
embed_certs=None,
|
||||||
|
password=None,
|
||||||
|
token=None,
|
||||||
|
username=None,
|
||||||
|
exec_command=None,
|
||||||
|
exec_api_version=None,
|
||||||
|
exec_arg=None,
|
||||||
|
exec_env=None):
|
||||||
|
"""Creates or updates a ``user`` entry under the ``users`` entry.
|
||||||
|
|
||||||
|
In the case where you are updating an existing user, only the optional
|
||||||
|
keyword args that you pass in will be updated on the entry.
|
||||||
|
|
||||||
|
:param str name: The name of the user to add or update.
|
||||||
|
:param str auth_provider: The auth provider name to use. For example,
|
||||||
|
``oidc``, ``gcp``, etc.
|
||||||
|
:param dict auth_provider_args: Some providers support extra config
|
||||||
|
params, which can be passed in as a flat dict.
|
||||||
|
:param str client_certificate: Path to your X.509 client cert (if
|
||||||
|
using cert auth).
|
||||||
|
:param str client_key: Path to your cert's private key (if using
|
||||||
|
cert auth).
|
||||||
|
:param bool embed_certs: Combined with ``client_certificate``,
|
||||||
|
|
||||||
|
setting this to ``True`` will cause the cert to be embedded
|
||||||
|
directly in the written config. If ``False`` or unspecified,
|
||||||
|
the path to the cert will be used instead.
|
||||||
|
:param str username: Your username (if using basic auth).
|
||||||
|
:param str password: Your user's password (if using basic auth).
|
||||||
|
:param str token: Your private token (if using token auth).
|
||||||
|
:param str exec_command: The command executable name to use. For
|
||||||
|
example, ``client-keystone-auth``
|
||||||
|
:param str exec_api_version: The api version to use. For example,
|
||||||
|
``client.authentication.k8s.io/v1beta1``
|
||||||
|
"""
|
||||||
|
flags = []
|
||||||
|
if auth_provider is not None:
|
||||||
|
flags += ['--auth-provider=%s' % auth_provider]
|
||||||
|
if auth_provider_args is not None:
|
||||||
|
arg_pairs = [
|
||||||
|
"%s=%s" % (k, v) for k, v in auth_provider_args.items()
|
||||||
|
]
|
||||||
|
for arg_pair in arg_pairs:
|
||||||
|
flags += ['--auth-provider-arg=%s' % arg_pair]
|
||||||
|
if client_certificate is not None:
|
||||||
|
flags += ['--client-certificate=%s' % client_certificate]
|
||||||
|
if client_key is not None:
|
||||||
|
flags += ['--client-key=%s' % client_key]
|
||||||
|
if embed_certs is not None:
|
||||||
|
flags += ['--embed-certs=%s' % self._bool_to_cli_str(embed_certs)]
|
||||||
|
if password is not None:
|
||||||
|
flags += ['--password=%s' % password]
|
||||||
|
if token is not None:
|
||||||
|
flags += ['--token=%s' % token]
|
||||||
|
if username is not None:
|
||||||
|
flags += ['--username=%s' % username]
|
||||||
|
if exec_command is not None:
|
||||||
|
flags += ['--exec-command=%s' % exec_command]
|
||||||
|
if exec_api_version is not None:
|
||||||
|
flags += ['--exec-api-version=%s' % exec_api_version]
|
||||||
|
if exec_arg is not None:
|
||||||
|
flags += ['--exec-arg=%s' % exec_arg]
|
||||||
|
if exec_env is not None:
|
||||||
|
arg_pairs = ["%s=%s" % (k, v) for k, v in exec_env.items()]
|
||||||
|
for arg_pair in arg_pairs:
|
||||||
|
flags += ['--exec-env=%s' % arg_pair]
|
||||||
|
self._run_kubectl_config('set-credentials', name, *flags)
|
0
kube_utility_container/services/__init__.py
Normal file
0
kube_utility_container/services/__init__.py
Normal file
63
kube_utility_container/services/exceptions.py
Normal file
63
kube_utility_container/services/exceptions.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
class KubeUtilityContainerException(Exception):
|
||||||
|
"""Class for Kube Utility Container Plugin Exceptions"""
|
||||||
|
|
||||||
|
def __init__(self, error="", message=""):
|
||||||
|
self.error = error or self.__class__.error
|
||||||
|
self.message = message or self.__class__.message
|
||||||
|
super(KubeUtilityContainerException, self).__init__(
|
||||||
|
''.join([self.error, '::', self.message]))
|
||||||
|
|
||||||
|
|
||||||
|
class KubeConfigException(Exception):
|
||||||
|
"""Exception class when Kubernetes config is not found"""
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = "Kubernetes config not found: {}".format(message)
|
||||||
|
super(KubeConfigException, self).__init__(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class KubeApiException(Exception):
|
||||||
|
"""Exception class for error in accessing Kubernetes APIs"""
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = "Exception occurred while accessing Kubernetes APIs: " \
|
||||||
|
"{}".format(message)
|
||||||
|
super(KubeApiException, self).__init__(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class KubeDeploymentNotFoundException(Exception):
|
||||||
|
"""Exception class for Kube Deployment not found in a namespace"""
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = "Deployment not found Error: {}".format(message)
|
||||||
|
super(KubeDeploymentNotFoundException, self).__init__(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class KubePodNotFoundException(Exception):
|
||||||
|
"""Exception class for specific utility pod not found in running state"""
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = "Pod not found: {}".format(message)
|
||||||
|
super(KubePodNotFoundException, self).__init__(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class KubeEnvVarException(Exception):
|
||||||
|
"""Exception class for environment variable not set."""
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = "Environment Variable Not Found: {}".format(message)
|
||||||
|
super(KubeEnvVarException, self).__init__(self.message)
|
247
kube_utility_container/services/utility_container_client.py
Normal file
247
kube_utility_container/services/utility_container_client.py
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 pathlib import Path
|
||||||
|
|
||||||
|
from kube_utility_container.kubecfg.kube_cfg import KubeCfg
|
||||||
|
|
||||||
|
from kube_utility_container.services.exceptions import \
|
||||||
|
KubeApiException
|
||||||
|
from kube_utility_container.services.exceptions import \
|
||||||
|
KubeConfigException
|
||||||
|
from kube_utility_container.services.exceptions import \
|
||||||
|
KubeDeploymentNotFoundException
|
||||||
|
from kube_utility_container.services.exceptions import \
|
||||||
|
KubeEnvVarException
|
||||||
|
from kube_utility_container.services.exceptions import \
|
||||||
|
KubePodNotFoundException
|
||||||
|
|
||||||
|
from kubernetes import client as kubeclient
|
||||||
|
from kubernetes import config as kubeconf
|
||||||
|
|
||||||
|
from kubernetes.client.rest import ApiException
|
||||||
|
from kubernetes.stream import stream
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from urllib3.exceptions import MaxRetryError
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilityContainerClient(object):
|
||||||
|
"""Client to execute utilscli command on utility containers"""
|
||||||
|
|
||||||
|
NAMESPACE = 'utility'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# Initialize variables
|
||||||
|
self._corev1api_client = None
|
||||||
|
self._appsv1api_client = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _corev1api_api_client(self):
|
||||||
|
"""Property to get the V1CoreAPI client object"""
|
||||||
|
if self._corev1api_client:
|
||||||
|
return self._corev1api_client
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
kubeconf.load_kube_config(config_file=self._kubeconfig_file)
|
||||||
|
self._corev1api_client = kubeclient.CoreV1Api()
|
||||||
|
return self._corev1api_client
|
||||||
|
except EnvironmentError as err:
|
||||||
|
LOG.exception(
|
||||||
|
'Failed to load Kubernetes config file: {}'.format(err))
|
||||||
|
raise KubeConfigException(err)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _appsv1api_api_client(self):
|
||||||
|
"""Property to get the V1AppsAPI client object"""
|
||||||
|
if self._appsv1api_client:
|
||||||
|
return self._appsv1api_client
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
kubeconf.load_kube_config(config_file=self._kubeconfig_file)
|
||||||
|
self._appsv1api_client = kubeclient.AppsV1Api()
|
||||||
|
return self._appsv1api_client
|
||||||
|
except EnvironmentError as err:
|
||||||
|
LOG.exception(
|
||||||
|
'Failed to load Kubernetes config file: {}'.format(err))
|
||||||
|
raise KubeConfigException(err)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _kubeconfig_file(self):
|
||||||
|
"""Property to generate kubeconfig file from environment variables"""
|
||||||
|
key = 'KUBECONFIG'
|
||||||
|
if os.environ.get(key) is not None:
|
||||||
|
kube_conf_filename = os.environ.get(key)
|
||||||
|
else:
|
||||||
|
raise KubeEnvVarException(key)
|
||||||
|
if os.path.isfile(kube_conf_filename):
|
||||||
|
return kube_conf_filename
|
||||||
|
else:
|
||||||
|
self._prepare_kube_config(kube_conf_filename)
|
||||||
|
return kube_conf_filename
|
||||||
|
|
||||||
|
def _prepare_kube_config(self, kube_conf_filename):
|
||||||
|
"""Method to generate the kube config file"""
|
||||||
|
Path(Path.cwd() / 'etc').mkdir(exist_ok=True)
|
||||||
|
Path(kube_conf_filename).touch()
|
||||||
|
conf = KubeCfg(kube_conf_filename)
|
||||||
|
region_key = 'OS_REGION_NAME'
|
||||||
|
kube_server_key = 'KUBE_SERVER'
|
||||||
|
if os.environ.get(region_key) is not None:
|
||||||
|
server = os.environ.get(kube_server_key)
|
||||||
|
else:
|
||||||
|
raise KubeEnvVarException(kube_server_key)
|
||||||
|
|
||||||
|
if os.environ.get(region_key) is not None:
|
||||||
|
conf.set_cluster(
|
||||||
|
name=os.environ.get(region_key),
|
||||||
|
server=server,
|
||||||
|
insecure_skip_tls_verify=True)
|
||||||
|
else:
|
||||||
|
raise KubeEnvVarException(region_key)
|
||||||
|
username_key = 'OS_USERNAME'
|
||||||
|
if os.environ.get(username_key) is not None:
|
||||||
|
conf.set_context(
|
||||||
|
name='context_uc',
|
||||||
|
user=os.environ.get(username_key),
|
||||||
|
namespace='utility',
|
||||||
|
cluster=os.environ.get(region_key))
|
||||||
|
else:
|
||||||
|
raise KubeEnvVarException(username_key)
|
||||||
|
conf.use_context('context_uc')
|
||||||
|
exec_command_key = 'KUBE_KEYSTONE_AUTH_EXEC'
|
||||||
|
if os.environ.get(exec_command_key) is not None:
|
||||||
|
conf.set_credentials(
|
||||||
|
name=os.environ.get(username_key),
|
||||||
|
exec_command=os.environ.get(exec_command_key),
|
||||||
|
exec_api_version='client.authentication.k8s.io/v1beta1')
|
||||||
|
else:
|
||||||
|
raise KubeEnvVarException(exec_command_key)
|
||||||
|
|
||||||
|
def _get_deployment_selectors(self, deployment_name):
|
||||||
|
"""Method to get the deployment selectors of the deployment queried.
|
||||||
|
|
||||||
|
:param deployment_name: if specified the deployment name of the utility pod
|
||||||
|
where the utilscli command is to be executed.
|
||||||
|
:type deployment_name: string
|
||||||
|
where the utilscli command is to be executed.
|
||||||
|
:return: selectors extracted from the deployment
|
||||||
|
returned as a string in the format: "key=value, key1=value2..."
|
||||||
|
:exception:
|
||||||
|
KubeDeploymentNotFoundException -- A custom exception
|
||||||
|
KubeDeploymentNotFoundException is raised if no deployment is
|
||||||
|
found with the with the parameters namespace and deployment_name
|
||||||
|
which is passed as a field_selector.
|
||||||
|
"""
|
||||||
|
# Get a specific deployment by passing the deployment metadata name
|
||||||
|
# and the namespace
|
||||||
|
deployment = self._appsv1api_api_client.list_namespaced_deployment(
|
||||||
|
self.NAMESPACE,
|
||||||
|
field_selector='metadata.name={}'.format(deployment_name)).items
|
||||||
|
if deployment:
|
||||||
|
# Get the selectors from the deployment object returned.
|
||||||
|
selector_dict = deployment[0].spec.selector.match_labels
|
||||||
|
# Convert the selector dictionary to a string object.
|
||||||
|
selectors = ', '.join(
|
||||||
|
"{!s}={!s}".format(k, v) for (k, v) in selector_dict.items())
|
||||||
|
return selectors
|
||||||
|
else:
|
||||||
|
raise KubeDeploymentNotFoundException(
|
||||||
|
'Deployment with name {} not found in {} namespace'.format(
|
||||||
|
deployment_name, self.NAMESPACE))
|
||||||
|
|
||||||
|
def _get_utility_container(self, deployment_name):
|
||||||
|
"""Method to get a specific utility container filtered by the selectors
|
||||||
|
|
||||||
|
:param deployment_name: if specified the deployment name of the utility pod
|
||||||
|
where the utilscli command is to be executed.
|
||||||
|
:type deployment_name: string
|
||||||
|
where the utilscli command is to be executed.
|
||||||
|
:return: selectors extracted from the deployment
|
||||||
|
utility_container {V1Pod} -- Returns the first pod matched.
|
||||||
|
:exception: KubePodNotFoundException -- Exception raised if not pods are found.
|
||||||
|
"""
|
||||||
|
deployment_selectors = self._get_deployment_selectors(deployment_name)
|
||||||
|
utility_containers = self._corev1api_api_client.list_namespaced_pod(
|
||||||
|
self.NAMESPACE, label_selector=deployment_selectors).items
|
||||||
|
if utility_containers:
|
||||||
|
return utility_containers[0]
|
||||||
|
else:
|
||||||
|
raise KubePodNotFoundException(
|
||||||
|
'No Pods found in Deployment {} with selectors {} in {} '
|
||||||
|
'namespace'.format(
|
||||||
|
deployment_name, deployment_selectors, self.NAMESPACE))
|
||||||
|
|
||||||
|
def _get_exec_cmd_output(self, utility_container, ex_cmd, default=1):
|
||||||
|
"""Exec into a specific utility container, then execute the utilscli
|
||||||
|
command and return the output of the command
|
||||||
|
|
||||||
|
:params utility_container: Utility container where the
|
||||||
|
utilscli command will be executed.
|
||||||
|
:type utility_container: string
|
||||||
|
:params ex_cmd: command to be executed inside the utility container
|
||||||
|
:type ex_cmd: strings
|
||||||
|
:params default: return of cmd_output. optionally can be disabled
|
||||||
|
:type integer: 1 (true)
|
||||||
|
:type ex_cmd: strings
|
||||||
|
:return: Output of command executed in the utility container
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
container = utility_container.spec.containers[0].name
|
||||||
|
LOG.info(
|
||||||
|
'\nPod Name: {} \nNamespace: {} \nContainer Name: {} '
|
||||||
|
'\nCommand: {}'.format(
|
||||||
|
utility_container.metadata.name, self.NAMESPACE, container,
|
||||||
|
ex_cmd))
|
||||||
|
cmd_output = stream(
|
||||||
|
self._corev1api_api_client.connect_get_namespaced_pod_exec,
|
||||||
|
utility_container.metadata.name,
|
||||||
|
self.NAMESPACE,
|
||||||
|
container=container,
|
||||||
|
command=ex_cmd,
|
||||||
|
stderr=True,
|
||||||
|
stdin=False,
|
||||||
|
stdout=True,
|
||||||
|
tty=False)
|
||||||
|
LOG.info(
|
||||||
|
'Pod Name: {} Command Output: {}'.format(
|
||||||
|
utility_container.metadata.name, cmd_output))
|
||||||
|
if default is 1:
|
||||||
|
return cmd_output
|
||||||
|
except (ApiException, MaxRetryError) as err:
|
||||||
|
LOG.exception(
|
||||||
|
"An exception occurred in pod "
|
||||||
|
"exec command: {}".format(err))
|
||||||
|
raise KubeApiException(err)
|
||||||
|
|
||||||
|
def exec_cmd(self, deployment_name, cmd):
|
||||||
|
"""Get specific utility container using deployment name, call
|
||||||
|
|
||||||
|
method to execute utilscli command and return the output of
|
||||||
|
the command.
|
||||||
|
|
||||||
|
:params deployment_name: deployment name of the utility pod
|
||||||
|
where the utilscli command is to be executed.
|
||||||
|
:type deployment_name: string
|
||||||
|
:params cmd: command to be executed inside the utility container
|
||||||
|
:type cmd: strings
|
||||||
|
:return: Output of command executed in the utility container
|
||||||
|
"""
|
||||||
|
|
||||||
|
utility_container = self._get_utility_container(deployment_name)
|
||||||
|
return self._get_exec_cmd_output(utility_container, cmd)
|
0
kube_utility_container/tests/__init__.py
Normal file
0
kube_utility_container/tests/__init__.py
Normal file
0
kube_utility_container/tests/unit/__init__.py
Normal file
0
kube_utility_container/tests/unit/__init__.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 kubernetes import client
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from unittest.mock import MagicMock as Mock
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from kube_utility_container.services.exceptions import \
|
||||||
|
KubeDeploymentNotFoundException
|
||||||
|
from kube_utility_container.services.exceptions import \
|
||||||
|
KubeEnvVarException
|
||||||
|
from kube_utility_container.services.exceptions import \
|
||||||
|
KubePodNotFoundException
|
||||||
|
from kube_utility_container.services.utility_container_client import \
|
||||||
|
UtilityContainerClient
|
||||||
|
|
||||||
|
|
||||||
|
class TestUtilityContainerClient(unittest.TestCase):
|
||||||
|
"""Unit tests for Utility Container Client"""
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
'kube_utility_container.services.utility_container_client.'
|
||||||
|
'UtilityContainerClient._get_utility_container')
|
||||||
|
@patch(
|
||||||
|
'kube_utility_container.services.utility_container_client.'
|
||||||
|
'UtilityContainerClient._get_exec_cmd_output')
|
||||||
|
def test_exec_cmd(self, mock_get_exec_cmd_output, mock_utility_container):
|
||||||
|
v1_container_obj = Mock(
|
||||||
|
spec=client.V1Container(
|
||||||
|
name='ceph_utility', image='sha', image_pull_policy='Always'))
|
||||||
|
v1_spec_obj = Mock(spec=client.V1PodSpec(containers=v1_container_obj))
|
||||||
|
v1_meta_obj = Mock(
|
||||||
|
spec=client.V1ObjectMeta(
|
||||||
|
name='clcp-ceph-utility-5454794df8-xqwj5', labels='app=ceph'))
|
||||||
|
|
||||||
|
v1_pod_obj = Mock(
|
||||||
|
spec=client.V1Pod(
|
||||||
|
api_version='v1', metadata=v1_meta_obj, spec=v1_spec_obj))
|
||||||
|
mock_utility_container.return_value = v1_pod_obj
|
||||||
|
mock_get_exec_cmd_output.return_value = "Health OK"
|
||||||
|
|
||||||
|
utility_container_client = UtilityContainerClient()
|
||||||
|
response = utility_container_client.exec_cmd(
|
||||||
|
'clcp-utility', ['utilscli', 'ceph', 'status'])
|
||||||
|
|
||||||
|
self.assertIsNotNone(response)
|
||||||
|
self.assertIsInstance(response, str)
|
||||||
|
self.assertEqual(response, mock_get_exec_cmd_output.return_value)
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
'kube_utility_container.services.utility_container_client.'
|
||||||
|
'UtilityContainerClient._get_utility_container',
|
||||||
|
side_effect=KubePodNotFoundException('utility'))
|
||||||
|
def test_exec_cmd_no_utility_pods_returned(self, mock_list_pods):
|
||||||
|
mock_list_pods.return_value = []
|
||||||
|
utility_container_client = UtilityContainerClient()
|
||||||
|
with self.assertRaises(KubePodNotFoundException):
|
||||||
|
utility_container_client.exec_cmd(
|
||||||
|
'clcp-utility', ['utilscli', 'ceph', 'status'])
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
'kube_utility_container.services.utility_container_client.'
|
||||||
|
'UtilityContainerClient._get_deployment_selectors',
|
||||||
|
side_effect=KubeDeploymentNotFoundException('utility'))
|
||||||
|
@patch(
|
||||||
|
'kube_utility_container.services.utility_container_client.'
|
||||||
|
'UtilityContainerClient._corev1api_api_client')
|
||||||
|
def test_exec_cmd_no_deployments_returned(self, deployment, api_client):
|
||||||
|
deployment.return_value = []
|
||||||
|
api_client.return_value = []
|
||||||
|
utility_container_client = UtilityContainerClient()
|
||||||
|
with self.assertRaises(KubeDeploymentNotFoundException):
|
||||||
|
utility_container_client.exec_cmd(
|
||||||
|
'clcp-ceph-utility', ['utilscli', 'ceph', 'status'])
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
'kube_utility_container.services.utility_container_client.'
|
||||||
|
'UtilityContainerClient._get_deployment_selectors',
|
||||||
|
side_effect=KubeEnvVarException('utility'))
|
||||||
|
@patch(
|
||||||
|
'kube_utility_container.services.utility_container_client.'
|
||||||
|
'UtilityContainerClient._appsv1api_api_client',
|
||||||
|
side_effect=KubeEnvVarException('KUBECONFIG'))
|
||||||
|
def test_env_var_kubeconfig_not_set_raises_exception(self, deployment, api_client):
|
||||||
|
deployment.return_value = []
|
||||||
|
api_client.return_value = []
|
||||||
|
utility_container_client = UtilityContainerClient()
|
||||||
|
with self.assertRaises(KubeEnvVarException):
|
||||||
|
utility_container_client.exec_cmd(
|
||||||
|
'clcp-ceph-utility', ['utilscli', 'ceph', 'status'])
|
0
kube_utility_container/tests/utility/__init__.py
Normal file
0
kube_utility_container/tests/utility/__init__.py
Normal file
25
kube_utility_container/tests/utility/base.py
Normal file
25
kube_utility_container/tests/utility/base.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 unittest
|
||||||
|
|
||||||
|
from kube_utility_container.services.utility_container_client\
|
||||||
|
import UtilityContainerClient
|
||||||
|
|
||||||
|
|
||||||
|
class TestBase(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.client = UtilityContainerClient()
|
||||||
|
|
@ -0,0 +1,32 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 unittest
|
||||||
|
|
||||||
|
from kube_utility_container.tests.utility.base import TestBase
|
||||||
|
|
||||||
|
class TestCalicoUtilityContainer(TestBase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.deployment_name = 'calicoctl-utility'
|
||||||
|
super(TestCalicoUtilityContainer, cls).setUpClass()
|
||||||
|
|
||||||
|
def test_verify_calico_client_calicoctl_is_present(self):
|
||||||
|
"""To verify calico-utility calicoctl is present"""
|
||||||
|
exec_cmd = ['utilscli', 'calicoctl', 'version']
|
||||||
|
expected = 'Client Version:'
|
||||||
|
result_set = self.client.exec_cmd(self.deployment_name, exec_cmd)
|
||||||
|
self.assertIn(
|
||||||
|
expected, result_set, 'Unexpected value for command: {}, '
|
||||||
|
'Command Output: {}'.format(exec_cmd, result_set))
|
@ -0,0 +1,31 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 unittest
|
||||||
|
from kube_utility_container.tests.utility.base import TestBase
|
||||||
|
|
||||||
|
class TestCephUtilityContainer(TestBase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.deployment_name = 'ceph-utility'
|
||||||
|
super(TestCephUtilityContainer, cls).setUpClass()
|
||||||
|
|
||||||
|
def test_verify_ceph_is_healthy(self):
|
||||||
|
"""To verify ceph-utility is healthy"""
|
||||||
|
exec_cmd = ['utilscli', 'ceph', 'status']
|
||||||
|
expected = 'HEALTH_OK'
|
||||||
|
result_set = self.client.exec_cmd(self.deployment_name, exec_cmd)
|
||||||
|
self.assertIn(
|
||||||
|
expected, result_set, 'Unexpected value for command: {}, '
|
||||||
|
'Command Output: {}'.format(exec_cmd, result_set))
|
@ -0,0 +1,49 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 unittest
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
|
from kube_utility_container.tests.utility.base import TestBase
|
||||||
|
|
||||||
|
node = os.uname().nodename
|
||||||
|
|
||||||
|
class TestComputeUtilityContainer(TestBase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.deployment_name = 'compute-utility'
|
||||||
|
super(TestComputeUtilityContainer, cls).setUpClass()
|
||||||
|
|
||||||
|
@unittest.expectedFailure
|
||||||
|
def test_verify_compute_ovsclient_is_present(self):
|
||||||
|
"""To verify compute-utility ovs-client is present."""
|
||||||
|
cmd = 'ovs-client '
|
||||||
|
exec_cmd = ['utilscli', cmd + node, 'ovs-vsctl -V']
|
||||||
|
expected = 'ovs-vsctl'
|
||||||
|
result_set = self.client.exec_cmd(self.deployment_name, exec_cmd)
|
||||||
|
self.assertIn(
|
||||||
|
expected, result_set, 'Unexpected value for command: {}, '
|
||||||
|
'Command Output: {}'.format(exec_cmd, result_set))
|
||||||
|
|
||||||
|
@unittest.expectedFailure
|
||||||
|
def test_verify_compute_libvirtclient_is_present_on_host(self):
|
||||||
|
"""To verify compute-utility Libvirt-client is present."""
|
||||||
|
cmd = 'libvirt-client '
|
||||||
|
exec_cmd = ['utilscli', cmd + node, 'virsh list']
|
||||||
|
expected = 'Id'
|
||||||
|
result_set = self.client.exec_cmd(self.deployment_name, exec_cmd)
|
||||||
|
self.assertIn(
|
||||||
|
expected, result_set, 'Unexpected value for command: {}, '
|
||||||
|
'Command Output: {}'.format(exec_cmd, result_set))
|
@ -0,0 +1,43 @@
|
|||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 re
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from kube_utility_container.tests.utility.base import TestBase
|
||||||
|
|
||||||
|
class TestEtcdUtilityContainer(TestBase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.deployment_name = 'etcdctl-utility'
|
||||||
|
super(TestEtcdUtilityContainer, cls).setUpClass()
|
||||||
|
|
||||||
|
def test_verify_etcd_ctl_is_present(self):
|
||||||
|
"""To verify etcdctl-utility etcdctl is present."""
|
||||||
|
exec_cmd = ['utilscli', 'etcdctl', 'version']
|
||||||
|
expected = 'etcdctl version:'
|
||||||
|
result_set = self.client.exec_cmd(self.deployment_name, exec_cmd)
|
||||||
|
self.assertIn(
|
||||||
|
expected, result_set, 'Unexpected value for command: {}, '
|
||||||
|
'Command Output: {}'.format(exec_cmd, result_set))
|
||||||
|
|
||||||
|
@unittest.expectedFailure
|
||||||
|
def test_verify_etcd_endpoint_is_healthy(self):
|
||||||
|
"""To verify etcdctl-utility endpoint is healthy"""
|
||||||
|
exec_cmd = ['utilscli', 'etcdctl', 'endpoint health']
|
||||||
|
expected = 'is health: successfully'
|
||||||
|
result_set = self.client.exec_cmd(self.deployment_name, exec_cmd)
|
||||||
|
self.assertIn(
|
||||||
|
expected, result_set, 'Unexpected value for command: {}, '
|
||||||
|
'Command Output: {}'.format(exec_cmd, result_set))
|
56
requirements-frozen.txt
Normal file
56
requirements-frozen.txt
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
Babel==2.8.0
|
||||||
|
cachetools==4.0.0
|
||||||
|
certifi==2020.4.5.1
|
||||||
|
chardet==3.0.4
|
||||||
|
cliff==3.1.0
|
||||||
|
cmd2==0.8.9
|
||||||
|
coverage==4.5.1
|
||||||
|
debtcollector==2.0.1
|
||||||
|
extras==1.0.0
|
||||||
|
fixtures==3.0.0
|
||||||
|
future==0.18.2
|
||||||
|
google-auth==1.13.1
|
||||||
|
idna==2.9
|
||||||
|
iso8601==0.1.12
|
||||||
|
kubeconfig==1.0.1
|
||||||
|
kubernetes==10.0.1
|
||||||
|
linecache2==1.0.0
|
||||||
|
monotonic==1.5
|
||||||
|
msgpack==1.0.0
|
||||||
|
netaddr==0.7.19
|
||||||
|
netifaces==0.10.9
|
||||||
|
oauthlib==3.1.0
|
||||||
|
oslo.config==6.7.0
|
||||||
|
oslo.context==3.0.2
|
||||||
|
oslo.i18n==4.0.1
|
||||||
|
oslo.log==3.40.1
|
||||||
|
oslo.serialization==3.1.1
|
||||||
|
oslo.utils==4.1.1
|
||||||
|
pbr==3.1.1
|
||||||
|
prettytable==0.7.2
|
||||||
|
pyasn1==0.4.8
|
||||||
|
pyasn1-modules==0.2.8
|
||||||
|
pyinotify==0.9.6
|
||||||
|
pyparsing==2.4.7
|
||||||
|
pyperclip==1.8.0
|
||||||
|
pytest==5.4.1
|
||||||
|
python-dateutil==2.8.1
|
||||||
|
python-mimeparse==1.6.0
|
||||||
|
python-subunit==1.4.0
|
||||||
|
pytz==2019.3
|
||||||
|
PyYAML==5.3.1
|
||||||
|
requests==2.23.0
|
||||||
|
requests-oauthlib==1.3.0
|
||||||
|
rfc3986==1.4.0
|
||||||
|
rsa==4.0
|
||||||
|
six==1.14.0
|
||||||
|
stestr==2.1.1
|
||||||
|
stevedore==1.32.0
|
||||||
|
testtools==2.4.0
|
||||||
|
traceback2==1.4.0
|
||||||
|
unittest2==1.1.0
|
||||||
|
urllib3==1.25.8
|
||||||
|
voluptuous==0.11.7
|
||||||
|
wcwidth==0.1.9
|
||||||
|
websocket-client==0.57.0
|
||||||
|
wrapt==1.12.1
|
12
requirements.txt
Normal file
12
requirements.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# The order of packages is significant, because pip processes them in the order
|
||||||
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
|
# When modifying this file `tox -e freeze-req` must be run to regenerate the requirements-frozen.txt.
|
||||||
|
coverage==4.5.1
|
||||||
|
kubeconfig==1.0.1
|
||||||
|
kubernetes==10.0.1
|
||||||
|
oslo.config==6.7.0 # Apache-2.0
|
||||||
|
oslo.log==3.40.1 # Apache-2.0
|
||||||
|
pbr==3.1.1
|
||||||
|
stestr==2.1.1 # Apache-2.0
|
21
setup.cfg
Normal file
21
setup.cfg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[metadata]
|
||||||
|
name = porthole_plugin
|
||||||
|
version = 1.0.0
|
||||||
|
summary = 'This plugin consists set of testcases for utility containers based on kubernetes exec api.'
|
||||||
|
description-file = README.rst
|
||||||
|
author = The Airship Authors
|
||||||
|
author-email = airship-discuss@lists.airshipit.org
|
||||||
|
home-page = https://opendev.org/airship/porthole
|
||||||
|
|
||||||
|
classifier =
|
||||||
|
Intended Audience :: Information Technology
|
||||||
|
Intended Audience :: System Administrators
|
||||||
|
License :: OSI Approved :: Apache Software License
|
||||||
|
Operating System :: POSIX :: Linux
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Programming Language :: Python :: 3.5
|
||||||
|
Programming Language :: Python :: 3.6
|
||||||
|
|
||||||
|
[files]
|
||||||
|
packages =
|
||||||
|
porthole
|
27
setup.py
Normal file
27
setup.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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 setuptools import setup
|
||||||
|
|
||||||
|
try:
|
||||||
|
import multiprocessing # noqa
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
setup(
|
||||||
|
setup_requires=['setuptools>=17.1', 'pbr>=2.0.0'],
|
||||||
|
pbr=True
|
||||||
|
)
|
22
test-requirements.txt
Normal file
22
test-requirements.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# The order of packages is significant, because pip processes them in the order
|
||||||
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
|
# When modifying this file `tox -e freeze-testreq` must be run to regenerate the test-requirements-frozen.txt.
|
||||||
|
|
||||||
|
astroid==2.3.3
|
||||||
|
bandit==1.5.1
|
||||||
|
|
||||||
|
flake8==3.6.0
|
||||||
|
hacking==0.12.0 # Apache-2.0
|
||||||
|
|
||||||
|
coverage==4.5.1 # Apache-2.0
|
||||||
|
pylint==2.4.4
|
||||||
|
python-subunit==1.3.0 # Apache-2.0/BSD
|
||||||
|
oslotest==3.7.0 # Apache-2.0
|
||||||
|
stestr==2.1.1 # Apache-2.0
|
||||||
|
testtools==2.3.0 # MIT
|
||||||
|
mock==3.0.5
|
||||||
|
nose==1.3.7
|
||||||
|
responses==0.10.2
|
||||||
|
yapf==0.24.0
|
23
tools/gate/playbooks/make-feature-tests.yaml
Normal file
23
tools/gate/playbooks/make-feature-tests.yaml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
- hosts: primary
|
||||||
|
tasks:
|
||||||
|
- name: Execute the make target for features testing
|
||||||
|
make:
|
||||||
|
chdir: "{{ zuul.project.src_dir }}"
|
||||||
|
target: feature_tests
|
||||||
|
register: result
|
||||||
|
failed_when: result.failed
|
||||||
|
|
||||||
|
- name: Include collecting running logs
|
||||||
|
import_playbook: airship-porthole-collect-logs.yaml
|
23
tools/gate/playbooks/make-unit-tests.yaml
Normal file
23
tools/gate/playbooks/make-unit-tests.yaml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
- hosts: primary
|
||||||
|
tasks:
|
||||||
|
- name: Execute the make target for unit testing
|
||||||
|
make:
|
||||||
|
chdir: "{{ zuul.project.src_dir }}"
|
||||||
|
target: unit_tests
|
||||||
|
register: result
|
||||||
|
failed_when: result.failed
|
||||||
|
|
||||||
|
- name: Include collecting running logs
|
||||||
|
import_playbook: airship-porthole-collect-logs.yaml
|
58
tools/run_avt.sh
Executable file
58
tools/run_avt.sh
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Copyright 2020 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
set -x
|
||||||
|
TYPE=$1
|
||||||
|
VENV=$(mktemp -d)
|
||||||
|
PLUGINS=kube_utility_container
|
||||||
|
export KUBECONFIG=${KUBECONFIG:-~/.kube/config}
|
||||||
|
|
||||||
|
function setup_venv() {
|
||||||
|
sudo apt-get install libffi-dev libssl-dev -y
|
||||||
|
python3 -m venv ${VENV}
|
||||||
|
if [[ -f ${VENV}/bin/activate ]] ;then
|
||||||
|
source $VENV/bin/activate
|
||||||
|
${VENV}/bin/pip install -r requirements-frozen.txt
|
||||||
|
${VENV}/bin/python -m pip list --format=columns
|
||||||
|
kubectl get deployment -n utility
|
||||||
|
kubectl get nodes -o wide
|
||||||
|
kubectl get po --all-namespaces -o wide
|
||||||
|
stestr init
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_avt() {
|
||||||
|
setup_venv
|
||||||
|
if [[ ${TYPE} == 'unit_tests' ]] ; then
|
||||||
|
run_unit_tests
|
||||||
|
elif [[ ${TYPE} == 'feature_tests' ]] ; then
|
||||||
|
run_feature_tests
|
||||||
|
else
|
||||||
|
echo "No validating tests performed..skip"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_feature_tests() {
|
||||||
|
python -m unittest discover -s ${PLUGINS}/tests/utility/compute -v
|
||||||
|
python -m unittest discover -s ${PLUGINS}/tests/utility/etcd -v
|
||||||
|
python -m unittest discover -s ${PLUGINS}/tests/utility/calico -v
|
||||||
|
python -m unittest discover -s ${PLUGINS}/tests/utility/ceph -v
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_unit_tests() {
|
||||||
|
python -m unittest discover -s ${PLUGINS}/tests/unit/services -v
|
||||||
|
}
|
||||||
|
|
||||||
|
run_avt
|
56
tox.ini
Normal file
56
tox.ini
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
[tox]
|
||||||
|
minversion = 3.4
|
||||||
|
envlist = dev,pep8,py36,bandit,docs,list-tests
|
||||||
|
skipsdist = true
|
||||||
|
|
||||||
|
[testenv:dev]
|
||||||
|
useddevelop = True
|
||||||
|
basepython = python3
|
||||||
|
passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
|
||||||
|
setenv =
|
||||||
|
VIRTUAL_ENV={envdir}
|
||||||
|
PYTHONWARNINGS=default::DeprecationWarning
|
||||||
|
|
||||||
|
deps =
|
||||||
|
freeze-req: -r{toxinidir}/requirements-frozen.txt
|
||||||
|
list-tests: -r {toxinidir}/requirements-frozen.txt
|
||||||
|
|
||||||
|
envdir=
|
||||||
|
{toxinidir}/.tox/{envname}
|
||||||
|
|
||||||
|
commands_pre =
|
||||||
|
find . -type f -name "*.pyc"
|
||||||
|
list-tests: stestr init
|
||||||
|
|
||||||
|
[testenv:venv]
|
||||||
|
commands = {posargs}
|
||||||
|
|
||||||
|
[testenv:py36]
|
||||||
|
setenv =
|
||||||
|
PYTHONWARNING=all
|
||||||
|
deps = -r{toxinidir}/requirements-frozen.txt
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
commands =
|
||||||
|
pytest {posargs}
|
||||||
|
|
||||||
|
[testenv:bandit]
|
||||||
|
deps =
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
commands =
|
||||||
|
bandit -r {toxinidir}
|
||||||
|
|
||||||
|
[testenv:docs]
|
||||||
|
whitelist_externals = rm
|
||||||
|
deps =
|
||||||
|
-r{toxinidir}/docs/requirements.txt
|
||||||
|
commands =
|
||||||
|
rm -rf docs/build
|
||||||
|
sphinx-build -W -b html docs/source docs/build/html
|
||||||
|
|
||||||
|
[testenv:pep8]
|
||||||
|
deps =
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
commands =
|
||||||
|
yapf -rd {toxinidir} {toxinidir}/tests
|
||||||
|
flake8 {toxinidir}
|
||||||
|
bandit -r {toxinidir}
|
@ -25,6 +25,8 @@
|
|||||||
- airship-porthole-images-build-gate-openstack-utility
|
- airship-porthole-images-build-gate-openstack-utility
|
||||||
- airship-porthole-images-build-gate-postgresql-utility
|
- airship-porthole-images-build-gate-postgresql-utility
|
||||||
- airship-porthole-deploy
|
- airship-porthole-deploy
|
||||||
|
- airship-porthole-unit-tests
|
||||||
|
- airship-porthole-feature-tests
|
||||||
|
|
||||||
gate:
|
gate:
|
||||||
jobs:
|
jobs:
|
||||||
@ -39,6 +41,8 @@
|
|||||||
- airship-porthole-deploy
|
- airship-porthole-deploy
|
||||||
- airship-porthole-apparmor:
|
- airship-porthole-apparmor:
|
||||||
voting: false
|
voting: false
|
||||||
|
- airship-porthole-unit-tests
|
||||||
|
- airship-porthole-feature-tests
|
||||||
|
|
||||||
experimental:
|
experimental:
|
||||||
jobs:
|
jobs:
|
||||||
@ -130,6 +134,56 @@
|
|||||||
args:
|
args:
|
||||||
chdir: "{{ zuul.project.src_dir }}"
|
chdir: "{{ zuul.project.src_dir }}"
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: airship-porthole-unit-tests
|
||||||
|
description: |
|
||||||
|
Executes unit tests
|
||||||
|
dependencies:
|
||||||
|
- airship-porthole-deploy
|
||||||
|
run: tools/gate/playbooks/airship-porthole-gate-runner.yaml
|
||||||
|
nodeset: airship-porthole-single-node
|
||||||
|
timeout: 7200
|
||||||
|
post-run: tools/gate/playbooks/make-unit-tests.yaml
|
||||||
|
vars:
|
||||||
|
gate_scripts:
|
||||||
|
- ./tools/deployment/utilities/000-install-packages.sh
|
||||||
|
- ./tools/deployment/utilities/001-setup-apparmor-profiles.sh
|
||||||
|
- ./tools/deployment/utilities/002-deploy-k8s.sh
|
||||||
|
- ./tools/deployment/utilities/005-calicoctl-utility.sh
|
||||||
|
- ./tools/deployment/utilities/010-ceph-utility.sh
|
||||||
|
- ./tools/deployment/utilities/020-compute-utility.sh
|
||||||
|
- ./tools/deployment/utilities/030-etcdctl-utility.sh
|
||||||
|
- ./tools/deployment/utilities/040-mysqlclient-utility.sh
|
||||||
|
- ./tools/deployment/utilities/050-openstack-utility.sh
|
||||||
|
- ./tools/deployment/utilities/060-postgresql-utility.sh
|
||||||
|
args:
|
||||||
|
chdir: "{{ zuul.project.src_dir }}"
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: airship-porthole-feature-tests
|
||||||
|
description: |
|
||||||
|
Executes feature tests
|
||||||
|
dependencies:
|
||||||
|
- airship-porthole-deploy
|
||||||
|
run: tools/gate/playbooks/airship-porthole-gate-runner.yaml
|
||||||
|
nodeset: airship-porthole-single-node
|
||||||
|
timeout: 7200
|
||||||
|
post-run: tools/gate/playbooks/make-feature-tests.yaml
|
||||||
|
vars:
|
||||||
|
gate_scripts:
|
||||||
|
- ./tools/deployment/utilities/000-install-packages.sh
|
||||||
|
- ./tools/deployment/utilities/001-setup-apparmor-profiles.sh
|
||||||
|
- ./tools/deployment/utilities/002-deploy-k8s.sh
|
||||||
|
- ./tools/deployment/utilities/005-calicoctl-utility.sh
|
||||||
|
- ./tools/deployment/utilities/010-ceph-utility.sh
|
||||||
|
- ./tools/deployment/utilities/020-compute-utility.sh
|
||||||
|
- ./tools/deployment/utilities/030-etcdctl-utility.sh
|
||||||
|
- ./tools/deployment/utilities/040-mysqlclient-utility.sh
|
||||||
|
- ./tools/deployment/utilities/050-openstack-utility.sh
|
||||||
|
- ./tools/deployment/utilities/060-postgresql-utility.sh
|
||||||
|
args:
|
||||||
|
chdir: "{{ zuul.project.src_dir }}"
|
||||||
|
|
||||||
- secret:
|
- secret:
|
||||||
name: quay_credentials
|
name: quay_credentials
|
||||||
data:
|
data:
|
||||||
|
Loading…
Reference in New Issue
Block a user