diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/app_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/app_shell.py index cec3f37615..e5aa0a4c82 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/app_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/app_shell.py @@ -144,11 +144,16 @@ def do_application_upload(cc, args): help=('Reuse user overrides when updating application' 'to a new version. It will supersede the metadata ' 'preference specified by the application.')) +@utils.arg('--reuse-attributes', + metavar='', + help=('Reuse attributes when updating application ' + 'to a new version. It will supersede the metadata ' + 'preference specified by the application.')) def do_application_update(cc, args): """Update the deployed application to a different version""" data = _application_check(args) - fields_list = ['reuse_user_overrides'] + fields_list = ['reuse_user_overrides', 'reuse_attributes'] fields = dict((k, v) for (k, v) in vars(args).items() if k in fields_list and not (v is None)) data.update(fields) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/kube_app.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/kube_app.py index efac5ac122..4b5df2b16b 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/kube_app.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/kube_app.py @@ -436,6 +436,19 @@ class KubeAppController(rest.RestController): "Application-update rejected: " "invalid reuse_user_overrides setting.")) + reuse_attributes_flag = body.get('reuse_attributes', None) + if reuse_attributes_flag is None: + # None means let the application decide + reuse_attributes = None + elif reuse_attributes_flag in ['true', 'True']: + reuse_attributes = True + elif reuse_attributes_flag in ['false', 'False']: + reuse_attributes = False + else: + raise wsme.exc.ClientSideError(_( + "Application-update rejected: " + "invalid reuse_attributes setting.")) + try: applied_app = objects.kube_app.get_by_name(pecan.request.context, name) except exception.KubeAppNotFound: @@ -525,7 +538,9 @@ class KubeAppController(rest.RestController): pecan.request.rpcapi.perform_app_update(pecan.request.context, applied_app, target_app, tarfile, operation, - lifecycle_hook_info, reuse_overrides) + lifecycle_hook_info, + reuse_overrides, + reuse_attributes) return KubeApp.convert_with_links(target_app) diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index 9cbd30c3f1..f2c5c0e013 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -1825,6 +1825,7 @@ HOOK_PARAMETERS_MAP = { } # Application metadata constants +APP_METADATA_MAINTAIN_ATTRIBUTES = 'maintain_attributes' APP_METADATA_MAINTAIN_USER_OVERRIDES = 'maintain_user_overrides' APP_METADATA_APPLY_PROGRESS_ADJUST = 'apply_progress_adjust' APP_METADATA_APPLY_PROGRESS_ADJUST_DEFAULT_VALUE = 0 diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/kube_app.py b/sysinv/sysinv/sysinv/sysinv/conductor/kube_app.py index f38d121ad4..80faaea5da 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/kube_app.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/kube_app.py @@ -1660,6 +1660,47 @@ class AppOperator(object): "Chart %s from version %s" % (to_app.name, to_app.version, chart.name, from_app.version)) + def _preserve_attributes(self, from_app, to_app): + """ + In the scenario of updating application to a new version, this + method is used to copy the attributes from the old version + to the new version. + + :param from_app: application object that application updating from + :param to_app: application object that application updating to + """ + to_db_app = self._dbapi.kube_app_get(to_app.name) + from_db_app = self._dbapi.kube_app_get_inactive_by_name_version( + from_app.name, version=from_app.version) + + from_app_db_charts = self._dbapi.helm_override_get_all(from_db_app.id) + from_app_charts = {} + for chart in from_app_db_charts: + from_app_charts.setdefault(chart.name, {}).update( + {chart.namespace: chart.system_overrides}) + + for chart in to_app.charts: + if (chart.name in from_app_charts and + chart.namespace in from_app_charts[chart.name] and + from_app_charts[chart.name][chart.namespace]): + system_overrides = {'system_overrides': from_app_charts[chart.name][chart.namespace]} + try: + self._dbapi.helm_override_update( + app_id=to_db_app.id, name=chart.name, + namespace=chart.namespace, values=system_overrides) + except exception.HelmOverrideNotFound: + # Unexpected + values = { + 'name': chart.name, + 'namespace': chart.namespace, + 'app_id': to_db_app.id + } + values.update(system_overrides) + self._dbapi.helm_override_create(values=values) + LOG.info("Application %s (%s) will apply the attributes for" + "Chart %s from version %s" % (to_app.name, to_app.version, + chart.name, from_app.version)) + def _make_app_request(self, app, request, overrides_str=None): if app.is_fluxcd_app: return self._make_fluxcd_operation_with_monitor(app, request) @@ -3086,7 +3127,8 @@ class AppOperator(object): return False def perform_app_update(self, from_rpc_app, to_rpc_app, tarfile, - operation, lifecycle_hook_info_app_update, reuse_user_overrides=None): + operation, lifecycle_hook_info_app_update, reuse_user_overrides=None, + reuse_attributes=None): """Process application update request This method leverages the existing application upload workflow to @@ -3114,6 +3156,7 @@ class AppOperator(object): :param operation: apply or rollback :param lifecycle_hook_info_app_update: LifecycleHookInfo object :param reuse_user_overrides: (optional) True or False + :param reuse_attributes: (optional) True or False """ @@ -3194,6 +3237,17 @@ class AppOperator(object): if reuse_overrides: self._preserve_user_overrides(from_app, to_app) + reuse_app_attributes = \ + self._get_metadata_value(to_app, + constants.APP_METADATA_MAINTAIN_ATTRIBUTES, + False) + if reuse_attributes is not None: + reuse_app_attributes = reuse_attributes + + # Preserve attributes for the new app + if reuse_app_attributes: + self._preserve_attributes(from_app, to_app) + # The app_apply will generate new versioned overrides for the # app upgrade and will enable the new plugins for that version. diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index bdb17a158f..4042d0ca46 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -14337,7 +14337,8 @@ class ConductorManager(service.PeriodicService): return app_applied def perform_app_update(self, context, from_rpc_app, to_rpc_app, tarfile, - operation, lifecycle_hook_info_app_update, reuse_user_overrides=None): + operation, lifecycle_hook_info_app_update, reuse_user_overrides=None, + reuse_attributes=None): """Handling of application update request (via AppOperator) :param context: request context. @@ -14349,12 +14350,14 @@ class ConductorManager(service.PeriodicService): :param operation: apply or rollback :param lifecycle_hook_info_app_update: LifecycleHookInfo object :param reuse_user_overrides: (optional) True or False + :param reuse_attributes: (optional) True or False """ lifecycle_hook_info_app_update.operation = constants.APP_UPDATE_OP self._app.perform_app_update(from_rpc_app, to_rpc_app, tarfile, - operation, lifecycle_hook_info_app_update, reuse_user_overrides) + operation, lifecycle_hook_info_app_update, reuse_user_overrides, + reuse_attributes) def perform_app_remove(self, context, rpc_app, lifecycle_hook_info_app_remove, force=False): """Handling of application removal request (via AppOperator) diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py index a6cf741ff0..d978398872 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py @@ -1786,7 +1786,8 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): lifecycle_hook_info_app_apply=lifecycle_hook_info)) def perform_app_update(self, context, from_rpc_app, to_rpc_app, tarfile, - operation, lifecycle_hook_info, reuse_user_overrides=None): + operation, lifecycle_hook_info, reuse_user_overrides=None, + reuse_attributes=None): """Handle application update request :param context: request context. @@ -1799,6 +1800,7 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): :param lifecycle_hook_info: LifecycleHookInfo object :param reuse_user_overrides: (optional) True or False + :param reuse_attributes: (optional) True or False """ return self.cast(context, self.make_msg('perform_app_update', @@ -1807,7 +1809,8 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): tarfile=tarfile, operation=operation, lifecycle_hook_info_app_update=lifecycle_hook_info, - reuse_user_overrides=reuse_user_overrides)) + reuse_user_overrides=reuse_user_overrides, + reuse_attributes=reuse_attributes)) def perform_app_remove(self, context, rpc_app, lifecycle_hook_info, force=False): """Handle application remove request