Skip to content

Commit

Permalink
docs/concept: working with incomplete CUE
Browse files Browse the repository at this point in the history
This adds a concept guide explaining the basics of working with
incomplete CUE using both the cue command and the Go API.

Fixes cue-lang/docs-and-content#180.

Preview-Path: /docs/concept/working-with-incomplete-cue/
Signed-off-by: Paul Jolly <[email protected]>
Change-Id: If2fdd49e42c96cd098a19464038827ad2000e951
Dispatch-Trailer: {"type":"trybot","CL":1203092,"patchset":14,"ref":"refs/changes/92/1203092/14","targetBranch":"master"}
  • Loading branch information
myitcv authored and cueckoo committed Nov 27, 2024
1 parent 597542b commit c816b9d
Show file tree
Hide file tree
Showing 4 changed files with 489 additions and 0 deletions.
157 changes: 157 additions & 0 deletions content/docs/concept/working-with-incomplete-cue/en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
title: "Working with incomplete CUE"
toc_hide: true
authors: [myitcv, jpluscplusm]
tags: [go api]
---

{{{with _script_ "en" "set caches to speed up re-running"}}}
export GOMODCACHE=/caches/gomodcache
export GOCACHE=/caches/gobuild
{{{end}}}

In general, CUE can handle references to fields that don't yet exist, or where
a value can't be calculated because there's insufficient information.
CUE that contains such references or values is referred to as *incomplete* CUE
-- but only if it's otherwise *valid* CUE.

Here's an example of incomplete CUE. The value of `connectionString` can't be
calculated given the information in `database.cue` alone because the value of
the `password` field isn't concrete - it's only a `string` constraint.

{{{with upload "en" "1"}}}
-- database.cue --
package database

connectionString: "\(system)://\(user):\(password)@\(host):\(port)/\(database)"

system: "postgres"
host: "prod.db.example.com"
user: "alex"
port: "5432"
database: "transactions"
password: string
{{{end}}}

CUE allows evaluations to be augmented by extra information introduced through
{{<linkto/inline "tour/basics/unification" >}}.
If we introduce information into an evaluation of the `database.cue` file that
provides a `string` value for the `password` field then the value of the
`connectionString` field could be calculated.
But until that happens, this otherwise valid CUE is referred to as "incomplete"
because (in isolation) it doesn't contain sufficient information to permit a
complete evaluation.

Note that because incomplete CUE is valid CUE it *can* be evaluated ...

{{{with script "en" "cue eval"}}}
cue eval database.cue
{{{end}}}

... but it *can't* be exported:

{{{with script "en" "cue export"}}}
! cue export database.cue
{{{end}}}

A configuration that includes incomplete CUE can be made *complete* by unifying
it with the right information. This means that every field that contributes to
the configuration being emitted must be able to be calculated.

Here's some YAML data that will do this for our example, by "filling in the
gaps" in our incomplete CUE by providing the `password` secret:

{{{with upload "en" "secrets.yaml"}}}
-- secrets.yaml --
password: "Ch^ngeMeBef0r3GoL!ve"
{{{end}}}

Unifying our incomplete CUE with this data makes the configuration complete,
and allows us to export the result:

{{{with script "en" "export example.cue data.yaml"}}}
cue export database.cue secrets.yaml
{{{end}}}

### Using the Go API

The Go API is also able to handle incomplete CUE.

To demonstrate the Go API in action we start by initializing a Go module:

{{{with script "en" "go mod init"}}}
#ellipsis 0
go mod init go.example
{{{end}}}

We place this example Go code in `main.go`.
Its comments explain what is being done at each step.

{{{with upload "en" "main.go"}}}
-- main.go --
package main

import (
"fmt"
"log"

"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
"cuelang.org/go/encoding/yaml"
)

func main() {
ctx := cuecontext.New()

// Step #1: load the CUE package in the current directory.
// It contains a single file - "example.cue", as shown above.
bis := load.Instances([]string{"."}, nil)
step1 := ctx.BuildInstance(bis[0])
fmt.Printf("step1: %v\n", step1)

// Step #2: load the "secrets.yaml" file shown above.
step2File, err := yaml.Extract("secrets.yaml", nil)
if err != nil {
log.Fatal(err)
}
step2 := ctx.BuildFile(step2File)
fmt.Printf("step2: %v\n", step2)

// Ensure that the result of unifying the two steps is both
// valid and concrete - and thus could be exported as data:
result := step1.Unify(step2)
if err := result.Validate(cue.Concrete(true)); err != nil {
log.Fatal(err)
}

// Display the resulting CUE:
fmt.Printf("result: %v\n", result)
}
{{{end}}}

