blob: dae657adbfba267eaabe17cc9233b55a0a9a8b83 [file] [log] [blame]
Yuriy Taradayaeeaa742017-06-28 15:54:56 +04001import difflib
tmeneaud92f4742017-10-18 09:57:19 -04002import os
3import logging
Yuriy Taraday84a21032017-06-27 11:13:16 +04004
tmeneaud92f4742017-10-18 09:57:19 -04005from salt.exceptions import CommandExecutionError
Yuriy Taradayaeeaa742017-06-28 15:54:56 +04006from salt.serializers import yaml
7
tmeneaud92f4742017-10-18 09:57:19 -04008LOG = logging.getLogger(__name__)
Yuriy Taraday76126042017-08-11 15:27:41 +04009
tmeneaud92f4742017-10-18 09:57:19 -040010def _get_values_from_file(values_file=None):
11 if values_file:
12 try:
13 with open(values_file) as values_stream:
14 values = yaml.deserialize(values_stream)
15 return values
16 except e:
17 raise CommandExecutionError("encountered error reading from values "
18 "file (%s): %s" % (values_file, e))
19 return None
20
21def _get_yaml_diff(new_yaml=None, old_yaml=None):
22 if not new_yaml and not old_yaml:
23 return None
24
25 old_str = yaml.serialize(old_yaml, default_flow_style=False)
26 new_str = yaml.serialize(new_yaml, default_flow_style=False)
27 return difflib.unified_diff(old_str.split('\n'), new_str.split('\n'))
28
29def _failure(name, message, changes={}):
Yuriy Taraday76126042017-08-11 15:27:41 +040030 return {
31 'name': name,
tmeneaud92f4742017-10-18 09:57:19 -040032 'changes': changes,
Yuriy Taraday76126042017-08-11 15:27:41 +040033 'result': False,
34 'comment': message,
35 }
36
tmeneau94bf68e2017-10-17 15:55:34 -040037def present(name, chart_name, namespace, version=None, values_file=None,
tmeneaud92f4742017-10-18 09:57:19 -040038 tiller_namespace='kube-system', **kwargs):
39 '''
40 Ensure that a release with the supplied name is in the desired state in the
41 Tiller installation. This state will handle change detection to determine
42 whether an installation or update needs to be made.
43
44 In the event the namespace to which a release is installed changes, the
45 state will first delete and purge the release and the re-install it into
46 the new namespace, since Helm does not support updating a release into a
47 new namespace.
48
49 name
50 The name of the release to ensure is present
51
52 chart_name
53 The name of the chart to install, including the repository name as
54 applicable (such as `stable/mysql`)
55
56 namespace
57 The namespace to which the release should be (re-)installed
58
59 version
60 The version of the chart to install. Defaults to the latest version
61
62 values_file
63 The path to the a values file containing all the chart values that
64 should be applied to the release. Note that this should not be passed
65 if there are not chart value overrides required.
66
67 '''
68 kwargs['tiller_namespace'] = tiller_namespace
69 old_release = __salt__['helm.get_release'](name, **kwargs)
70 if not old_release:
tmeneaub858a002017-10-27 09:12:30 -040071 try:
72 result = __salt__['helm.release_create'](
tmeneaud92f4742017-10-18 09:57:19 -040073 name, chart_name, namespace, version, values_file, **kwargs
74 )
Yuriy Taraday76126042017-08-11 15:27:41 +040075 return {
tmeneaub858a002017-10-27 09:12:30 -040076 'name': name,
77 'changes': {
Yuriy Taraday76126042017-08-11 15:27:41 +040078 'name': name,
tmeneaub858a002017-10-27 09:12:30 -040079 'chart_name': chart_name,
80 'namespace': namespace,
81 'version': version,
82 'values': _get_values_from_file(values_file),
83 'stdout': result.get('stdout')
84 },
85 'result': True,
86 'comment': ('Release "%s" was created' % name +
87 '\nExecuted command: %s' % result['cmd'])
Yuriy Taraday76126042017-08-11 15:27:41 +040088 }
tmeneaub858a002017-10-27 09:12:30 -040089 except CommandExecutionError as e:
90 msg = (("Failed to create new release: %s" % e.error) +
91 "\nExecuted command: %s" % e.cmd)
92 return _failure(name, msg)
Yuriy Taraday76126042017-08-11 15:27:41 +040093
tmeneaud92f4742017-10-18 09:57:19 -040094 changes = {}
95 warnings = []
96 if old_release.get('chart') != chart_name.split("/")[1]:
97 changes['chart'] = { 'old': old_release['chart'], 'new': chart_name }
Yuriy Taraday76126042017-08-11 15:27:41 +040098
tmeneaud92f4742017-10-18 09:57:19 -040099 if old_release.get('version') != version:
100 changes['version'] = { 'old': old_release['version'], 'new': version }
Yuriy Taraday76126042017-08-11 15:27:41 +0400101
tmeneaud92f4742017-10-18 09:57:19 -0400102 if old_release.get('namespace') != namespace:
103 changes['namespace'] = { 'old': old_release['namespace'], 'new': namespace }
104
105 if (not values_file and old_release.get("values") or
106 not old_release.get("values") and values_file):
107 changes['values'] = { 'old': old_release['values'], 'new': values_file }
108
109 values = _get_values_from_file(values_file)
110 diff = _get_yaml_diff(values, old_release.get('values'))
111
112 if diff:
113 diff_string = '\n'.join(diff)
114 if diff_string:
115 changes['values'] = diff_string
116
117 if not changes:
118 return {
Yuriy Taraday84a21032017-06-27 11:13:16 +0400119 'name': name,
tmeneaud92f4742017-10-18 09:57:19 -0400120 'result': True,
121 'changes': {},
122 'comment': 'Release "{}" is already in the desired state'.format(name)
123 }
124
125 module_fn = 'helm.release_upgrade'
126 if changes.get("namespace"):
127 LOG.debug("purging old release (%s) due to namespace change" % name)
tmeneaub858a002017-10-27 09:12:30 -0400128 try:
129 result = __salt__['helm.release_delete'](name, **kwargs)
130 except CommandExecutionError as e:
131 msg = ("Failed to delete release for namespace change: %s" % e.error +
132 "\nExecuted command: %s" % e.cmd)
133 return _failure(name, msg, changes)
134
tmeneaud92f4742017-10-18 09:57:19 -0400135 module_fn = 'helm.release_create'
136 warnings.append('Release (%s) was replaced due to namespace change' % name)
137
tmeneaub858a002017-10-27 09:12:30 -0400138 try:
139 result = __salt__[module_fn](
tmeneaud92f4742017-10-18 09:57:19 -0400140 name, chart_name, namespace, version, values_file, **kwargs
tmeneaub858a002017-10-27 09:12:30 -0400141 )
142 changes.update({ 'stdout': result.get('stdout') })
143 ret = {
tmeneaud92f4742017-10-18 09:57:19 -0400144 'name': name,
145 'changes': changes,
Yuriy Taraday84a21032017-06-27 11:13:16 +0400146 'result': True,
tmeneaub858a002017-10-27 09:12:30 -0400147 'comment': 'Release "%s" was updated\nExecuted command: %s' % (name, result['cmd'])
148 }
149 if warnings:
150 ret['warnings'] = warnings
Yuriy Taraday893b3fb2017-07-03 16:22:57 +0400151
tmeneaub858a002017-10-27 09:12:30 -0400152 return ret
153 except CommandExecutionError as e:
154 msg = ("Failed to delete release for namespace change: %s" % e.error +
155 "\nExecuted command: %s" % e.cmd)
156 return _failure(name, msg, changes)
tmeneaud92f4742017-10-18 09:57:19 -0400157
158
159def absent(name, tiller_namespace='kube-system', **kwargs):
160 '''
161 Ensure that any release with the supplied release name is absent from the
162 tiller installation.
163
164 name
165 The name of the release to ensure is absent
166 '''
167 kwargs['tiller_namespace'] = tiller_namespace
168 exists = __salt__['helm.release_exists'](name, **kwargs)
Yuriy Taraday893b3fb2017-07-03 16:22:57 +0400169 if not exists:
170 return {
171 'name': name,
172 'changes': {},
173 'result': True,
tmeneaub858a002017-10-27 09:12:30 -0400174 'comment': 'Release "%s" doesn\'t exist' % name
Yuriy Taraday893b3fb2017-07-03 16:22:57 +0400175 }
tmeneaub858a002017-10-27 09:12:30 -0400176 try:
177 result = __salt__['helm.release_delete'](name, **kwargs)
178 return {
Yuriy Taraday893b3fb2017-07-03 16:22:57 +0400179 'name': name,
tmeneaub858a002017-10-27 09:12:30 -0400180 'changes': { name: 'DELETED', 'stdout': result['stdout'] },
Yuriy Taraday893b3fb2017-07-03 16:22:57 +0400181 'result': True,
tmeneaub858a002017-10-27 09:12:30 -0400182 'comment': 'Release "%s" was deleted\nExecuted command: %s' % (name, result['cmd'])
183 }
184 except CommandExecutionError as e:
185 return _failure(e.cmd, e.error)
186