Skip to main content

Simple kubernetes / openshift templating engine with templating, libraries, state tracking

Project description

Kubernetes / openshift templating and deployment engine

Simple python templating and deployment engine for openshift/k8 yml files. Detect changes made and only applies the required objects.

Use case

This tool was born in the need to a simple templating system which can track changes (similar to terraform) but with k8s object awareness.

Installation

  • Python 3.8 or later
  • kubectl or oc binary in path
pip install octoploy

Usage

graph  TB
    root[Root folder<br>_root.yml]
    root --> app1[App 1<br>_index.yml<br>k8s yml files]
    root --> app2[App 2<br>_index.yml<br>k8s yml files]

In octoploy you define apps, each app can contain multiple yml files. Additionally, there is a project configuration which describes the k8 namespace.

All yml files will be pre-processed before they will be imported. This includes replacing any known ${KEY} variables with their associate values and merging referenced templates.

Deploy / Plan

Deploys everything

octoploy deploy-all

Deploys all object of the app with the give name

octoploy deploy nginx

The same commands are available for plan - which will list changes to be applied.

octoploy plan / plan-all

This command executes the on-config-change trigger

octoploy reload prometheus

Config structure

configs
|- _root.yml <- Root config
|- my-app <- App
    |- _index.yml <- App config
    |- dc.yml <- Openshift/K8 yml file(s)
    |- secrets.yml < Encrypted secrets file

Root config

Here is a sample _root.yml file

# Name of the namespace / openshift project
project: 'my-oc-project'

# K8s context which should be used for deploying
context: 'my-k8s-cluster-config'

# OPTIONAL STUFF
# Global variables
vars:
  DOMAIN: "dev-core.org"

App config

An app is represented by a folder containing an _index.yml file and any additional openshift yml files. The following shows all available parameters. Only the first 3 are required.

# The type defines how the app will be used.
# Can be "app" (default) or "template"
type: 'app'

# Indicates if this app should be deployed or ignored
enabled: true

# Name of the deployment, available as variable, see below
name: 'my-app'

# Template which should be applied, none by default
applyTemplates: [ ]

# Templates which should be applied AFTER processing the other templates and base yml files
postApplyTemplates: [ ]

# Action which should be executed if a configmap has been changed
on-config-change:
  # Available options: 
  # deploy (re-deploys the deployment config - openshift specific)
  - deploy

  # exec (Executes a command inside the running container)
  - exec:
      command: /bin/sh
      args:
        - "-c"
        - "kill -HUP $(ps a | grep prometheus | grep -v grep | awk '{print $1}')"

# Additional variables which are used for replacements
vars:
  NSQ_NAME: 'nsq-core'

# Required parameters which must be passed to this app.
# This can be done via the CLI or if this app is a template the referring app can define it in "vars" 
params:
  - PASSWORD

# File based configmaps
configmaps:
  - name: nginx-config
    files:
      - file: "nginx.conf"

Configmaps

In addition to the regular configmaps you can also define configmaps with a file source. This is done in the _index.yml file:

configmaps:
  - name: nginx-config
    files:
      - file: "nginx.conf"

This will create a new configmap from the file nginx.conf with the name nginx-config. Any changes made to the file will be automatically deployed.

Variables

You can refer to variables in yml files by using ${VAR-NAME}. Variables can also be loaded from files (see loaders below).

# _index.yml
vars:
  # Regular key/value assignment
  key: value

It is also possible to decorate objects using variables and merge them into other objects:

# _index.yml
vars:
  someMerging:
    hello: world
    replicas: 2
# dc.yml
spec:
  replicas: 1
  _merge: ${someMerging}

Results in:

spec:
  replicas: 2
  hello: world

Global variables

The following variables are available anywhere inside the yml files by default

Key Value
APP_NAME Name of the app in the _index.yml
NAMESPACE Name of the namespace in _root.yml

Value Loaders

You can load values from various sources using value loaders

vars:
  # This will load the public/private and intermediate certs
  # from a pem file and store it in *_KEY, *_PUBLIC, *_CACERT
  # where * is the key of the value.
  # In this example: CERT_KEY, CERT_PUBLIC, CERT_CACERT
  CERT:
    loader: pem
    file: my-cert.pem

  # Load the content of a file into "MY_FILE"
  MY_FILE:
    loader: file
    file: someFile.bin
    # Optional: Defines in which encoding the file content should be read
    # utf-8 by default, only applicable if "conversion" is not set.
    encoding: utf-8

    # Optional: Conversion can be used to convert a binary file 
    # into a string representation, in this base base64 
    conversion: base64

  # Load the content of environment variables into ENV_*
  ENV:
    loader: env

