§ 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.
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.
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.
package estate
{
"buckets": {
"logs": {
"name": "logs",
"versioning": true,
"tags": { "owner": "platform" }
},
"uploads": {
"name": "uploads",
"versioning": false,
"tags": { "owner": "web" }
}
}
}
Evaluate the file.
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.
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.
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.
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.
package estate
buckets: {
logs: {
name: "logs"
versioning: true // keep old versions
tags: owner: platform
}
uploads: {
name: "uploads"
versioning: false
tags: owner: "web"
}
}
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.owneris 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
platformand finds none. A later lesson calls such a name a reference. - Location.
./estate.cue:7:16is 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.
package estate
buckets: {
logs: {
name: "logs"
versioning: true // keep old versions
tags: owner: "platform"
}
uploads: {
name: "uploads"
versioning: false
tags: owner: "web"
}
}
same
Exercise
Shorten the rules
The rules arrived as JSON. Keep the buckets as they are and shorten the rules field without changing the data.
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.
The check compares the output with before.txt. It also fails while any label has quotes or any comma remains.
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.
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
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 estateabove 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.