If you've written any amount of deployment config for a moderately complex app, you might be familiar with the pattern of: the deployment config being structured, the app config being structured, but embedded as a string within the deployment config. This is unfortunate, since you lose syntax checking etc.
Example, yaml in yaml
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: map1
5data:
6 config.yaml: |
7 foo: bar
8 structured:
9 config:
10 - in
11 - a
12 - string
Example: json in hcl
1resource "aws_iam_policy" "policy" {
2 name = "foo"
3 policy = <<EOF
4 {
5 "Version": "1"
6 "Statement": [{
7 "Action": [
8 "ec2:Describe*"
9 ]
10 }]
11 }
12 EOF
13}
One solution would be to break things out into their own files, and include the result, each one gets its own filetype, but that doesn't compose well, and if you need templating within the nested config, you now have an incomplete config with invalid syntax. Also, this is very much tied to the tool you're using
Example: helm
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: map1
5data:
6 config.yaml: {{ $.Files.Get "config.yaml" }}
Example: terraform
1resource "aws_iam_policy" "policy" {
2 name = "foo"
3 policy = file("policy.json")
4}
So, if your config language supports it, you can do the conversions inline:
1resource "aws_iam_policy" "policy" {
2 name = "foo"
3 policy = jsonencode({
4 Version = "1"
5 Statement = [{
6 Action = [
7 "ec2:Describe*",
8 ]
9 }]
10 })
11}
So what do you do? Thankfully, most config tools accept json as input, so instead of writing the You add a level of indirection with cue.
1resource: aws_iam_policy: policy: {
2 name: "foo"
3 policy: json.Marshal({
4 Version: "1"
5 Action: [
6 "ec2:Describe*",
7 ]
8 })
9}
1apiVersion: v1
2kind: ConfigMap
3metadata: name: foo
4data: config.yaml: yaml.Marshal({
5 foo: bar
6 structured: config: [
7 "in",
8 "a",
9 "string",
10 ]
11})