shalm

command module
v0.4.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 29, 2020 License: Apache-2.0 Imports: 1 Imported by: 0

README

Scriptable helm charts

This project brings the starlark scripting language to helm charts.

Features

  • Define APIs for helm charts
  • Ease composition of charts
  • Control deployment by overriding methods
  • Compatible with helm
  • Share a common service like a database manager or an ingress between a set of sub charts
  • Use starlark methods in templates (replacement for _helpers.tpl)
  • Interact with kubernetes during installation
  • Manage user credentials
  • Manage certificates
  • Act as glue code between helm charts
  • Rendering of ytt templates
  • Also available as kubernetes controller
  • Easy embeddable and extendable
  • Integration of kapp

Installation

Prerequisite
  • Install kubectl e.g. using brew install kubernetes-cli
  • Install ytt e.g. from https://github.com/k14s/ytt/releases
  • Install kapp e.g. from https://github.com/k14s/kapp/releases
Install binary
  • Download shalm (e.g. for mac os)
curl -L https://github.com/kramerul/shalm/releases/download/0.1.1/shalm-binary-darwin.tgz | tar xzvf -
Build shalm from source
  • Install go e.g. using brew install go
  • Install shalm
go get github.com/kramerul/shalm

Usage

shalm template <chart>
shalm apply <chart>
shalm delete <chart>
shalm package <chart>

A set of example charts can be found in the charts/examples folder.

Charts can be given by path or by url. In case of an url, the chart must be packaged using shalm package or zip.

Writing charts

Just follow the rules of helm to write charts. Additionally, you can put a Chart.star file in the charts folder

<chart>/
├── Chart.yaml
├── values.yaml
├── Chart.star
└── templates/
Using embedded ytt yaml templates

You can use ytt yaml templates to render kubernetes artifacts. You simply put them in the any folder inside a chart. There is currently no support for data, star or text files. The only value supplied to the templates is self, which is the current chart. You can access all values and methods within your chart. To use this feature, you need to override the template method

apiVersion: v1
kind: Namespace
metadata:
  name: #@ self.namespace
def template(self,glob=''):
  return self.eytt("ytt",glob=glob)  # Use ytt templating with templates in directory 'ytt'

To use this feature, you need to override the template method

apiVersion: v1
kind: Namespace
metadata:
  name: #@ self.namespace
def template(self,glob=''):
  return self.ytt("yttx",self.helm())  # Use ytt templating with templates in directory 'yttx' feeding in output from another helm template

Packaging charts

You can package shalm charts using the following command:

shalm package <shalm chart>

It's also possible to convert shalm charts to helm charts:

shalm package --helm <shalm chart>

In this case, the helm chart only includes two jobs (post-install and pre-delete hooks) which do the whole work.

kapp Support

Shalm charts can be applied/deleted using kapp. Therefore, you can pass --tool kapp at the command line.

Examples

Share database

The following example shows how a database manager could be shared.

  • Define an API for a database manager (e.g. mariadb)
def create_database(self,db="db",username="",password=""):
   ...
  • Define a constructor for a service, which requires a database
def init(self,database=None):
  if database:
    database.create_database(db="uaa",username="uaa",password="randompass")
  • Use the API within another chart
def init(self):
  self.mariadb = chart("mariadb")
  self.uaa = chart("uaa",database = self.mariadb)
Override apply, delete or template

With shalm it's possible to override the apply, delete and template methods. The following example illustrates how this could be done

def init(self):
  self.mariadb = chart("mariadb")
  self.uaa = chart("uaa",database = self.mariadb)

def apply(self,k8s):
  self.mariadb.apply(k8s) # Apply mariadb stuff (recursive)
  k8s.rollout_status("statefulset","mariadb-master")  # Interact with kubernetes
  self.uaa.apply(k8s)     # Apply uaa stuff (recursive)
  self.__apply(k8s)       # Apply everthing defined in this chart (not recursive)

def template(self,glob=''):
  return self.helm(glob=glob)  # Use helm templating (default)
Vaults

Shalm provides the concept of vaults to store things like

  • certificates
  • user credentials

with the help of secrets in kubernetes.

It's also possible to extend shalm to provide other types of vaults:

  • AWS users
  • GCP users
  • letsencrypt certificates
  • ...
