-
Notifications
You must be signed in to change notification settings - Fork 45
/
metalk8s_kubernetes.py
89 lines (69 loc) · 2.97 KB
/
metalk8s_kubernetes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
"""A renderer for Kubernetes YAML manifests.
Given a Kubernetes YAML file (which may be a stream of objects, i.e. YAML
snippets separated by `---` lines), this will render a sequence of states
(represented as an OrderedDict), mapping every such object to an invocation
of our custom `object_[present|absent]` state function.
To use it, add a shebang like `#!metalk8s_kubernetes` as the first line of your
manifests SLS file. Optionally, you can use rendering pipelines (if templating
is required), e.g. `#!jinja | metalk8s_kubernetes`.
The shebang also supports passing options to this renderer, in the format
`#!metalk8s_kubernetes argument1=value1&argument2=value2` (basically, a query
string in the `application/x-www-form-urlencoded` format).
The supported options are:
- `kubeconfig`, the path to the kubeconfig file to use for communicating with
K8s API (defaults define in salt-master configuration)
- `context`, the context from the kubeconfig to use (defaults define in
salt-master configuration
- `absent`, a boolean to toggle which state function variant (`object_present`
or `object_absent`) to use (defaults to False)
"""
import yaml
from salt.exceptions import SaltRenderError
from salt.ext import six
from salt.utils.yaml import SaltYamlSafeLoader
from salt.utils.odict import OrderedDict
__virtualname__ = 'metalk8s_kubernetes'
def __virtual__():
return __virtualname__
def _step_name(manifest, absent=False):
try:
name = manifest['metadata']['name']
except KeyError:
raise SaltRenderError('Object `metadata.name` must be set.')
namespace = manifest['metadata'].get('namespace', None)
if namespace is not None:
full_name = '{}/{}'.format(namespace, name)
else:
full_name = name
return "{verb} {api_version}/{kind} '{name}'".format(
verb='Remove' if absent else 'Apply',
api_version=manifest['apiVersion'],
kind=manifest['kind'],
name=full_name,
)
def _step(manifest, kubeconfig=None, context=None, absent=False):
"""Render a single Kubernetes object into a state 'step'."""
step_name = _step_name(manifest, absent)
state_func = 'metalk8s_kubernetes.object_{}'.format(
'absent' if absent else 'present'
)
state_args = [
{'name': step_name},
{'kubeconfig': kubeconfig},
{'context': context},
{'manifest': manifest},
]
return step_name, {state_func: state_args}
def render(source, saltenv='', sls='', argline='', **kwargs):
args = six.moves.urllib.parse.parse_qs(argline)
kubeconfig = args.get('kubeconfig', [None])[0]
context = args.get('context', [None])[0]
absent = args.get('absent', [False])[0]
if not isinstance(source, six.string_types):
# Assume it is a file handle
source = source.read()
data = yaml.load_all(source, Loader=SaltYamlSafeLoader)
return OrderedDict(
_step(manifest, kubeconfig=kubeconfig, context=context, absent=absent)
for manifest in data if manifest
)