§ C4 · Track A · Companions

eval and validate

What pravo eval and pravo validate check, what they print, and what the flags -c, -e and -O change.

Time
8 min
Level
intermediate
Needs
A7
You will learn
Which command answers which question? What do -c, -e and -O change?
Done when
You can say whether a given file passes pravo eval, pravo eval -c and pravo validate.

View as Markdown

On this page

Two commands read a specification without running it. pravo eval evaluates it and prints the result. pravo validate evaluates it and checks every resource against its provider's schema.

pravo has no vet command. To ask whether every value is final, use pravo eval -c.

Work in an empty directory. Neither command needs a provider for a file that declares none.

Quick reference

Command Use it to Exit status on failure
pravo eval Print the evaluated specification. 6
pravo eval -e EXPR Print one path or one expression. 6, or 1 when the path does not exist
pravo eval -c Fail unless every value is concrete. 1 for a value that is not concrete, 6 otherwise
pravo eval -O Also print optional fields. 6
pravo eval -t key=value Give an @tag field its value. 6
pravo validate Check the specification against the provider schemas. 6
pravo validate --json The same, as one JSON document. 6

Status 6 means the specification does not load, evaluate or validate. Status 1 means a value is missing or not final.

The flags -t and -e combine with -c and -O. -t also works with validate.

What eval prints

pravo eval prints the value of the whole package: references resolved, comprehensions expanded, constraints unified and defaults selected. It prints CUE, in the order the fields were written.

estate.cue
package estate

#Bucket: {
	name!:      string
	versioning: *false | bool
	size?:      int
}

let prefix = "estate"
_zones: ["a", "b"]

class: *"standard" | "ia"
names: [for z in _zones {"\(prefix)-\(z)"}]
logs:  #Bucket & {name: names[0]}
terminal
$pravo eval
class: "standard"
names: ["estate-a", "estate-b"]
logs: {
	name:       "estate-a"
	versioning: false
}

The output holds the result of each step.

  • class shows its default, "standard".
  • names shows the comprehension expanded, with prefix and _zones already used.
  • logs shows the fields of the definition, with the default false selected.

What it leaves out: the definition #Bucket, the let and the hidden field _zones. A definition, a let and a hidden field are not data. Name a definition with -e to see it.

terminal
$pravo eval -e '#Bucket'
name!:      string
versioning: false

pravo eval reads the state of the directory so that it can show recorded values. A directory without one is read as an empty state, and eval creates nothing.

terminal
$test -e pravo.state || echo "no state directory"
no state directory

Concrete values with -c

A value is concrete when it is one final value: "logs", 3, true. A type such as string, a range such as >=1 and a choice such as "standard" | "ia" are not concrete.

pravo eval prints a value that is not concrete as it is written. The flag -c makes the same value an error. It does not change what is printed. It changes which input fails.

estate.cueunintended
package estate

#Bucket: {
	name:       string
	class:      "standard" | "ia"
	versioning: *false | bool
	size?:      int
	owner!:     string
}

bucket: #Bucket
terminal
$pravo eval
bucket: {
	name:       string
	class:      "standard" | "ia"
	versioning: false
	owner!:     string
}
terminalexit 1
$pravo eval -c
Error: the value is not concrete:
bucket.class: incomplete value "standard" | "ia":
    ./estate.cue:11:9
bucket.name: incomplete value string:
    ./estate.cue:4:14
    ./estate.cue:11:9
bucket.owner: field is required but not present:
    ./estate.cue:8:2
    ./estate.cue:11:9

Each error names a path and a reason.

  • incomplete value string means the field is still a type. Write a value.
  • incomplete value "standard" | "ia" means no choice is selected and no default marks one. Write a value or mark a default.
  • field is required but not present means a field declared with ! has no value.

The optional field size? and the defaulted field versioning do not appear. An optional field may stay unset. A default is a value.

Write the missing values and run -c again.

estate.cuevalid
package estate

#Bucket: {
	name:       string
	class:      "standard" | "ia"
	versioning: *false | bool
	size?:      int
	owner!:     string
}

bucket: #Bucket & {name: "logs", class: "ia", owner: "platform"}
terminal
$pravo eval -c
bucket: {
	name:       "logs"
	class:      "ia"
	versioning: false
	owner:      "platform"
}

The exit status is 0, and the output is the value. C2 lists every cause of an incomplete value.

One expression with -e

-e evaluates one path or one CUE expression in the scope of the specification and prints only that.

terminal
$pravo eval -e bucket.class
$pravo eval -e 'len(bucket.name)'
$pravo eval -e 'bucket.owner + "/" + bucket.name'
$pravo eval -e 'strings.ToUpper(bucket.name)'
"ia"
4
"platform/logs"
"LOGS"
  • A path prints the value at that path.
  • An expression may call functions of the standard library without an import.
  • -e sees regular fields and definitions. A hidden field or a let is not visible to it. Copy the value into a regular field to look at it.
  • With -c, only the selected value must be concrete.

A path that does not exist is an error.