Create User Credentials

User credentials are used to manage username and password pairs. They are mapped to kubernets Secrets. If the secret doesn't exist, the username and password are created with random content, otherwise the fields are read from the secret. The keys used to store the username and password inside the secret can be modified.

The content of username and password can only be accessed after the call to __apply. Therefore, you need to override the apply method.

All user credentials created inside a Chart.star file are automatically applied to kubernetes. If you run shalm template, the content of the username and password is undefined.

def init(self):
   self.nats = chart("https://charts.bitnami.com/bitnami/nats-4.2.6.tgz")
   self.auth = user_credential("nats-auth")

def apply(self,k8s):
  self.__apply(k8s)
  self.nats.auth["user"] = self.auth.username
  self.nats.auth["password"] = self.auth.password
  self.nats.apply(k8s)
Create Certificates

Shalm provides creation on self signed certificactes out of the box. These certificates can be used for

  • Mutual TLS within your application
  • Register k8s webhooks

The certificates are stored as secrets inside kubernetes. If you deploy your application again, the certificates will not be changed. Certificate rotation or renewal is not implemented yet. For this purpose you need to remove the secrets manually from kubernetes.

The following example will deploy 3 artifacts to kubernetes

  • A secret ca, which containts your ca certificate
  • A secret server, which containts your server certificate
  • A configmap configmap, which containts the ca of your server certificate
def init(self):
  self.ca = certificate("ca",is_ca=True,validity="P10Y",domains=["ca.com"]) # Create CA
  self.cert = certificate("server",signer=self.ca,domains=["example.com"],validity="P1Y")

def template(self,glob=""):
  return self.eytt("eytt") # use embedded ytt for templating

Put this template info eytt/configmap.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap
data:
  #! render ca of server certificate
  ca: #@ self.cert.ca

Using github repositories

It's possible to use github repositories directly as source for shalm charts.

shalm apply https://github.com/<repo>/archive/<branch-or-tag>.zip
shalm apply https://github.com/kramerul/cf-for-k8s/archive/shalm.zip  --set domain=cf.example.com

The zip file always contains a root directory, which always stripped off.

Fragments

You can also specify a fragment to get only a part of a zip archive.

shalm apply https://github.com/kramerul/shalm/archive/master.zip#charts/shalm

Normally, a zip file always contains a root folder. This root folder is always added to the path given in the fragment to ease the usage.

Enterprise github repos
https://<host>/api/v3/repos/<owner>/<repo>/zipball/<branch>
Download credentials

Download credentials can be configured in$HOME/.shalm/config. Example

credentials:
  - url: https://github.tools.sap/
    token: 123j9iasdfj2j3412934

Extending and embedding shalm

It's possible to extend shalm with starlark modules. See examples/extension directory for details.

Using shalm controller

Charts can be also applied (in parts) using the shalm controller. Two proxy modes are support

  • local the chart CR is applied to the same cluster.
  • remote the chart CR is applied to the cluster where the current k8s value points to.

These modes only behave different, if you are applying charts to different clusters.

Install shalm controller
shalm apply charts/shalm
Install a shalm chart using the controller
shalm apply --proxy remote <chart>

or from inside another shalm chart

def init(self):
  self.mariadb = chart("mariadb",proxy="local")

Comparison

shalm helm ytt/kapp kustomize
Scripting + (3.1) + -
API definition + - (+) -
Reuse of existing charts + + (+) ?
Only simple logic in templates + + - +
Interaction with k8s + - - -
Repository + + - -
Mature technology - + + +
Manage user credentials + - - -
Manage user certificate + - - -
Controller based installation + - + -
Remove outdated objects +(1) + + -
Migrate existing objects +(1) - - -

(1): Must be implemented inside apply method.

Reference

The following section describes the available methods inside Chart.star

Chart
chart("<url>",namespace=namespace,proxy='off',...)

An new chart is created.
If no namespace is given, the namespace is inherited from the parent chart.

