blob: dae657adbfba267eaabe17cc9233b55a0a9a8b83 [file] [log] [blame]
import difflib
import os
import logging
from salt.exceptions import CommandExecutionError
from salt.serializers import yaml
LOG = logging.getLogger(__name__)
def _get_values_from_file(values_file=None):
if values_file:
try:
with open(values_file) as values_stream:
values = yaml.deserialize(values_stream)
return values
except e:
raise CommandExecutionError("encountered error reading from values "
"file (%s): %s" % (values_file, e))
return None
def _get_yaml_diff(new_yaml=None, old_yaml=None):
if not new_yaml and not old_yaml:
return None
old_str = yaml.serialize(old_yaml, default_flow_style=False)
new_str = yaml.serialize(new_yaml, default_flow_style=False)
return difflib.unified_diff(old_str.split('\n'), new_str.split('\n'))
def _failure(name, message, changes={}):
return {
'name': name,
'changes': changes,
'result': False,
'comment': message,
}
def present(name, chart_name, namespace, version=None, values_file=None,
tiller_namespace='kube-system', **kwargs):
'''
Ensure that a release with the supplied name is in the desired state in the
Tiller installation. This state will handle change detection to determine
whether an installation or update needs to be made.
In the event the namespace to which a release is installed changes, the
state will first delete and purge the release and the re-install it into
the new namespace, since Helm does not support updating a release into a
new namespace.
name
The name of the release to ensure is present
chart_name
The name of the chart to install, including the repository name as
applicable (such as `stable/mysql`)
namespace
The namespace to which the release should be (re-)installed
version
The version of the chart to install. Defaults to the latest version
values_file
The path to the a values file containing all the chart values that
should be applied to the release. Note that this should not be passed
if there are not chart value overrides required.
'''
kwargs['tiller_namespace'] = tiller_namespace
old_release = __salt__['helm.get_release'](name, **kwargs)
if not old_release:
try:
result = __salt__['helm.release_create'](
name, chart_name, namespace, version, values_file, **kwargs
)
return {
'name': name,
'changes': {
'name': name,
'chart_name': chart_name,
'namespace': namespace,
'version': version,
'values': _get_values_from_file(values_file),
'stdout': result.get('stdout')
},
'result': True,
'comment': ('Release "%s" was created' % name +
'\nExecuted command: %s' % result['cmd'])
}
except CommandExecutionError as e:
msg = (("Failed to create new release: %s" % e.error) +
"\nExecuted command: %s" % e.cmd)
return _failure(name, msg)
changes = {}
warnings = []
if old_release.get('chart') != chart_name.split("/")[1]:
changes['chart'] = { 'old': old_release['chart'], 'new': chart_name }
if old_release.get('version') != version:
changes['version'] = { 'old': old_release['version'], 'new': version }
if old_release.get('namespace') != namespace:
changes['namespace'] = { 'old': old_release['namespace'], 'new': namespace }
if (not values_file and old_release.get("values") or
not old_release.get("values") and values_file):
changes['values'] = { 'old': old_release['values'], 'new': values_file }
values = _get_values_from_file(values_file)
diff = _get_yaml_diff(values, old_release.get('values'))
if diff:
diff_string = '\n'.join(diff)
if diff_string:
changes['values'] = diff_string
if not changes:
return {
'name': name,
'result': True,
'changes': {},
'comment': 'Release "{}" is already in the desired state'.format(name)
}
module_fn = 'helm.release_upgrade'
if changes.get("namespace"):
LOG.debug("purging old release (%s) due to namespace change" % name)
try:
result = __salt__['helm.release_delete'](name, **kwargs)
except CommandExecutionError as e:
msg = ("Failed to delete release for namespace change: %s" % e.error +
"\nExecuted command: %s" % e.cmd)
return _failure(name, msg, changes)
module_fn = 'helm.release_create'
warnings.append('Release (%s) was replaced due to namespace change' % name)
try:
result = __salt__[module_fn](
name, chart_name, namespace, version, values_file, **kwargs
)
changes.update({ 'stdout': result.get('stdout') })
ret = {
'name': name,
'changes': changes,
'result': True,
'comment': 'Release "%s" was updated\nExecuted command: %s' % (name, result['cmd'])
}
if warnings:
ret['warnings'] = warnings
return ret
except CommandExecutionError as e:
msg = ("Failed to delete release for namespace change: %s" % e.error +
"\nExecuted command: %s" % e.cmd)
return _failure(name, msg, changes)
def absent(name, tiller_namespace='kube-system', **kwargs):
'''
Ensure that any release with the supplied release name is absent from the
tiller installation.
name
The name of the release to ensure is absent
'''
kwargs['tiller_namespace'] = tiller_namespace
exists = __salt__['helm.release_exists'](name, **kwargs)
if not exists:
return {
'name': name,
'changes': {},
'result': True,
'comment': 'Release "%s" doesn\'t exist' % name
}
try:
result = __salt__['helm.release_delete'](name, **kwargs)
return {
'name': name,
'changes': { name: 'DELETED', 'stdout': result['stdout'] },
'result': True,
'comment': 'Release "%s" was deleted\nExecuted command: %s' % (name, result['cmd'])
}
except CommandExecutionError as e:
return _failure(e.cmd, e.error)