Import Configuration



To save you time, effort, and mistakes, Cue has some commands to import and minimize your existing configurations into Cue.

Importing configuration

cue import will process files and directories of Yaml and JSON into Cue values. There are many flags for controlling how this happens and how the output is organized.

Let’s continue with our Cuetorials Kubernetes example. Run the following command in the same directory as the cuetorials.yaml file:

cue import -f -o cuetorials.cue -l 'strings.ToLower(kind)' -l 'metadata.name' -p cuetorials cuetorials.yaml
  • -f overwrites the output file
  • -o sets the output filename
  • -l tells Cue how to name the values in the output so that they do not collide. Here we are using <kind>: <name>: {...}
  • -p tells Cue to put the output in package cuetorials
  • the last argument is our file, but can also be a glob or directories (./...)

cuetorials.cue

package cuetorials

deployment: cuetorials: {
	apiVersion: "apps/v1"
	kind:       "Deployment"
	metadata: {
		name:      "cuetorials"
		namespace: "websites"
		labels: app: "cuetorials"
	}
	spec: {
		selector: matchLabels: app: "cuetorials"

		strategy: {
			type: "RollingUpdate"
			rollingUpdate: {
				maxSurge:       1
				maxUnavailable: 0
			}
		}
		minReadySeconds: 5

		template: {
			metadata: labels: app: "cuetorials"
			spec: containers: [{
				name:            "website"
				image:           "us.gcr.io/hof-io--develop/cuetorials.com:manual"
				imagePullPolicy: "Always"
				ports: [{
					containerPort: 80
					protocol:      "TCP"
				}]

				readinessProbe: {
					httpGet: port: 80
					initialDelaySeconds: 6
					failureThreshold:    3
					periodSeconds:       10
				}
				livenessProbe: {
					httpGet: port: 80
					initialDelaySeconds: 6
					failureThreshold:    3
					periodSeconds:       10
				}
			}]
		}
	}
}
service: cuetorials: {
	apiVersion: "v1"
	kind:       "Service"
	metadata: {
		name:      "cuetorials"
		namespace: "websites"
		labels: app: "cuetorials"
	}
	spec: {
		selector: app: "cuetorials"
		type: "ClusterIP"
		ports: [{
			port:       80
			targetPort: 80
		}]
	}
}
ingress: cuetorials: {
	apiVersion: "extensions/v1beta1"
	kind:       "Ingress"
	metadata: {
		name:      "cuetorials"
		namespace: "websites"
		labels: app: "cuetorials"
		annotations: {
			"kubernetes.io/tls-acme":                         "true"
			"kubernetes.io/ingress.class":                    "nginx"
			"nginx.ingress.kubernetes.io/force-ssl-redirect": "true"
			"cert-manager.io/cluster-issuer":                 "letsencrypt-prod"

			"kubernetes.io/configuration-snippet": """
				location ~* \\.(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
				  expires 1h;
				  access_log off;
				  add_header Cache-Control \"public\";
				}
				location / {
				  expires -1;
				}

				"""
		}
	}

	spec: {
		tls: [{
			hosts: [
				"cuetorials.com",
			]
			secretName: "cuetorials-tls"
		}]

		rules: [{
			host: "cuetorials.com"
			http: paths: [{
				backend: {
					serviceName: "cuetorials"
					servicePort: 80
				}
			}]
		}]
	}
}

Importing Go to Cue

Cue can turn Go types into Cue definitions. This is helpful when you have existing code and you want to validate Yaml or JSON that will be unmarshaled into these types. You can also generate Go helpers functions for validating these types from the Cue schema (see Cuelang docs, requires writing go).

Before we can import Go to Cue, we need to setup a file needed for Cue modules to work. We’ll talk about modules and packages in an upcoming section, for now create the following file cue.mod/module.cue:

module: "github.com/hofstadter-io/cuetorials"

Then use the following command to init go.mod file,

go mod init  "github.com/hofstadter-io/cuetorials.com" 

With that in place, what we are going to do is:

  1. Download the Kubernetes API code (you need a local copy)
  2. Import the Go into Cue definitions
  3. Apply the Kubernetes Cue to our Cuetorials Cue
  4. Validate that our config complies with the Kubernetes spec