terminalexit 1
$pravo eval -e bucket.colour
Error: -e "bucket.colour": undefined field: colour:
    1:8

Optional fields with -O

Without -O, pravo eval leaves out a field declared with ?. With -O it prints the field and its type. A field declared with ! is always printed.

terminal
$pravo eval -O -e bucket
name:       "logs"
class:      "ia"
versioning: false
size?:      int
owner:      "platform"

The flag shows what a definition offers. Combine it with -e to read the whole shape.

terminal
$pravo eval -O -e '#Bucket'
name:       string
class:      "standard" | "ia"
versioning: false
size?:      int
owner!:     string

Tags with -t and -T

A tag is a field marked @tag(name). The flag -t name=value gives it a value. A name that no field declares is refused.

estate.cue
package estate

env: *"dev" | "staging" | "prod" @tag(env)
terminalexit 6
$pravo eval -t env=prod
$pravo eval -t stage=prod
env: "prod"

Error: loading .:
no tag for "stage"

The flag -T fills tags declared with var=, such as @tag(os,var=os), from the system. The names include os, arch and cwd.

What validate checks

pravo validate loads and evaluates the specification. It checks each resource, data source and call against the schema of its provider. It prints one line when everything holds.

terminal
$pravo validate
Success! 0 resources, 0 data sources, 0 ephemeral, 0 function calls, 0 outputs across 0 providers.

For a file without providers, validate loads and evaluates the file as eval does. It prints a count instead of the value. It does not require concrete values.

The counts matter when providers are declared. Declare one and generate its schema with pravo init.

estate.cue
package estate

import rnd "pravo.dev/providers/hashicorp/random"

$random: rnd.#Provider & {$version: "~> 3.6"}

pet: $random.#Resource.random_pet & {
	length:    3
	separator: "-"
}
terminal
$pravo init
$pravo validate
Configuration does not load yet; generating schemas for the declared providers first.
Created cue.mod/module.cue
Providers:
  random = hashicorp/random v3.9.1
Up to date cue.mod/gen/pravo.dev/providers/hashicorp/random (10 resource types, 0 data sources, 2 ephemeral, 0 functions)

pravo has been initialised. Run "pravo plan" to see what it would do.
Success! 1 resources, 0 data sources, 0 ephemeral, 0 function calls, 0 outputs across 1 providers.

pravo validate accepts the resource. It checks the field names, the types and the constraints of the generated definition. Misspell a field.

estate.cuerejected
package estate

import rnd "pravo.dev/providers/hashicorp/random"

$random: rnd.#Provider & {$version: "~> 3.6"}

pet: $random.#Resource.random_pet & {
	lenght:    3
	separator: "-"
}
terminalexit 6
$pravo validate
Error: evaluating .:
pet.lenght: field not allowed:
    ./estate.cue:8:2

pravo validate exits with 6 when the specification does not evaluate or validate. pravo eval exits with 6 for the same file.

terminalexit 6
$pravo eval
Error: evaluating .:
pet.lenght: field not allowed:
    ./estate.cue:8:2

Restore the field name. A resource has attributes that the provider computes, such as id. They are unknown before an apply, so -c over the whole file fails, and validate passes.

estate.cue
package estate

import rnd "pravo.dev/providers/hashicorp/random"

$random: rnd.#Provider & {$version: "~> 3.6"}

pet: $random.#Resource.random_pet & {
	length:    3
	separator: "-"
}
terminalexit 1
$pravo validate
$pravo eval -c
Success! 1 resources, 0 data sources, 0 ephemeral, 0 function calls, 0 outputs across 1 providers.

Error: the value is not concrete:
pet.id: incomplete value string | null

Select a value that is known, and -c passes.

terminal
$pravo eval -c -e pet.length
3
After an apply

An apply records the computed attributes in the state. pravo eval fills them in, so -c passes for the whole file. B3 covers the apply.

terminal
$pravo apply --auto-approve > /dev/null
$pravo eval -c -e pet.id
"quiet-river-fox"

The id is random, so your output shows another name.

Which command, when

Question Command
What does my specification evaluate to? pravo eval
What is this one value? pravo eval -e path
Is every value final? pravo eval -c, with -e for the part you care about
What fields can I still set? pravo eval -O -e '#Shape'
Does each resource match its provider's schema? pravo validate
Should a script stop? The exit status: 6 for an error in the specification, 1 for a value that is missing or not final

A short gate for a script runs both.

pravo validate && pravo eval -c -e buckets

Recap

  • pravo eval prints the evaluated value. It leaves out definitions, let and hidden fields.
  • -c makes a value that is not concrete an error. -e selects one expression. -O adds optional fields.
  • pravo validate checks resources against provider schemas and does not require concrete values. It exits with 6.

The full list of flags is in the command reference.

Check yourself

A file contains size: int. Does pravo eval accept it? Does pravo eval -c?

pravo eval accepts it and prints size: int. pravo eval -c fails with incomplete value int.

Which command refuses a misspelt attribute of a resource, and with which exit status?

pravo validate, with exit status 6. It reports field not allowed.