§ A1 · Track A · CUE from zero

Your data is already CUE

A CUE file may contain plain JSON. You shorten it one piece of punctuation at a time, and the data never changes.

Time
6 min
Level
beginner
Needs
A pravo binary on your path (Quick start). No provider, no account.
You will learn
Is JSON valid CUE? Which punctuation can you drop without changing the data?
Done when
pravo eval prints the same data for the JSON you pasted and for the short CUE you wrote.

View as Markdown

On this page

Your estate is data: buckets, tags, rules. That data often arrives as a JSON export. In HCL, a function decodes the export into a value, and the configuration reads the value.

main.tf
locals {
  estate = jsondecode(file("${path.module}/estate.json"))
}

The export and the configuration are two syntaxes joined by a function call.

In CUE, a JSON file is already a CUE file. Everything else in the language is a convenience that you adopt one piece at a time.

Paste the JSON

Work in an empty directory named estate. Save the export as estate.cue. Put package estate on the first line, and paste the JSON under it without changing a character. The package line makes the file part of a package. A4 explains it. Until then, write it first in every file.

estate.cuevalid
package estate

{
  "buckets": {
    "logs": {
      "name": "logs",
      "versioning": true,
      "tags": { "owner": "platform" }
    },
    "uploads": {
      "name": "uploads",
      "versioning": false,
      "tags": { "owner": "web" }
    }
  }
}

Evaluate the file.

terminal
$pravo eval
buckets: {
	logs: {
		name:       "logs"
		versioning: true
		tags: {
			owner: "platform"
		}
	}
	uploads: {
		name:       "uploads"
		versioning: false
		tags: {
			owner: "web"
		}
	}
}

The file is valid as pasted. pravo eval prints the evaluated data. It prints CUE, not JSON: labels have no quotes, fields have no commas, and the file has no outer braces. You are about to write the file in that form.

A field is a label and a value, such as name: "logs". A struct is a set of fields in braces. Here buckets is a field whose value is a struct. That struct holds the fields logs and uploads.

Drop what CUE does not need

Save the output first. You compare against it after every change.

terminal
$pravo eval > before.txt

CUE accepts the JSON you pasted and four shorter forms of it.

In JSON In CUE Rule
"name": "logs" name: "logs" A label that is a plain word needs no quotes.
}, at the end of a line } A newline ends a field. A comma is optional.
{ ... } around the file nothing A file is a struct.
"tags": { "owner": "web" } tags: owner: "web" A struct with one field folds into a path.

JSON has no comments. CUE has //, which runs to the end of the line. Replace estate.cue with the short form.

estate.cuevalid
package estate

buckets: {
	logs: {
		name:       "logs"
		versioning: true // keep old versions
		tags: owner: "platform"
	}
	uploads: {
		name:       "uploads"
		versioning: false
		tags: owner: "web"
	}
}

The file has no quotes on labels, no commas and no outer braces. Compare its output with the output you saved.

terminal
$pravo eval | diff before.txt - && echo same
same

diff succeeds when its two inputs match, and then echo same runs. The file changed and the data did not. The comment is not part of the output.

Break it

Quotes are optional on labels. They are not optional on string values. Remove the quotes from "platform" and run pravo eval.

estate.cuerejected
package estate

buckets: {
	logs: {
		name:       "logs"
		versioning: true // keep old versions
		tags: owner: platform
	}
	uploads: {
		name:       "uploads"
		versioning: false
		tags: owner: "web"
	}
}
terminalexit 6
$pravo eval
Error: evaluating .:
buckets.logs.tags.owner: reference "platform" not found:
    ./estate.cue:7:16

Read the error from left to right.

  • Path. buckets.logs.tags.owner is the field that failed, counted from the top of the file.
  • Message. A word without quotes is a name. CUE looks for a field called platform and finds none. A later lesson calls such a name a reference.
  • Location. ./estate.cue:7:16 is the file, the line and the column. Column 16 is the first letter of the bare word.

Note

Your terminal prints the full path of the file where this page prints ./estate.cue. This site shortens every path to ./.

Put the quotes back.

estate.cuevalid
package estate

buckets: {
	logs: {
		name:       "logs"
		versioning: true // keep old versions
		tags: owner: "platform"
	}
	uploads: {
		name:       "uploads"
		versioning: false
		tags: owner: "web"
	}
}
terminal
$pravo eval | diff before.txt - && echo same
same

Exercise

Exercise

Shorten the rules

The rules arrived as JSON. Keep the buckets as they are and shorten the rules field without changing the data.

estate.cuevalid
package estate

buckets: {
	logs: {
		name:       "logs"
		versioning: true // keep old versions
		tags: owner: "platform"
	}
	uploads: {
		name:       "uploads"
		versioning: false
		tags: owner: "web"
	}
}

"rules": {
	"https": {
		"port": 443,
		"protocol": "tcp"
	},
	"ssh": {
		"port": 22,
		"protocol": "tcp"
	}
}

Save the output before you edit.

terminal
$pravo eval > before.txt

The check compares the output with before.txt. It also fails while any label has quotes or any comma remains.

check.sh
pravo eval | diff before.txt - > /dev/null || { echo "the data changed"; exit 1; }
if grep -Eq '"[a-z_]+":' estate.cue; then echo "a label still has quotes"; exit 1; fi
if grep -q ',' estate.cue; then echo "a comma is still there"; exit 1; fi
echo ok

Run it on the start state. It fails.

terminalexit 1
$sh check.sh
a label still has quotes

Edit estate.cue until sh check.sh prints ok.

Hint 1

Remove the quotes from the labels first. Then remove the commas. Run sh check.sh after each edit.

Hint 2

The table in Drop what CUE does not need lists every short form. The CUE cheat sheet has them on one page.

Solution
estate.cuevalid
package estate

buckets: {
	logs: {
		name:       "logs"
		versioning: true // keep old versions
		tags: owner: "platform"
	}
	uploads: {
		name:       "uploads"
		versioning: false
		tags: owner: "web"
	}
}

rules: {
	https: {
		port:     443
		protocol: "tcp"
	}
	ssh: {
		port:     22
		protocol: "tcp"
	}
}

Recap

  • A CUE file may hold plain JSON. Put package estate above it.
  • Quotes on labels, commas and the outer braces are optional. A struct with one field folds into a path. // starts a comment.
  • A word without quotes in a value position is a name. String values keep their quotes.

Check yourself

Which punctuation can you drop from a JSON file without changing the data?

The quotes on labels that are plain words, the commas at the end of lines and the outer braces. A struct with one field also folds into a path.

Why does owner: platform fail while owner: "platform" works?

A word without quotes is a name. CUE looks for a field called platform and finds none. A string value needs its quotes.

The next lesson shows that string is a value too.