We fetch the latest version of CUE, and tidy our Go module:

{{{with script "en" "go get etc"}}}
#ellipsis 0
go get cuelang.org/go@${CUELANG_CUE_LATEST}
#ellipsis 0
go mod tidy
{{{end}}}

When we run our Go code, it behaves the same as the `cue export` command above
\- except that it *also* displays the interim `step1` and `step2` values:

{{{with script "en" "go run"}}}
go run .
{{{end}}}

{{{with _script_ "en" "https://github.com/cue-lang/cue/issues/3496"}}}
go vet ./...
#ellipsis 0
go run honnef.co/go/tools/cmd/[email protected] ./...
{{{end}}}

## Related content

- Tag: {{<tag "go api">}} -- Guides exploring CUE's Go API
142 changes: 142 additions & 0 deletions content/docs/concept/working-with-incomplete-cue/gen_cache.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package site
{
content: {
docs: {
concept: {
"working-with-incomplete-cue": {
page: {
cache: {
upload: {
"1": "NdJxcqzHmZx0wmQZ1FB4F4gS0JwSEdLTu0vKBUpZ2OU="
"secrets.yaml": "sUqXH6hdWAbMP6JZLqRqBFEi30XoNIyUoKkPOy/lPC8="
"main.go": "34xkXVAj87ZzMsaQXMPo3jk3IlmQ6Cc+npZZStxoe48="
}
multi_step: {
hash: "NKV1EQ4M5GLD5BP194QUQACO0J7A9OGHI3LCFEL8GO1U29E2QRAG===="
scriptHash: "VFD430PCU2LQE0TRAITD5G8HND8BVOBUDLC2T10GLQLACR4P3HOG===="
steps: [{
doc: ""
cmd: "export GOMODCACHE=/caches/gomodcache"
exitCode: 0
output: ""
}, {
doc: ""
cmd: "export GOCACHE=/caches/gobuild"
exitCode: 0
output: ""
}, {
doc: ""
cmd: "cue eval database.cue"
exitCode: 0
output: """
connectionString: "\\(system)://\\(user):\\(password)@\\(host):\\(port)/\\(database)"
system: "postgres"
host: "prod.db.example.com"
user: "alex"
port: "5432"
database: "transactions"
password: string
"""
}, {
doc: ""
cmd: "cue export database.cue"
exitCode: 1
output: """
password: incomplete value string:
./database.cue:10:11
connectionString: invalid interpolation: non-concrete value string (type string):
./database.cue:3:19
./database.cue:10:11
"""
}, {
doc: ""
cmd: "cue export database.cue secrets.yaml"
exitCode: 0
output: """
{
"connectionString": "postgres://alex:Ch^[email protected]:5432/transactions",
"system": "postgres",
"host": "prod.db.example.com",
"user": "alex",
"port": "5432",
"database": "transactions",
"password": "Ch^ngeMeBef0r3GoL!ve"
}
"""
}, {
doc: "#ellipsis 0"
cmd: "go mod init go.example"
exitCode: 0
output: """
...
"""
}, {
doc: "#ellipsis 0"
cmd: "go get cuelang.org/[email protected]"
exitCode: 0
output: """
...
"""
}, {
doc: "#ellipsis 0"
cmd: "go mod tidy"
exitCode: 0
output: """
...
"""
}, {
doc: ""
cmd: "go run ."
exitCode: 0
output: """
step1: {
\tconnectionString: "\\(system)://\\(user):\\(password)@\\(host):\\(port)/\\(database)"
\tsystem: "postgres"
\thost: "prod.db.example.com"
\tuser: "alex"
\tport: "5432"
\tdatabase: "transactions"
\tpassword: string
}
step2: {
\tpassword: "Ch^ngeMeBef0r3GoL!ve"
}
result: {
\tconnectionString: "postgres://alex:Ch^[email protected]:5432/transactions"
\tsystem: "postgres"
\thost: "prod.db.example.com"
\tuser: "alex"
\tport: "5432"
\tdatabase: "transactions"
\tpassword: "Ch^ngeMeBef0r3GoL!ve"
}
"""
}, {
doc: ""
cmd: "go vet ./..."
exitCode: 0
output: ""
}, {
doc: "#ellipsis 0"
cmd: "go run honnef.co/go/tools/cmd/[email protected] ./..."
exitCode: 0
output: """
...
"""
}]
}
}
}
}
}
}
}
}
3 changes: 3 additions & 0 deletions content/docs/concept/working-with-incomplete-cue/page.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package site

content: docs: concept: "working-with-incomplete-cue": page: _
Loading

0 comments on commit c816b9d

Please sign in to comment.