# "go get" the Kubernetes code
go get k8s.io/api/...

# "cue get go" to import into Cue defs
cue get go k8s.io/api/...

# inspect what "cue get go" into the cue.mod/gen directory
tree cue.mod/gen/

Now add the following lines to your cuetorials.cue file:

package cuetorials // alread there

import (
	apps "k8s.io/api/apps/v1"
	core "k8s.io/api/core/v1"
	extn "k8s.io/api/extensions/v1beta1"
)

deployment: [string]: apps.#Deployment
service: [string]:    core.#Service
ingress: [string]:    extn.#Ingress

// ...

Now try running cue eval cuetorials.cue. You are likely to see some errors. This is because Kubernetes accepts both strings and integers in several places. To fix these, add the IntVal: <#> before the integer (or StrVal: if you are using a different yaml that has strings).

  • deployment >>> rollingUpdate
  • deployment >>> httpGet
  • service >>> targetPort
  • ingress >>> servicePort

Once you get cue eval working (printing out Cue values) you can run cue trim on the file to remove redundant code (based on minimization and defaults). Advanced trimming is on the roadmap for Cue via anti-unification.

package cuetorials

import (
	apps "k8s.io/api/apps/v1"
	core "k8s.io/api/core/v1"
	extn "k8s.io/api/extensions/v1beta1"
)

deployment: [string]: apps.#Deployment
service: [string]:    core.#Service
ingress: [string]:    extn.#Ingress

deployment: cuetorials: {
	apiVersion: "apps/v1"
	kind:       "Deployment"
	metadata: {
		name:      "cuetorials"
		namespace: "websites"
		labels: app: "cuetorials"
	}
	spec: {
		selector: matchLabels: app: "cuetorials"

		strategy: {
			type: "RollingUpdate"
			rollingUpdate: {
				maxSurge: IntVal:       1
				maxUnavailable: IntVal: 0
			}
		}
		minReadySeconds: 5

		template: {
			metadata: labels: app: "cuetorials"
			spec: containers: [{
				name:            "website"
				image:           "us.gcr.io/hof-io--develop/cuetorials.com:manual"
				imagePullPolicy: "Always"
				ports: [{
					containerPort: 80
					protocol:      "TCP"
				}]

				readinessProbe: {
					httpGet: port: IntVal: 80
					initialDelaySeconds: 6
					failureThreshold:    3
					periodSeconds:       10
				}
				livenessProbe: {
					httpGet: port: IntVal: 80
					initialDelaySeconds: 6
					failureThreshold:    3
					periodSeconds:       10
				}
			}]
		}
	}
}
service: cuetorials: {
	apiVersion: "v1"
	kind:       "Service"
	metadata: {
		name:      "cuetorials"
		namespace: "websites"
		labels: app: "cuetorials"
	}
	spec: {
		selector: app: "cuetorials"
		type: "ClusterIP"
		ports: [{
			port: 80
			targetPort: IntVal: 80
		}]
	}
}
ingress: cuetorials: {
	apiVersion: "extensions/v1beta1"
	kind:       "Ingress"
	metadata: {
		name:      "cuetorials"
		namespace: "websites"
		labels: app: "cuetorials"
		annotations: {
			"kubernetes.io/tls-acme":                         "true"
			"kubernetes.io/ingress.class":                    "nginx"
			"nginx.ingress.kubernetes.io/force-ssl-redirect": "true"
			"cert-manager.io/cluster-issuer":                 "letsencrypt-prod"

			"kubernetes.io/configuration-snippet": """
				location ~* \\.(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
				  expires 1h;
				  access_log off;
				  add_header Cache-Control \"public\";
				}
				location / {
				  expires -1;
				}

				"""
		}
	}

	spec: {
		tls: [{
			hosts: [
				"cuetorials.com",
			]
			secretName: "cuetorials-tls"
		}]

		rules: [{
			host: "cuetorials.com"
			http: paths: [{
				backend: {
					serviceName: "cuetorials"
					servicePort: IntVal: 80
				}
			}]
		}]
	}
}

You may be saying, “this wackiness about int/str ports does not match my kubernetes files” and you are totally right. We’ll see later how to work with OpenAPI specs and more in the integrations sections.

We'll never share your email with anyone else.
2023 Hofstadter, Inc