Templates

You can use templates to reuse and generate yml files. This might be useful when you want to attach sidecar containers to multiple apps. To do so you create a new app with the type field set to template. Other apps can now refer to this template via the applyTemplates or postApplyTemplates field. Templates can refer to other templates (recursively). Any vars defined are passed to the next template.

classDiagram
    App <|-- Root: App receives global variables
    Template <|-- App: Template receives variables

    Template --|>App: Injects yml files into app
    class Root{
      +variables
    }
    class App{
      +parameters
      +k8s yml files
    }
    class Template{
      +parameters
      +k8s yml files
    }  

Example:

|- some-template
    |- _index.yml
    |- dc.yml

|- my-app
    |- _index.yml <-- Referes to "some-template"
    |- others.yml

Will result in

|- my-app
    |- _index.yml
    |- others.yml
    |- dc.yml

Object merging

The template engine support content aware object merging. This allows you to decorate existing templates or enhance apps with features. A common example would be a template which adds a monitoring sidecar container. In the examples you can find the nsq-template which defines a sidecar container.

Here is a minimal example:

# examples/nsq-template/dc.yml
kind: DeploymentConfig
apiVersion: v1
spec:
  template:
    spec:
    containers:
      - name: "nsqd"
      image: "nsqio/nsq"
# examples/my-app/dc.yml
kind: DeploymentConfig
apiVersion: v1
metadata:
  name: "${DC_NAME}"

spec:
  replicas: 1
  selector:
    name: "${DC_NAME}"
  strategy:
    type: Rolling

  template:
    metadata:
      labels:
        name: "${DC_NAME}"

    spec:
      containers:
        - name: "${DC_NAME}"
          image: "docker-registry.default.svc:5000/oc-project/my-app:prod"

If we now apply the nsq-template to our app using postApplyTemplates: [nsq-template] the DeploymentConfig object gets automatically merged:

# Merged result after applying template
kind: DeploymentConfig
apiVersion: v1
metadata:
  name: "${DC_NAME}"

spec:
  replicas: 1
  selector:
    name: "${DC_NAME}"
  strategy:
    type: Rolling

  template:
    metadata:
      labels:
        name: "${DC_NAME}"

    spec:
      containers:
        - name: "${DC_NAME}"
          image: "docker-registry.default.svc:5000/oc-project/my-app:prod"
        # This is the part of the template
        - name: "nsqd"
          image: "nsqio/nsq"

Loops

Loops allow you to apply the same template with different parameters. This is useful when deploying microservices which all have the same openshift config.

# _index.yml
enabled: true
applyTemplates: [ api-template ]
forEach:
  # NAME is required for each instance that should be created
  - NAME: entity-compare-api
    # You can define other vars as well
    PORT: 8080

  - NAME: favorite-api
    PORT: 8081

Library

It's possible to define whole project as a library. This allows all apps and templates to be reused by another project. An example would be the same setup for multiple systems which are separated by projects (e.g. dev/test/prod).

# testLib/_root.yml
type: library

# Required parameters
params:
  - domain
  - dockerDomain
  - imageStreamTag
# prod/_root.yml
project: 'prod-project'
inherit: testLib

# Required parameters
vars:
  domain: my-prod.dev-core.org
  dockerDomain: prod-docker.com
  imageStreamTag: prod

When you now deploy the prod project it will inherit all apps inside testLib.

Secrets

You can encrypt your k8s secrets using

export OCTOPLOY_KEY="my password"
octoploy encrypt secrets.yml

The file will be updated in place.

For deploying encrypted secrets, you'll need to set the environment variable OCTOPLOY_KEY with your key used to encrypt the data.

Change tracking

Changes are detected by storing a md5 sum in the label of the object. If this hash has changed the whole object will be applied. If no label has been found in openshift the object is assumed to be equal, and the label is added.

Currently, the actual fields of the yml files are not compared, however this is a planned feature.

Examples

All examples can be found in the examples folder.

Contribute

The code should be mostly commented. If you found a bug or want to improve something feel free to open an issue and discuss your ideas.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

octoploy-1.1.0.tar.gz (44.9 kB view hashes)

Uploaded Source

Built Distribution

octoploy-1.1.0-py3-none-any.whl (52.3 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page