From c8714a9be2820348b9b74721e97c3e34070e6cf2 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 21 Jun 2017 10:32:14 +0200 Subject: [PATCH] kubernetes: handle ConfigMap resources Allow kubenetes' ConfigMap resources to be managed by salt Signed-off-by: Flavio Castelli --- salt/modules/kubernetes.py | 164 +++++++++++++++++++++++++++++++++++++ salt/states/kubernetes.py | 120 +++++++++++++++++++++++++++ 2 files changed, 284 insertions(+) diff --git a/salt/modules/kubernetes.py b/salt/modules/kubernetes.py index 46ebda727d..3a267f4510 100644 --- a/salt/modules/kubernetes.py +++ b/salt/modules/kubernetes.py @@ -297,6 +297,32 @@ def secrets(namespace='default', **kwargs): raise CommandExecutionError(exc) +def configmaps(namespace='default', **kwargs): + ''' + Return a list of kubernetes configmaps defined in the namespace + + CLI Examples:: + + salt '*' kubernetes.configmaps + salt '*' kubernetes.configmaps namespace=default + ''' + _setup_conn(**kwargs) + try: + api_instance = kubernetes.client.CoreV1Api() + api_response = api_instance.list_namespaced_config_map(namespace) + + return [secret['metadata']['name'] for secret in api_response.to_dict().get('items')] + except (ApiException, HTTPError) as exc: + if isinstance(exc, ApiException) and exc.status == 404: + return None + else: + log.exception( + 'Exception when calling ' + 'CoreV1Api->list_namespaced_config_map: {0}'.format(exc) + ) + raise CommandExecutionError(exc) + + def show_deployment(name, namespace='default', **kwargs): ''' Return the kubernetes deployment defined by name and namespace @@ -434,6 +460,32 @@ def show_secret(name, namespace='default', decode=False, **kwargs): '{0}'.format(exc) ) raise CommandExecutionError(exc) + + +def show_configmap(name, namespace='default', **kwargs): + ''' + Return the kubernetes configmap defined by name and namespace. + + CLI Examples:: + + salt '*' kubernetes.show_configmap game-config default + salt '*' kubernetes.show_configmap name=game-config namespace=default + ''' + _setup_conn(**kwargs) + try: + api_instance = kubernetes.client.CoreV1Api() + api_response = api_instance.read_namespaced_config_map( + name, + namespace) + + return api_response.to_dict() + except (ApiException, HTTPError) as exc: + if isinstance(exc, ApiException) and exc.status == 404: + return None + else: + log.exception( + 'Exception when calling ' + 'ExtensionsV1beta1Api->read_namespaced_config_map: ' '{0}'.format(exc) ) raise CommandExecutionError(exc) @@ -589,6 +641,38 @@ def delete_secret(name, namespace='default', **kwargs): raise CommandExecutionError(exc) +def delete_configmap(name, namespace='default', **kwargs): + ''' + Deletes the kubernetes configmap defined by name and namespace + + CLI Examples:: + + salt '*' kubernetes.delete_configmap settings default + salt '*' kubernetes.delete_configmap name=settings namespace=default + ''' + _setup_conn(**kwargs) + body = kubernetes.client.V1DeleteOptions(orphan_dependents=True) + + try: + api_instance = kubernetes.client.CoreV1Api() + api_response = api_instance.delete_namespaced_config_map( + name=name, + namespace=namespace, + body=body) + + return api_response.to_dict() + except (ApiException, HTTPError) as exc: + if isinstance(exc, ApiException) and exc.status == 404: + return None + else: + log.exception( + 'Exception when calling ' + 'CoreV1Api->delete_namespaced_config_map: ' + '{0}'.format(exc) + ) + raise CommandExecutionError(exc) + + def create_deployment( name, namespace, @@ -721,6 +805,47 @@ def create_secret( raise CommandExecutionError(exc) +def create_configmap( + name, + namespace, + data, + source, + template, + saltenv, + **kwargs): + ''' + Creates the kubernetes configmap as defined by the user. + ''' + if source: + data = __read_and_render_yaml_file(source, template, saltenv) + elif data is None: + data = {} + + data = __enforce_only_strings_dict(data) + + body = kubernetes.client.V1ConfigMap( + metadata=__dict_to_object_meta(name, namespace, {}), + data=data) + + _setup_conn(**kwargs) + + try: + api_instance = kubernetes.client.CoreV1Api() + api_response = api_instance.create_namespaced_config_map( + namespace, body) + + return api_response.to_dict() + except (ApiException, HTTPError) as exc: + if isinstance(exc, ApiException) and exc.status == 404: + return None + else: + log.exception( + 'Exception when calling ' + 'CoreV1Api->create_namespaced_config_map: {0}'.format(exc) + ) + raise CommandExecutionError(exc) + + def create_namespace( name, **kwargs): @@ -893,6 +1018,45 @@ def replace_secret(name, raise CommandExecutionError(exc) +def replace_configmap(name, + data, + source, + template, + saltenv, + namespace='default', + **kwargs): + ''' + Replaces an existing configmap with a new one defined by name and + namespace, having the specificed data. + ''' + if source: + data = __read_and_render_yaml_file(source, template, saltenv) + + data = __enforce_only_strings_dict(data) + + body = kubernetes.client.V1ConfigMap( + metadata=__dict_to_object_meta(name, namespace, {}), + data=data) + + _setup_conn(**kwargs) + + try: + api_instance = kubernetes.client.CoreV1Api() + api_response = api_instance.replace_namespaced_config_map( + name, namespace, body) + + return api_response.to_dict() + except (ApiException, HTTPError) as exc: + if isinstance(exc, ApiException) and exc.status == 404: + return None + else: + log.exception( + 'Exception when calling ' + 'CoreV1Api->replace_namespaced_configmap: {0}'.format(exc) + ) + raise CommandExecutionError(exc) + + def __create_object_body(kind, obj_class, spec_creator, diff --git a/salt/states/kubernetes.py b/salt/states/kubernetes.py index 34f4fddd75..52e36dfaa2 100644 --- a/salt/states/kubernetes.py +++ b/salt/states/kubernetes.py @@ -571,3 +571,123 @@ def secret_present( ret['result'] = True return ret + +def configmap_absent(name, namespace='default', **kwargs): + ''' + Ensures that the named configmap is absent from the given namespace. + + name + The name of the configmap + + namespace + The name of the namespace + ''' + + ret = {'name': name, + 'changes': {}, + 'result': False, + 'comment': ''} + + configmap = __salt__['kubernetes.show_configmap'](name, namespace, **kwargs) + + if configmap is None: + ret['result'] = True if not __opts__['test'] else None + ret['comment'] = 'The configmap does not exist' + return ret + + if __opts__['test']: + ret['comment'] = 'The configmap is going to be deleted' + ret['result'] = None + return ret + + __salt__['kubernetes.delete_configmap'](name, namespace, **kwargs) + # As for kubernetes 1.6.4 doesn't set a code when deleting a configmap + # The kubernetes module will raise an exception if the kubernetes + # server will return an error + ret['result'] = True + ret['changes'] = { + 'kubernetes.configmap': { + 'new': 'absent', 'old': 'present'}} + ret['comment'] = 'ConfigMap deleted' + + return ret + + +def configmap_present( + name, + namespace='default', + data=None, + source='', + template='', + **kwargs): + ''' + Ensures that the named configmap is present inside of the specified namespace + with the given data. + If the configmap exists it will be replaced. + + name + The name of the configmap. + + namespace + The namespace holding the configmap. The 'default' one is going to be + used unless a different one is specified. + + data + The dictionary holding the configmaps. + + source + A file containing the data of the configmap in plain format. + + template + Template engine to be used to render the source file. + ''' + ret = {'name': name, + 'changes': {}, + 'result': False, + 'comment': ''} + + if data and source: + return _error( + ret, + '\'source\' cannot be used in combination with \'data\'' + ) + + configmap = __salt__['kubernetes.show_configmap'](name, namespace, **kwargs) + + if configmap is None: + if __opts__['test']: + ret['result'] = None + ret['comment'] = 'The configmap is going to be created' + return ret + res = __salt__['kubernetes.create_configmap'](name=name, + namespace=namespace, + data=data, + source=source, + template=template, + saltenv=__env__, + **kwargs) + ret['changes']['{0}.{1}'.format(namespace, name)] = { + 'old': {}, + 'new': res} + else: + if __opts__['test']: + ret['result'] = None + return ret + + # TODO: improve checks # pylint: disable=fixme + log.info('Forcing the recreation of the service') + ret['comment'] = 'The configmap is already present. Forcing recreation' + res = __salt__['kubernetes.replace_configmap']( + name=name, + namespace=namespace, + data=data, + source=source, + template=template, + saltenv=__env__, + **kwargs) + + ret['changes'] = { + 'data': res['data'] + } + ret['result'] = True + return ret