Import YAML as a Component

Quickly move configurations to ksonnet

Overview

Transitioning from YAML to ksonnet can be challenging because ksonnet introduces many new concepts. This document will show how to import a YAML configuration directly into a ksonnet application.

Then some of the drawbacks of importing YAML will be show and how moving to Jsonnet can provide more flexibility in a configuration.

Starting with a configuration

Beginning with an NGINX deployment, ksonnet offers to manage configurations that differ slightly. The configuration below with replicas and image will be parametrized since changing the number of replicas or NGINX version are common changes.

nginx-deployment.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

Importing a YAML file

The fastest way to get started with a component is to import the YAML in the components/ directory of the ksonnet application. Use the -f flag to specify the filename and --module as / as the default.

$ ks import -f /path/to/nginx-deployment.yaml --module /
INFO Writing component at '$HOME/app/components/deployment-nginx-deployment-dkecx.yaml'

This updates params.libsonnet with a reference to the imported YAML file where it can be further modified to add parameters.

params.libsonnet
1
2
3
4
5
6
{
  global: {},
  components: {
    "deployment-nginx-deployment-dkecx": {},
  },
}
Note: It is also possible to directly drop-in YAML into the components directory. However, this feature is currently in alpha.

Set a parameter

In order to override the key value pair, use +: to select spec and replace the value of replicas from nginx-deployment.yaml from 2 to 10.

param.libsonnet
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  global: {},
  components: {
    "deployment-nginx-deployment-dkecx"+: {
      spec+: {
        replicas: 10,
      },
    },
  },
}

List the parameters to see the override added in Jsonnet listed as a key value pair.

$ ks param list
COMPONENT                         PARAM VALUE
=========                         ===== =====
deployment-nginx-deployment-dkecx spec  { replicas: 10 }

Deeply nested parameter

Overriding the image requires access to a deeper level inside the component. Since containers is an array containing an object, one way to override the container is to add information about the container.

param.libsonnet
 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
{
  global: {},
  components: {
    "deployment-nginx-deployment-dkecx"+: {
      spec+: {
        replicas: 10,
        template+: {
          spec+: {
            containers+: [
              {
                name: "nginx",
                image: "nginx:latest",
                ports: [
                  {
                    containerPort: 80,
                  },
                ],
              },
            ],
          },
        },
      },
    },
  },
}

When inspecting the parameters again, observe the changes are both under spec as the parameter despite only trying to change replicas and image. This approach is undesirable because it repeats information already contained in the YAML. Moreover, the parameters cannot be set independently.

$ ks param list
COMPONENT                         PARAM VALUE
=========                         ===== =====
deployment-nginx-deployment-dkecx spec  { replicas: 10, template: {
  spec: {
    containers: [
      {
        image: 'nginx:latest',
        name: 'nginx',
        ports: [
          {
            containerPort: 80,
          },
        ],
      },
    ],
  },
} }

The section below shows how switching to Jsonnet can solve these two problems.

Switching to Jsonnet

Convert the YAML into JSON using a tool such as yq. In this example, nginx-deployment.yaml is converted into nginx-deployment.jsonnet then placed in the components/ directory.

In params.libsonnet, add the name of the component, nginx-deployment. Then add the key value pairs replicas and image.

params.libsonnet
1
2
3
4
5
6
7
8
9
{
  global: {},
  components: {
    'nginx-deployment': {
      replicas: 10,
      image: "nginx:latest",
    },
  },
}

Import the values from param.libsonnet by passing the component name as a params variable. This will expose the two key pairs set in the previous step to be accessible as params.replicas and params.image.

nginx-deployment.jsonnet
 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
local params = std.extVar("__ksonnet/params").components["nginx-deployment"];

{
  "apiVersion": "apps/v1",
  "kind": "Deployment",
  "metadata": {
    "name": "nginx-deployment"
  },
  "spec": {
    "selector": {
      "matchLabels": {
        "app": "nginx"
      }
    },
    "replicas": params.replicas,
    "template": {
      "metadata": {
        "labels": {
          "app": "nginx"
        }
      },
      "spec": {
        "containers": [
          {
            "name": "nginx",
            "image": params.image,
            "ports": [
              {
                "containerPort": 80
              }
            ]
          }
        ]
      }
    }
  }
}

Finally, list the parameters again and see these are two values that can be configured independently.

$ ks param list
COMPONENT        PARAM    VALUE
=========        =====    =====
nginx-deployment image    'nginx:latest'
nginx-deployment replicas 10

This approach is cleaner because it does not require tracking the nested objects explicitly. While importing YAML is convenient, changing nested objects can be difficult. Switching to Jsonnet provides more options when working with complex configurations.

Last updated on: September 4, 2018