Issue test certificate via lifecyle
Issue a test certificate using the application lifecycle in order to assert cert-manager readiness. This allows dependent applications to promptly rely on cert-manager once it is applied. This is a re-implementation of the original code, which was removed with the 21-k8s-app-upgrade.sh script, in order to allow apps to be updated directly via the Application Framework using the new dependency feature: https://review.opendev.org/c/starlingx/update/+/953526 Tox sysinv import was fixed for the application plugins as part of this change. Test plan: PASS: build-pkgs && build-image PASS: upload/apply/update/remove/delete cert-manager PASS: Platform fresh install PASS: Apply ptp-notification-app Platform upgrade from stx 10 Depends-on: https://review.opendev.org/c/starlingx/config/+/956724 Closes-bug: 2119681 Change-Id: Ic37e97ee47c08eabcb1afde5c2a820bf68196326 Signed-off-by: Igor Soares <Igor.PiresSoares@windriver.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2022 Wind River Systems, Inc.
|
# Copyright (c) 2022,2025 Wind River Systems, Inc.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
#
|
#
|
||||||
@@ -12,18 +12,63 @@
|
|||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
# pylint: disable=no-name-in-module
|
# pylint: disable=no-name-in-module
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from sysinv.common import constants
|
from sysinv.common import constants
|
||||||
from sysinv.common import exception
|
from sysinv.common import exception
|
||||||
|
from sysinv.common import kubernetes
|
||||||
from sysinv.helm import lifecycle_base as base
|
from sysinv.helm import lifecycle_base as base
|
||||||
from sysinv.helm import lifecycle_utils as lifecycle_utils
|
from sysinv.helm import lifecycle_utils as lifecycle_utils
|
||||||
from sysinv.helm.lifecycle_constants import LifecycleConstants
|
from sysinv.helm.lifecycle_constants import LifecycleConstants
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
NAMESPACE = "cert-manager"
|
||||||
|
CERT_MANAGER_GROUP = 'cert-manager.io'
|
||||||
|
CERT_MANAGER_VERSION = 'v1'
|
||||||
|
CERT_NAME = "stx-test-cm"
|
||||||
|
CERT_SECRET_NAME = "stx-test-cm"
|
||||||
|
ISSUER_NAME = "system-local-ca"
|
||||||
|
ISSUER_PLURAL = "issuers"
|
||||||
|
PLURAL_NAME_CERT = 'certificates'
|
||||||
|
TEST_CERT_RETRIES = 60
|
||||||
|
ISSUER = {
|
||||||
|
'apiVersion': f'{CERT_MANAGER_GROUP}/{CERT_MANAGER_VERSION}',
|
||||||
|
'kind': 'Issuer',
|
||||||
|
'metadata': {
|
||||||
|
'creationTimestamp': None,
|
||||||
|
'name': ISSUER_NAME,
|
||||||
|
'namespace': NAMESPACE
|
||||||
|
},
|
||||||
|
'spec': {
|
||||||
|
'ca': {
|
||||||
|
'secretName': ISSUER_NAME
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'status': {}
|
||||||
|
}
|
||||||
|
CERT = {
|
||||||
|
'apiVersion': 'cert-manager.io/v1',
|
||||||
|
'kind': 'Certificate',
|
||||||
|
'metadata': {
|
||||||
|
'creationTimestamp': None,
|
||||||
|
'name': CERT_NAME,
|
||||||
|
'namespace': NAMESPACE
|
||||||
|
},
|
||||||
|
'spec': {
|
||||||
|
'commonName': CERT_NAME,
|
||||||
|
'issuerRef': {
|
||||||
|
'kind': 'Issuer',
|
||||||
|
'name': ISSUER_NAME,
|
||||||
|
'namespace': NAMESPACE
|
||||||
|
},
|
||||||
|
'secretName': CERT_SECRET_NAME,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CertManagerAppLifecycleOperator(base.AppLifecycleOperator):
|
class CertManagerAppLifecycleOperator(base.AppLifecycleOperator):
|
||||||
|
|
||||||
def app_lifecycle_actions(self, context, conductor_obj, app_op, app, hook_info):
|
def app_lifecycle_actions(self, context, conductor_obj, app_op, app, hook_info):
|
||||||
""" Perform lifecycle actions for an operation
|
""" Perform lifecycle actions for an operation
|
||||||
|
|
||||||
@@ -61,6 +106,13 @@ class CertManagerAppLifecycleOperator(base.AppLifecycleOperator):
|
|||||||
hook_info.relative_timing == LifecycleConstants.APP_LIFECYCLE_TIMING_POST:
|
hook_info.relative_timing == LifecycleConstants.APP_LIFECYCLE_TIMING_POST:
|
||||||
return lifecycle_utils.delete_local_registry_secrets(app_op, app, hook_info)
|
return lifecycle_utils.delete_local_registry_secrets(app_op, app, hook_info)
|
||||||
|
|
||||||
|
# FluxCD request
|
||||||
|
elif hook_info.lifecycle_type == LifecycleConstants.APP_LIFECYCLE_TYPE_FLUXCD_REQUEST:
|
||||||
|
if hook_info.operation == constants.APP_APPLY_OP and \
|
||||||
|
hook_info.relative_timing == LifecycleConstants.APP_LIFECYCLE_TIMING_STATUS \
|
||||||
|
and not os.path.isfile(constants.ANSIBLE_BOOTSTRAP_FLAG):
|
||||||
|
return self.issue_test_cert()
|
||||||
|
|
||||||
# Use the default behaviour for other hooks
|
# Use the default behaviour for other hooks
|
||||||
super(CertManagerAppLifecycleOperator, self).app_lifecycle_actions(
|
super(CertManagerAppLifecycleOperator, self).app_lifecycle_actions(
|
||||||
context, conductor_obj, app_op, app, hook_info)
|
context, conductor_obj, app_op, app, hook_info)
|
||||||
@@ -75,3 +127,77 @@ class CertManagerAppLifecycleOperator(base.AppLifecycleOperator):
|
|||||||
if os.path.isfile(constants.ANSIBLE_BOOTSTRAP_FLAG):
|
if os.path.isfile(constants.ANSIBLE_BOOTSTRAP_FLAG):
|
||||||
raise exception.LifecycleSemanticCheckException(
|
raise exception.LifecycleSemanticCheckException(
|
||||||
"Auto-apply disabled during bootstrap.")
|
"Auto-apply disabled during bootstrap.")
|
||||||
|
|
||||||
|
def issue_test_cert(self):
|
||||||
|
""" Issue a test certificate to ensure cert-manager is functional """
|
||||||
|
|
||||||
|
LOG.info("Issue test certificate to assert cert-manager readiness.")
|
||||||
|
kubernetes_operator = kubernetes.KubeOperator()
|
||||||
|
|
||||||
|
# Delete any pre-existing test certificate
|
||||||
|
try:
|
||||||
|
kubernetes_operator.delete_custom_resource(CERT_MANAGER_GROUP,
|
||||||
|
CERT_MANAGER_VERSION,
|
||||||
|
NAMESPACE,
|
||||||
|
PLURAL_NAME_CERT,
|
||||||
|
CERT_NAME)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warning(f"Unable to remove existing test certificate: {e}")
|
||||||
|
|
||||||
|
apply_failed = True
|
||||||
|
secret_failed = True
|
||||||
|
|
||||||
|
for _ in range(1, TEST_CERT_RETRIES + 1):
|
||||||
|
if apply_failed:
|
||||||
|
LOG.info("Applying Issuer...")
|
||||||
|
try:
|
||||||
|
kubernetes_operator.apply_custom_resource(CERT_MANAGER_GROUP,
|
||||||
|
CERT_MANAGER_VERSION,
|
||||||
|
NAMESPACE,
|
||||||
|
ISSUER_PLURAL,
|
||||||
|
ISSUER_NAME,
|
||||||
|
ISSUER)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warning(f"Error applying certificate issuer: {e}. Retrying.")
|
||||||
|
time.sleep(3)
|
||||||
|
continue
|
||||||
|
|
||||||
|
LOG.info("Applying test certificate CRD...")
|
||||||
|
try:
|
||||||
|
kubernetes_operator.apply_custom_resource(CERT_MANAGER_GROUP,
|
||||||
|
CERT_MANAGER_VERSION,
|
||||||
|
NAMESPACE,
|
||||||
|
PLURAL_NAME_CERT,
|
||||||
|
CERT_NAME,
|
||||||
|
CERT)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warning(f"Error applying certificate CRD: {e}. Retrying.")
|
||||||
|
time.sleep(3)
|
||||||
|
continue
|
||||||
|
apply_failed = False
|
||||||
|
|
||||||
|
LOG.info("Waiting cert-manager to issue the certificate...")
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = kubernetes_operator.kube_get_secret(CERT_SECRET_NAME, NAMESPACE)
|
||||||
|
if result:
|
||||||
|
LOG.info("cert-manager is ready to issue certificates.")
|
||||||
|
secret_failed = False
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warning(f"Unable to retrieve secret: {e}")
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
try:
|
||||||
|
kubernetes_operator.delete_custom_resource(CERT_MANAGER_GROUP,
|
||||||
|
CERT_MANAGER_VERSION,
|
||||||
|
NAMESPACE,
|
||||||
|
PLURAL_NAME_CERT,
|
||||||
|
CERT_NAME)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warning(f"Unable to remove existing test certificate: {e}")
|
||||||
|
|
||||||
|
if secret_failed:
|
||||||
|
raise exception.SysinvException("Cert-manager is not ready after the allotted time. "
|
||||||
|
"Check the pod logs.")
|
||||||
|
@@ -39,7 +39,7 @@ setenv = VIRTUAL_ENV={envdir}
|
|||||||
|
|
||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
-e{[tox]stxdir}/config/sysinv/sysinv/sysinv
|
{[tox]stxdir}/config/sysinv/sysinv/sysinv
|
||||||
-e{[tox]stxdir}/config/tsconfig/tsconfig
|
-e{[tox]stxdir}/config/tsconfig/tsconfig
|
||||||
-e{[tox]stxdir}/fault/fm-api/source
|
-e{[tox]stxdir}/fault/fm-api/source
|
||||||
-e{[tox]stxdir}/fault/python-fmclient/fmclient
|
-e{[tox]stxdir}/fault/python-fmclient/fmclient
|
||||||
|
Reference in New Issue
Block a user