Parameter Description
url The chart is loaded from the given url. The url can be relative. In this case the chart is loaded from a path relative to the current chart location.
namespace If no namespace is given, the namespace is inherited from the parent chart.
suffix This suffix is appended to each chart name. The suffix is inhertied from the parent if no value is given
proxy If local or remote, a proxy for the chart is returned. Applying or deleting a proxy chart is done by applying a CustomerResource to kubernetes. The installation process is then performed by the shalm-controller in the background
... Additional parameters are passed to the init method of the corresponding chart.
chart.apply(k8s)

Applies the chart recursive to k8s. This method can be overwritten.

Parameter Description
8s See below
chart.__apply(k8s, timeout=0, glob=pattern)

Applies the chart to k8s without recursion. This should only be used within apply

Parameter Description
k8s See below
timeout Timeout passed to kubectl apply. A timeout of zero means wait forever.
glob Pattern used to find the templates. Default is "*.yaml"
chart.delete(k8s)

Deletes the chart recursive from k8s. This method can be overwritten.

Parameter Description
k8s See below
chart.__delete(k8s, timeout=0, glob=pattern)

Deletes the chart from k8s without recursion. This should only be used within delete

Parameter Description
k8s See below
timeout Timeout passed to kubectl apply, A timeout of zero means wait forever.
glob Pattern used to find the templates. Default is "*.y*ml"
chart.template(glob=pattern)

Renders helm templates and returns a stream. The default implementation of this methods renders

  • all templates in directory templates using helm
  • all templates in directory ytt-templates using eytt

It's possible to override this method.

Parameter Description
glob Pattern used to find the templates. Default is "*.y*ml"
chart.helm(dir,glob=pattern)

Renders helm templates and returns a stream.

Parameter Description
dir Directory to search for templates
glob Pattern used to find the templates. Default is "*.y*ml"
chart.eytt(dir,glob=pattern)

Renders embedded ytt templates and returns a stream.

Parameter Description
dir Directory to search for ytt templates
glob Pattern used to find the templates. Default is "*.y*ml"
chart.ytt(*files)

Renders ytt templates using the ytt binary and returns a stream.

Parameter Description
files These files are passed as -f option to ytt. You can also pass streams returned from helm or eytt
chart.load_yaml(name)

Load values from yaml file inside chart. The loaded values will override the existing values in self.

Parameter Description
name Name of yaml file
Attributes
Name Description
name Name of the chart. Defaults to self.__class__.name
namespace Default namespace of the chart given via command line
__class__ Class of the chart. See chart_class for details
K8s
k8s.delete(kind,name,namespaced=false,timeout=0,namespace=None,ignore_not_found=False)

Deletes one kubernetes object

Parameter Description
kind k8s kind
name name of k8s object
timeout Timeout passed to kubectl apply. A timeout of zero means wait forever.
namespaced If true object in the current namespace are deleted. Otherwise object in cluster scope will be deleted. Default is true
namespace Override default namespace of chart
ignore_not_found Ignore not found
k8s.get(kind,name,namespaced=false,timeout=0,namespace=None,ignore_not_found=False)

Get one kubernetes object. The value is returned as a dict.

Parameter Description
kind k8s kind
name name of k8s object
timeout Timeout passed to kubectl get. A timeout of zero means wait forever.
namespaced If true object in the current namespace are listed. Otherwise object in cluster scope will be listed. Default is true
namespace Override default namespace of chart
ignore_not_found Ignore not found
k8s.watch(kind,name,namespaced=false,timeout=0,namespace=None,ignore_not_found=False)

Watch one kubernetes object. The value is returned as a iterator.

Parameter Description
kind k8s kind
name name of k8s object
timeout Timeout passed to kubectl watch. A timeout of zero means wait forever.
namespaced If true object in the current namespace are listed. Otherwise object in cluster scope will be listed. Default is true
namespace Override default namespace of chart
ignore_not_found Ignore not found
k8s.rollout_status(kind,name,timeout=0,namespace=None,ignore_not_found=False)

Wait for rollout status of one kubernetes object

Parameter Description
kind k8s kind
name name of k8s object
timeout Timeout passed to kubectl apply. A timeout of zero means wait forever.
namespace Override default namespace of chart
ignore_not_found Ignore not found
k8s.wait(kind,name,condition, timeout=0,namespace=None,ignore_not_found=False)

Wait for condition of one kubernetes object

