I wager that at that second, you might be considering:
“One other programming language”?
Settle down, settle down, include me, and it’ll make sense ð
Not like different languages like Go or Rust, that are “general-purpose languages,” CUE has some explicit aims. Its identify is definitely an acronym that stands for “Configure Unify Execute,” and in line with the official documentation:
Though the language just isn’t a general-purpose programming language, it has many functions, similar to information validation, information templating, configuration, querying, code era, and even scripting.
It’s described as a “superset of JSON” and is closely impressed by Go. Or, as I prefer to assume:
“Think about that Go and JSON had a romance, and the fruit of that union was CUE” ð
On this publish, I’ll current two situations the place the language can be utilized, however the official documentation has extra examples and an excellent quantity of knowledge to seek the advice of.
Validating information
The primary situation the place CUE excels is in information validation. It has native support for validating YAML, JSON, and Protobuf, amongst others.
I am going to use some examples of configuration files from the Traefik challenge, an API Gateway.
The next YAML defines a sound path to Traefik:
apiVersion: traefik.containo.us/v1alpha1
form: IngressRoute
metadata:
identify: simpleingressroute
namespace: default
spec:
entryPoints:
- net
routes:
- match: Host(`your.instance.com`) && PathPrefix(`/notls`)
form: Rule
companies:
- identify: whoami
port: 80
With this data, it’s doable to outline a brand new route in API Gateway, but when one thing is incorrect, we will trigger some issues. That is why it is important to have a simple method to detect points in configuration recordsdata like this. And that is the place CUE reveals its power.
Step one is to have the language put in on the machine. As I am utilizing macOS, I simply ran the command:
brew set up cue-lang/faucet/cue
Within the official documentation, you may see how one can set up it on different working techniques.
Now we will use the cue
command to show this YAML right into a schema
of the CUE language:
cue import traefik-simple.yaml
A file referred to as traefik-simple.cue
is created with the contents:
apiVersion: "traefik.containo.us/v1alpha1"
form: "IngressRoute"
metadata: {
identify: "simpleingressroute"
namespace: "default"
}
spec: {
entryPoints: [
"web",
]
routes: [{
match: "Host(`your.example.com`) && PathPrefix(`/notls`)"
kind: "Rule"
services: [{
name: "whoami"
port: 80
}]
}]
}
It is a literal translation from YAML to CUE, however let’s edit it to create some validation guidelines. The ultimate content material of traefik-simple.cue
appears to be like like this:
apiVersion: "traefik.containo.us/v1alpha1"
form: "IngressRoute"
metadata: {
identify: string
namespace: string
}
spec: {
entryPoints: [
"web",
]
routes: [{
match: string
kind: "Rule"
services: [{
name: string
port: >0 & <= 65535
}]
}]
}
A number of the objects had been precisely the identical, like apiVersion: "traefik.containo.us/v1alpha1"
and form: "IngressRoute."
Which means that these are the precise values anticipated in all recordsdata that might be validated by this schema.
Any worth totally different from these might be thought of an error. Different data has modified, similar to:
metadata: {
identify: string
namespace: string
}
On this snippet, we outline that the content material of identify,
for instance, could be any legitimate string.
Within the excerpt port: >0 & <= 65535
, we outline that this subject can solely settle for a quantity between 0 and 65535.
It’s now doable to validate that the YAML content material conforms to the schema
utilizing the command:
cue vet traefik-simple.cue traefik-simple.yaml
If all the pieces is appropriate, nothing is displayed on the command line. To show the way it works, I altered traefik-simple. yaml
, altering the worth of port
to 0
. Then, when rerunning the command, you may see the error:
cue vet traefik-simple.cue traefik-simple.yaml
spec.routes.0.companies.0.port: invalid worth 0 (out of certain >0):
./traefik-simple.cue:16:10
./traefik-simple.yaml:14:18
If we alter any of the anticipated values, similar to form: IngressRoute
to one thing totally different, similar to form: Ingressroute,
the result’s a validation error:
cue vet traefik-easy.cue traefik-easy.yaml
form: conflicting values "IngressRoute" and "Ingressroute":
./traefik-easy.cue:2:13
./traefik-easy.yaml:2:8
This manner, discovering an error in a Traefik route configuration could be very simple. The identical could be utilized to different codecs like JSON, Protobuf, Kubernetes recordsdata, and many others.
I see an apparent situation of utilizing this information validation energy: including a step in CI/CDs to make use of CUE and validate configurations at construct
time, avoiding issues within the deploy
stage and utility execution. One other situation is so as to add the instructions in a hook
of Git to validate the configurations within the improvement surroundings.
One other thrilling characteristic of CUE is the potential for creating packages,
which comprise a collection of schemas
that may be shared between tasks in the identical means as a package deal
in Go. Within the official documentation, you may see how one can use this characteristic and a few native packages
of the language, similar to strigs,
lists,
regex,
and many others. We’ll use a package deal
within the following instance.
Configuring functions
One other utilization situation for CUE is as an utility configuration language. Anybody who is aware of me is aware of I’ve no appreciation for YAML (to say the least), so every other possibility catches my eye. However CUE has some thrilling benefits:
- As a result of it’s JSON-based, studying and writing are a lot less complicated (in my view)
- Solves some JSON points like lacking feedback, which was a successful characteristic for YAML
- As a result of it’s a full language, it’s doable to make use of
if,
loop,
built-in packages, sort inheritance, and many others.
Step one for this instance was making a package deal to retailer our configuration. For that, I made a listing referred to as config,
and inside it, a file referred to as config.cue
with the content material:
package deal config
db: {
consumer: "db_user"
password: "password"
host: "127.0.0.1"
port: 3306
}
metric: {
host: "http://localhost"
port: 9091
}
langs: [
"pt_br",
"en",
"es",
]
The subsequent step was to create the appliance that reads the configuration:
package deal predominant
import (
"fmt"
"cuelang.org/go/cue"
"cuelang.org/go/cue/load"
)
sort Config struct {
DB struct {
Person string
Password string
Host string
Port int
}
Metric struct {
Host string
Port int
}
Langs []string
}
// LoadConfig hundreds the Cue config recordsdata, beginning within the dirname listing.
func LoadConfig(dirname string) (*Config, error) {
cueConfig := &load.Config{
Dir: dirname,
}
buildInstances := load.Cases([]string{}, cueConfig)
runtimeInstances := cue.Construct(buildInstances)
occasion := runtimeInstances[0]
var config Config
err := occasion.Worth().Decode(&config)
if err != nil {
return nil, err
}
return &config, nil
}
func predominant() {
c, err := LoadConfig("config/")
if err != nil {
panic("error studying config")
}
//a struct foi preenchida com os valores
fmt.Println(c.DB.Host)
}
One benefit of CUE’s package deal
idea is that we will break our configuration into smaller recordsdata, every with its personal performance. For instance, contained in the config
listing, I break up config. Cue
into separate recordsdata:
config/db.cue
package deal config
db: {
consumer: "db_user"
password: "password"
host: "127.0.0.1"
port: 3306
}
config/metric.cue
package deal config
metric: {
host: "http://localhost"
port: 9091
}
config/lang.cue
package deal config
langs: [
"pt_br",
"en",
"es",
]
And it was not crucial to alter something within the predominant.go
file for the settings to be loaded. With this, we will higher separate the contents from the settings with out impacting the appliance code.
Conclusion
I simply “scratched the floor” of what is doable with CUE on this publish. It has been attracting attention and being adopted in tasks similar to Istio, which it makes use of to generate OpenAPI schemes
and CRDs for Kubernetes and Dagger. It’s a instrument that may be very helpful for a number of tasks, primarily on account of its information validation energy. And as a substitute for YAML, for my private pleasure ð
Initially printed at https://eltonminetto.dev on November 08, 2022.