nuvolaris/kustomize.py (110 lines of code) (raw):
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# this module wraps generation of kustomizations
import os, io, yaml, subprocess
import nuvolaris.kube as kube
import nuvolaris.kustomize as nku
import nuvolaris.template as ntp
# execute the kustomization of a folder under "deploy"
# specified with `where`
# it generate a kustomization.yaml, adding the header
# then including all the files of that folder as resources
# you have to pass a list of kustomizations to apply
# you can use various helpers in this module to generate customizations
# it returns the expanded kustomization
def kustomize(where, *what, templates=[], data={}):
"""Test kustomize
>>> import nuvolaris.kustomize as ku
>>> import nuvolaris.testutil as tu
>>> tu.grep(ku.kustomize("test", ku.image("nginx", "busybox")), "kind|image:", sort=True)
- image: busybox
kind: Pod
kind: Service
>>> tu.grep(ku.kustomize("test", ku.configMapTemplate("test-cm", "test", "test.json", {"item":"value"})), r"_id|value")
"_id": "test",
"value": "value"
>>> tu.grep(ku.kustomize("test", ku.image("nginx", "busybox"), templates=['testcm.yaml'], data={"name":"test-config"}), "name: test-", sort=True)
name: test-config
name: test-pod
name: test-svc
"""
# prepare the kustomization
dir = f"deploy/{where}"
tgt = f"{dir}/kustomization.yaml"
with open(tgt, "w") as f:
f.write("apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\n")
for s in list(what):
f.write(s)
f.write("resources:\n")
dirs = os.listdir(f"deploy/{where}")
dirs.sort()
for file in dirs:
if file == "kustomization.yaml":
continue
if file.startswith("_"):
continue
f.write(f"- {file}\n")
# adding extra temmplatized resources
for template in templates:
out = f"deploy/{where}/__{template}"
file = ntp.spool_template(template, out, data)
f.write(f"- __{template}\n")
res = subprocess.run(["kustomize", "build", dir], capture_output=True)
return res.stdout.decode("utf-8")
# execute the kustomization of a folder under "deploy"
# specified with `where` returning the expanded kustomization
# it expects that the folder contains already a full kustomization
# this methid will be used to extract the existing kustomization in case
# the nuvolaris operator needs to delete a component
def build(where):
dir = f"deploy/{where}"
res = subprocess.run(["kustomize", "build", dir], capture_output=True)
return res.stdout.decode("utf-8")
# execute the kustomization of a folder under "deploy"
# specified with `where`
# it generate a kustomization.yaml, adding the header
# then including all the files of that folder as resources limited to the one
# specified in templates_filter parameter
# you have to pass a list of kustomizations to apply
# you can use various helpers in this module to generate customizations
# it returns the expanded kustomization
def restricted_kustomize(where, *what, templates=[], templates_filter=[],data={}):
"""Test kustomize
>>> import nuvolaris.kustomize as ku
>>> import nuvolaris.testutil as tu
>>> tu.grep(ku.restricted_kustomize("test", ku.image("nginx", "busybox"), templates_filter=["pod.yaml","svc.yaml"]), "kind|image:", sort=True)
- image: busybox
kind: Pod
kind: Service
>>> tu.grep(ku.restricted_kustomize("test", ku.configMapTemplate("test-cm", "test", "test.json", {"item":"value"}),templates_filter=["pod.yaml","svc.yaml"]), r"_id|value")
"_id": "test",
"value": "value"
>>> tu.grep(ku.restricted_kustomize("test", ku.image("nginx", "busybox"), templates=['testcm.yaml'], templates_filter=["pod.yaml","svc.yaml"],data={"name":"test-config"}), "name: test-", sort=True)
name: test-config
name: test-pod
name: test-svc
"""
# prepare the kustomization
dir = f"deploy/{where}"
tgt = f"{dir}/kustomization.yaml"
with open(tgt, "w") as f:
f.write("apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\n")
for s in list(what):
f.write(s)
f.write("resources:\n")
dirs = os.listdir(f"deploy/{where}")
dirs.sort()
for file in dirs:
if file == "kustomization.yaml":
continue
if file.startswith("_"):
continue
if file in templates_filter:
f.write(f"- {file}\n")
# adding extra temmplatized resources
for template in templates:
out = f"deploy/{where}/__{template}"
file = ntp.spool_template(template, out, data)
f.write(f"- __{template}\n")
res = subprocess.run(["kustomize", "build", dir], capture_output=True)
return res.stdout.decode("utf-8")
# generate image kustomization
def image(name, newName=None, newTag=None):
"""Test image
>>> import nuvolaris.kustomize as ku
>>> print(ku.image("busybox"), end='')
images:
- name: busybox
>>> print(ku.image("busybox", "nginx"), end='')
images:
- name: busybox
newName: nginx
>>> print(ku.image("busybox", newTag="nightly"), end='')
images:
- name: busybox
newTag: nightly
>>> print(ku.image("busybox", "nginx", "nightly"), end='')
images:
- name: busybox
newName: nginx
newTag: nightly
"""
r = f"images:\n- name: {name}\n"
if newName: r += f" newName: {newName}\n"
if newTag: r += f" newTag: {newTag}\n"
return r
# generate a configmap kustomization expanding a template
def configMapTemplate(name, where, template, data):
"""
>>> import nuvolaris.testutil as tu
>>> print(configMapTemplate("test-cm", "test", "test.json", {"item":"value"}), end='')
configMapGenerator:
- name: test-cm
namespace: nuvolaris
files:
- test.json=__test.json
"""
out = f"deploy/{where}/__{template}"
file = ntp.spool_template(template, out, data)
return f"""configMapGenerator:
- name: {name}
namespace: nuvolaris
files:
- {template}=__{template}
"""
# generate a patch from a template
def patchTemplate(where, template, data):
"""
>>> import nuvolaris.testutil as tu
>>> import os.path
>>> data = {"name":"test-pod", "container":"test-pod", "dir":"/usr/share/nginx/html"}
>>> print(patchTemplate("test", "set-attach.yaml", data), end='')
patches:
- path: __set-attach.yaml
>>> os.path.exists("deploy/test/__set-attach.yaml")
True
"""
out = f"deploy/{where}/__{template}"
file = ntp.spool_template(template, out, data)
return f"""patches:
- path: __{template}
"""
# generate a patch from a list of templates
def patchTemplates(where, templates=[], data={}):
"""
>>> import nuvolaris.testutil as tu
>>> import os.path
>>> data = {"name":"test-pod", "container":"test-pod","dir":"/usr/share/nginx/html"}
>>> print(patchTemplates("test", ["set-attach.yaml","cron-init.yaml"], data), end='')
patches:
- path: __set-attach.yaml
- path: __cron-init.yaml
>>> os.path.exists("deploy/test/__set-attach.yaml")
True
"""
paths = []
for template in templates:
out = f"deploy/{where}/__{template}"
file = ntp.spool_template(template, out, data)
paths.append(f"- path: __{template}\n")
patches = ""
for path in paths:
patches += path
return f"""patches:\n{patches}"""
def secretLiteral(name, *args):
"""
>>> import nuvolaris.testutil as tu
>>> import nuvolaris.kustomize as ku
>>> tu.grep(ku.secretLiteral("test-sec", "user=mike", "pass=hello"), r"name:|user=|pass=")
- name: test-sec
- user=mike
- pass=hello
"""
res = f"""secretGenerator:
- name: {name}
namespace: nuvolaris
literals:
"""
for arg in args:
res += f" - {arg}\n"
return res
# returns a list of kustomized objects
def kustom_list(where, *what, templates=[], data={}):
"""
>>> import nuvolaris.kustomize as nku
>>> where = "test"
>>> what = []
>>> res = nku.kustom_list(where, *what)
>>> out = [x['kind'] for x in res['items']]
>>> out.sort()
>>> print(out)
['Pod', 'Service']
"""
yml = nku.kustomize(where, *what, templates=templates, data=data)
stream = io.StringIO(yml)
res = list(yaml.load_all(stream, yaml.Loader))
return {"apiVersion": "v1", "kind": "List", "items": res }
# returns a list of kustomized objects restricting the deploy available templates to given ones
def restricted_kustom_list(where, *what, templates=[], templates_filter=[], data={}):
"""
>>> import nuvolaris.kustomize as nku
>>> where = "test"
>>> what = []
>>> res = nku.restricted_kustom_list(where, *what,templates_filter=["pod.yaml","svc.yaml"])
>>> out = [x['kind'] for x in res['items']]
>>> out.sort()
>>> print(out)
['Pod', 'Service']
"""
yml = nku.restricted_kustomize(where, *what, templates=templates, templates_filter=templates_filter,data=data)
stream = io.StringIO(yml)
res = list(yaml.load_all(stream, yaml.Loader))
return {"apiVersion": "v1", "kind": "List", "items": res }
# load the given yaml file under deploy/{where} folder
def raw(where, yamlfile):
with open(f"deploy/{where}/{yamlfile}", 'r') as f:
return list(yaml.load_all(f, yaml.Loader))
def processTemplate(where,template,data,out_template=None):
"""
merges the given template and write it under the deploy/{where} folder returning a kind list items
"""
out = f"deploy/{where}/_{template}"
if(out_template):
out = f"deploy/{where}/{out_template}"
ntp.spool_template(template, out, data)
with open(out, 'r') as f:
res = list(yaml.load_all(f, yaml.Loader))
return {"apiVersion": "v1", "kind": "List", "items": res }
def renderTemplate(where,template,data,out_template):
"""
merges the given template and write it under the deploy/{where} folder returning the relative generated file path
"""
out = f"deploy/{where}/{out_template}"
ntp.spool_template(template, out, data)
return out
# generate a kustomization for a persistence volume claim using inline patchesJson6902 format
def patchPersistentVolumeClaim(name, path, value):
"""
>>> print(patchPersistentVolumeClaim("mongodb-data", "/spec/resources/requests/storage", "10Gi"), end='')
- target:
version: v1
kind: PersistentVolumeClaim
namespace: nuvolaris
name: mongodb-data
patch: |-
- op: replace
path: /spec/resources/requests/storage
value: 10Gi
"""
return f"""- target:
version: v1
kind: PersistentVolumeClaim
namespace: nuvolaris
name: {name}
patch: |-
- op: replace
path: {path}
value: {value}
"""
# generate a generic customization
def patchGenericEntry(kind, name, path, value, op="replace",apiVersion="v1"):
"""
>>> print(patchGenericEntry("Secret","postgres-nuvolaris-secret", "/stringData/superUserPassword","xyz"), end='')
- target:
version: v1
kind: Secret
namespace: nuvolaris
name: postgres-nuvolaris-secret
patch: |-
- op: replace
path: /stringData/superUserPassword
value: xyz
"""
return f"""- target:
version: {apiVersion}
kind: {kind}
namespace: nuvolaris
name: {name}
patch: |-
- op: {op}
path: {path}
value: {value}
"""