Parameter Description
kind k8s kind
name name of k8s object
condition condition
timeout Timeout passed to kubectl apply. A timeout of zero means wait forever.
namespace Override default namespace of chart
ignore_not_found Ignore not found
k8s.for_config(kube_config_content)

Create a new k8s object for a different k8s cluster

Parameter Description
kube_config_content Content of kube config
k8s.progress(value)

Report progress of installation

Parameter Description
value A value between 0 and 100
user_credential
user_credential(name,username='',password='',username_key='username',password_key='password')

Creates a new user credential. All user credentials assigned to a root attribute inside a chart are automatically applied to kubernetes.

Parameter Description
name The name of the kubernetes secret used to hold the information
username Username. If it's empty it's either read from the secret or created with a random content.
password Password. If it's empty it's either read from the secret or created with a random content.
username_key The name of the key used to store the username inside the secret
password_key The name of the key used to store the password inside the secret
Attributes
Name Description
username Returns the content of the username attribute. It is only valid after calling chart.__apply(k8s) or it was set in the constructor.
password Returns the content of the password attribute. It is only valid after calling chart.__apply(k8s) or it was set in the constructor.
certificate
certificate(name,ca_key='ca.crt',private_key_key='tls.key',cert_key='tls.crt',is_ca=false,signer=None,domains=[],validity='P3M')

Creates a new certificate. All certificates assigned to a root attribute inside a chart are automatically applied to kubernetes.

Parameter Description
name The name of the kubernetes secret used to hold the information
ca_key The key which is used to store the CA into the secret
private_key_key The key which is used to store the private key into the secret
cert_key The key which is used to store the certificate into the secret
is_ca
signer The signing certificate
validity The period if validity in ISO-8601 format
domains The list of DNS names
config_value
config_value(name,type='string',default='',description='Long description',options=[])

Creates a config value. The user is asked for the value.

Parameter Description
name The name of the kubernetes secret used to hold the information. Also the name of the config value
type Can be string,password,bool,selection. Default if string
default Default value
description A description
options Options. Only valid for type selection
Attributes
Name Description
value The value given by the user
struct

See bazel documentation. to_proto and to_json are not yet supported.

chart_class

The chart_class represents the values read from the Chart.yaml file

stream

The stream class represents the values returned from template, helm, ytt or eytt methods. Streams have not methods. They can be passed to other templating functions. You can use str to convert them to strings

self.config=str(self.eytt("template-dir"))
Attributes
Name Description
api_version API version
name Name
version Version
description Description
keywords Keywords
home Home
sources Sources
icon Icon
utility variables
Name Description
version shalm version
kube_version Kubernetes version

Testing

Tests can be written in starlark. The following symbols are predefined

Name Description
chart(url,...) Function to load shalm chart. The url can be given relative to the test script
k8s In memory implemention of k8s
env(name) Read environment variable
assert.fail(msg) Make test fail with given message
assert.true(cond,msg) Make test fail with given message if cond is false
assert.eq(v1,v2) Assert equals
assert.neq(v1,v2) Assert not equals
c = chart("../charts/example/simple/mariadb")
c.apply(k8s)
mariadb = k8s.get("statefulset","mariadb-master")
assert.eq(mariadb.metadata.name,"mariadb-master")
assert.neq(mariadb.metadata.name,"mariadb-masterx")
Running tests
shalm test test/*.star

Difference to helm

  • Subcharts are not loaded automatically. They must be loaded using the chart command
  • Global variables are not supported.
  • The --set command line parameters are passed to the init method of the corresponding chart. It's not possible to set values (from values.yaml) directly. If you would like to set a lot of values, it's more convenient to write a separate shalm chart.
  • shalm doesn't track installed charts on a kubernetes cluster (except you are using kapp for deployment). It works more like kubectl apply
  • The .Release.Name value is build as follows: <chart.name>-<chart.suffix>. If no suffix is given, the hyphen is also ommited.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
api
v1alpha2
Package v1alpha2 contains API Schema definitions for the shalm v1alpha2 API group +kubebuilder:object:generate=true +groupName=kramerul.github.com
Package v1alpha2 contains API Schema definitions for the shalm v1alpha2 API group +kubebuilder:object:generate=true +groupName=kramerul.github.com
examples
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL