Introduce Kubernetes upgrade metadata for stx apps
This commit handles Kubernetes upgrade related metadata for StarlingX applications. The metadata retrieved is parsed, validated and stored into the appropriated variables for future use. The new metadata section introduced has the following form: k8s_upgrades: auto_update: true/false timing: pre/post This new block aims to inform to the Application Framework whether apps should be automatically updated (auto_update: true/false) if a Kubernetes upgrade is taking place. It also informs when applications should be updated, either during kube-upgrade-start (timing: pre) or during kube-upgrade-complete (timing: post). In addition, improvements were made to the already existing metadata section: supported_k8s_version: minimum: <version> maximum: <version> A bug was found on the existing method that checks the supported Kubernetes version. An exception was being raised when comparing different formats such as 'v1.0.0' and '1.0.0'. This bug was fixed by standardizing the formats on the comparison code. It is not the goal of this commit to implement the logic to check whether an app should be updated based on the active Kubernetes version. Test plan: PASS: Create a test application containing new valid metadata Upload the test application Apply the test application PASS: Create a test application without supported_k8s_version:minimum Upload the test application Check if a warning message was raised on the logs PASS: Create a test application without supported_k8s_version:minimum Move test application tarball to Helm applications folder Wait for the auto update process to start Check if a warning message was raised on the logs Check if the application was successfully updated PASS: Create a test application without the k8s_upgrades section Check if k8s_upgrades:auto_update defaults to true Check if k8s_upgrades:timing defaults to false Check if a warning message was raised on the logs Check if the application was successfully updated Story: 2010929 Task: 48929 Change-Id: I54362b036b25b6f42a18a2a29e43e2936a8a328d Signed-off-by: Igor Soares <Igor.PiresSoares@windriver.com>
This commit is contained in:
parent
74b727e502
commit
3511174f95
|
@ -1958,6 +1958,10 @@ APP_METADATA_SUPPORTED_K8S_VERSION = 'supported_k8s_version'
|
|||
APP_METADATA_SUPPORTED_RELEASES = 'supported_releases'
|
||||
APP_METADATA_MINIMUM = 'minimum'
|
||||
APP_METADATA_MAXIMUM = 'maximum'
|
||||
APP_METADATA_K8S_UPGRADES = 'k8s_upgrades'
|
||||
APP_METADATA_K8S_AUTO_UPDATE_DEFAULT_VALUE = True
|
||||
APP_METADATA_TIMING = 'timing'
|
||||
APP_METADATA_TIMING_DEFAULT_VALUE = 'post'
|
||||
|
||||
APP_EVALUATE_REAPPLY_TYPE_HOST_ADD = 'host-add'
|
||||
APP_EVALUATE_REAPPLY_TYPE_HOST_DELETE = 'host-delete'
|
||||
|
|
|
@ -266,8 +266,12 @@ def is_kube_version_supported(kube_version, min_version=None, max_version=None):
|
|||
|
||||
:returns bool: True if k8s version is supported
|
||||
"""
|
||||
if ((min_version is not None and LooseVersion(kube_version) < LooseVersion(min_version)) or
|
||||
(max_version is not None and LooseVersion(kube_version) > LooseVersion(max_version))):
|
||||
|
||||
kube_version = kube_version.strip().lstrip('v')
|
||||
if ((min_version is not None and LooseVersion(kube_version) <
|
||||
LooseVersion(min_version.strip().lstrip('v'))) or
|
||||
(max_version is not None and LooseVersion(kube_version) >
|
||||
LooseVersion(max_version.strip().lstrip('v')))):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
|
@ -2224,6 +2224,9 @@ def find_metadata_file(path, metadata_file, upgrade_from_release=None):
|
|||
supported_k8s_version:
|
||||
minimum: <version>
|
||||
maximum: <version>
|
||||
k8s_upgrades:
|
||||
auto_update: <true/false/yes/no>
|
||||
timing: <pre/post>
|
||||
supported_releases:
|
||||
<release>:
|
||||
- <patch.1>
|
||||
|
@ -2278,6 +2281,42 @@ def find_metadata_file(path, metadata_file, upgrade_from_release=None):
|
|||
a monitoring task.
|
||||
Default value is zero (no adjustment)
|
||||
"""
|
||||
|
||||
def verify_auto_update(parent):
|
||||
""" Validate the auto_update field of a given parent section
|
||||
|
||||
:param parent: parent section that contains the auto_update field
|
||||
to be verified
|
||||
"""
|
||||
try:
|
||||
auto_update = \
|
||||
parent[constants.APP_METADATA_AUTO_UPDATE]
|
||||
if not is_valid_boolstr(auto_update):
|
||||
raise exception.SysinvException(_(
|
||||
"Invalid {}: {} expected value is either 'true' "
|
||||
"or 'false'."
|
||||
"".format(metadata_file,
|
||||
constants.APP_METADATA_AUTO_UPDATE)))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def verify_timing(parent):
|
||||
""" Validate the timing field of a given parent section
|
||||
|
||||
:param parent: parent section that contains the timing field
|
||||
to be verified
|
||||
"""
|
||||
try:
|
||||
timing = \
|
||||
parent[constants.APP_METADATA_TIMING]
|
||||
if timing != "pre" and timing != "post":
|
||||
raise exception.SysinvException(_(
|
||||
"Invalid {}: {} expected value is either 'pre' or 'post'."
|
||||
"".format(metadata_file,
|
||||
constants.APP_METADATA_TIMING)))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
app_name = ''
|
||||
app_version = ''
|
||||
patches = []
|
||||
|
@ -2447,16 +2486,7 @@ def find_metadata_file(path, metadata_file, upgrade_from_release=None):
|
|||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
auto_update = \
|
||||
upgrades[constants.APP_METADATA_AUTO_UPDATE]
|
||||
if not is_valid_boolstr(auto_update):
|
||||
raise exception.SysinvException(_(
|
||||
"Invalid {}: {} expected value is a boolean string."
|
||||
"".format(metadata_file,
|
||||
constants.APP_METADATA_AUTO_UPDATE)))
|
||||
except KeyError:
|
||||
pass
|
||||
verify_auto_update(upgrades)
|
||||
|
||||
try:
|
||||
from_versions = upgrades[constants.APP_METADATA_FROM_VERSIONS]
|
||||
|
@ -2511,6 +2541,22 @@ def find_metadata_file(path, metadata_file, upgrade_from_release=None):
|
|||
except KeyError:
|
||||
pass
|
||||
|
||||
k8s_upgrades = None
|
||||
|
||||
try:
|
||||
k8s_upgrades = doc[constants.APP_METADATA_K8S_UPGRADES]
|
||||
if not isinstance(k8s_upgrades, dict):
|
||||
raise exception.SysinvException(_(
|
||||
"Invalid {}: {} should be a dict."
|
||||
"".format(metadata_file,
|
||||
constants.APP_METADATA_K8S_UPGRADES)))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if k8s_upgrades:
|
||||
verify_auto_update(k8s_upgrades)
|
||||
verify_timing(k8s_upgrades)
|
||||
|
||||
supported_releases = {}
|
||||
try:
|
||||
supported_releases = doc[constants.APP_METADATA_SUPPORTED_RELEASES]
|
||||
|
|
|
@ -7043,6 +7043,66 @@ class ConductorManager(service.PeriodicService):
|
|||
|
||||
self._inner_sync_auto_apply(context, app_name)
|
||||
|
||||
@staticmethod
|
||||
def check_app_k8s_auto_update(app_name, tarball):
|
||||
""" Check whether an application should be automatically updated
|
||||
based on its Kubernetes upgrade metadata fields.
|
||||
|
||||
:param tarball: tarball object of the application to be checked
|
||||
"""
|
||||
|
||||
minimum_supported_k8s_version = tarball.metadata.get(
|
||||
constants.APP_METADATA_SUPPORTED_K8S_VERSION, {}).get(
|
||||
constants.APP_METADATA_MINIMUM, None)
|
||||
|
||||
if minimum_supported_k8s_version is None:
|
||||
# TODO: Turn this into an error message rather than a warning
|
||||
# when the k8s app upgrade implementation is in place. Also,
|
||||
# return False in this scenario.
|
||||
LOG.warning("Minimum supported Kubernetes version missing from "
|
||||
"{} metadata".format(app_name))
|
||||
else:
|
||||
LOG.debug("minimum_supported_k8s_version for {}: {}"
|
||||
.format(app_name, minimum_supported_k8s_version))
|
||||
|
||||
maximum_supported_k8s_version = tarball.metadata.get(
|
||||
constants.APP_METADATA_SUPPORTED_K8S_VERSION, {}).get(
|
||||
constants.APP_METADATA_MAXIMUM, None)
|
||||
|
||||
if maximum_supported_k8s_version:
|
||||
LOG.debug("maximum_supported_k8s_version for {}: {}"
|
||||
.format(app_name, maximum_supported_k8s_version))
|
||||
|
||||
k8s_upgrades = tarball.metadata.get(
|
||||
constants.APP_METADATA_K8S_UPGRADES, None)
|
||||
|
||||
if k8s_upgrades is None:
|
||||
k8s_auto_update = constants.APP_METADATA_K8S_AUTO_UPDATE_DEFAULT_VALUE
|
||||
k8s_update_timing = constants.APP_METADATA_TIMING_DEFAULT_VALUE
|
||||
LOG.warning("k8s_upgrades section missing from {} metadata"
|
||||
.format(app_name))
|
||||
else:
|
||||
k8s_auto_update = tarball.metadata.get(
|
||||
constants.APP_METADATA_K8S_UPGRADES).get(
|
||||
constants.APP_METADATA_AUTO_UPDATE,
|
||||
constants.APP_METADATA_K8S_AUTO_UPDATE_DEFAULT_VALUE)
|
||||
k8s_update_timing = tarball.metadata.get(
|
||||
constants.APP_METADATA_K8S_UPGRADES).get(
|
||||
constants.APP_METADATA_TIMING,
|
||||
constants.APP_METADATA_TIMING_DEFAULT_VALUE)
|
||||
|
||||
# TODO: check if the application meets the criteria to be updated
|
||||
# according to the 'supported_k8s_version' and 'k8s_upgrades'
|
||||
# metadata sections. This initial implementation is only intended to
|
||||
# set the default values for each entry.
|
||||
|
||||
LOG.debug("k8s_auto_update value for {}: {}"
|
||||
.format(app_name, k8s_auto_update))
|
||||
LOG.debug("k8s_update_timing value for {}: {}"
|
||||
.format(app_name, k8s_update_timing))
|
||||
|
||||
return True
|
||||
|
||||
def _auto_update_app(self, context, app_name, managed_app):
|
||||
"""Auto update applications"""
|
||||
try:
|
||||
|
@ -7129,6 +7189,11 @@ class ConductorManager(service.PeriodicService):
|
|||
% (app.name, tarball.app_version, app.app_version))
|
||||
return
|
||||
|
||||
# Check if the update should proceed based on the application's
|
||||
# Kubernetes metadata
|
||||
if not ConductorManager.check_app_k8s_auto_update(app_name, tarball):
|
||||
return
|
||||
|
||||
self._inner_sync_auto_update(context, app, tarball)
|
||||
|
||||
@cutils.synchronized(LOCK_APP_AUTO_MANAGE)
|
||||
|
|
Loading…
Reference in New Issue