§ A2 · Track A · CUE from zero

A value can be a type

In CUE, int and 30 are both values. One allows every integer and the other allows one.

Time
8 min
Level
beginner
Needs
A1
You will learn
What is a type in CUE? What does it mean for a value to be incomplete?
Done when
pravo eval -c prints the whole estate with no type left in it.

View as Markdown

On this page

In HCL, a type and a value are separate declarations. They sit in separate files and meet by name.

variables.tf
variable "retention_days" {
  type = number
}
terraform.tfvars
retention_days = 30

OpenTofu checks the second file against the first when it plans. The type has no value, and the value has no type.

In CUE, int is a value. So is 30. The first stands for every integer. The second stands for one. A field can hold either, and a bound such as >=1 sits between them.

A value that stands for one thing is concrete. A value that stands for many things is a constraint. 30, "web" and true are concrete. int, string, bool and >=1 are constraints.

You write It allows Kind
int every integer constraint
>=1 every number from 1 upward constraint
30 exactly 30 concrete

The more specific the value, the fewer values it allows.

Leave some values open

Work in the directory from A1. The platform team knows everything about logs. The web team has answered only part of the questions about uploads. Record what is known and leave the rest as constraints. Replace estate.cue. Each bucket gains encrypted and retention_days.

estate.cuevalid
package estate

buckets: {
	logs: {
		name:           "logs"
		versioning:     true // keep old versions
		encrypted:      true
		retention_days: 365
		tags: owner: "platform"
	}
	uploads: {
		name:           "uploads"
		versioning:     false
		encrypted:      bool
		retention_days: >=1
		tags: owner: string
	}
}

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

Ask for the uploads bucket only. The -e flag evaluates one field, given by its path.

terminal
$pravo eval -e buckets.uploads
name:           "uploads"
versioning:     false
encrypted:      bool
retention_days: >=1
tags: {
	owner: string
}

pravo eval prints a constraint as it is. An unfinished value is not an error.

uploads has five fields: name, versioning, encrypted, retention_days and tags.owner. Decide before you open the answer.

Predict Which fields of the uploads bucket are concrete?

name and versioning. They hold "uploads" and false, each one value. The other three hold bool, >=1 and string. The flag -c asks pravo eval to fail unless the value is concrete, so these two commands pass.

terminal
$pravo eval -c -e buckets.uploads.name
$pravo eval -c -e buckets.uploads.versioning
"uploads"
false

Break it

The flag -c makes pravo eval fail unless every value is concrete. Run it on the file you wrote. The file is valid. The command is refused.

terminalrejectedexit 1
$pravo eval -c
Error: the value is not concrete:
buckets.uploads.encrypted: incomplete value bool:
    ./estate.cue:14:19
buckets.uploads.retention_days: incomplete value >=1:
    ./estate.cue:15:19
buckets.uploads.tags.owner: incomplete value string:
    ./estate.cue:16:16

Read the error from left to right.

  • Stage. the value is not concrete says that -c refused the file. The CUE is valid.
  • Path. Each message starts with the path of a field: buckets.uploads.encrypted. There are three, one for each constraint left in uploads.
  • Message. incomplete value bool says that the field holds the constraint bool. It names the constraint after the word value.
  • Location. ./estate.cue:14:19 is the file, the line and the column of the constraint.

Incomplete does not mean wrong. It means not finished. Why is my value incomplete? lists the causes.

Exercise

Exercise

Finish the uploads bucket

The web team replies: encrypted, kept for 30 days, owned by web. Replace the three constraints in uploads with these concrete values.

The check passes when pravo eval -c succeeds.

check.sh
pravo eval -c > /dev/null 2>&1 || { echo "a value is still not concrete"; exit 1; }
echo ok

Run it on the start state. It fails.

terminalexit 1
$sh check.sh
a value is still not concrete
Hint 1

Replace each constraint with one value of the same kind. Use a boolean, a whole number from 1 upward and a string.

Hint 2

pravo eval -c lists every field that is still open. See concrete for the definition.

Solution
estate.cue
package estate

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

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

Recap

  • A type such as int is a value that stands for every value of its kind. 30 stands for one.
  • A constraint allows many values. A concrete value allows one.
  • pravo eval prints constraints as they are. pravo eval -c fails while any remain.

Check yourself

Is string a value?

Yes. It is the value that stands for every string. "web" is a narrower value that stands for one.

What does incomplete value bool tell you?

The field holds the constraint bool and not a concrete value. It is not wrong. It is not finished.

The next lesson writes encrypted: bool and encrypted: true for the same field.