Skip to content

Commit

Permalink
feat: Demonstrate proto.libsonnet
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Baldry <jack.baldry@grafana.com>
  • Loading branch information
jdbaldry committed Sep 15, 2021
1 parent 67e63a2 commit 7b5a3b0
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
129 changes: 129 additions & 0 deletions README.md
@@ -0,0 +1,129 @@
# Jsonnet prototype

Jsonnet prototype presents an approach to writing Jsonnet libraries that provides easier inspection by an end-user.

- Every Jsonnet file should be able to be manifested individually using the standard Jsonnet tool.
- Every field should be manifested.
- Every Jsonnet file should a corresponding `.proto.libsonnet` file.

If every field of every library file can be manfiested individually, the end-user can easily use the Jsonnet tool to
understand the data that your library produces. Furthermore, the `.proto.libsonnet` file provides easy insight into the
high level structure of your library.

## Inspecting a Jsonnet file

A discussion of the usefulness of runtime errors is outside of the scope of this demonstration.
```console
$ jsonnet -e "(import 'deployment.jsonnet')"
RUNTIME ERROR: an image must be provided as $._config.image
deployment.jsonnet:4:12-64 object <anonymous>
deployment.jsonnet:(2:12)-(5:4) object <anonymous>
During manifestation
$ jsonnet -e "(import 'deployment.jsonnet') { _config+: { image: 'foo' } }"
RUNTIME ERROR: a name must be provided as $._config.name
deployment.jsonnet:3:11-60 object <anonymous>
<cmdline>:1:31-61 object <anonymous>
During manifestation
$ jsonnet -e "(import 'deployment.jsonnet') { _config+: { image: 'foo', name: 'bar' } }"
{
"_config": {
"image": "foo",
"name": "bar"
},
"deployment": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"labels": {
"name": "bar"
},
"name": "bar"
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"name": "bar"
},
"template": {
"metadata": {
"labels": {
"name": "bar"
}
},
"spec": {
"containers": [
{
"image": "foo",
"name": "bar"
}
]
}
}
}
}
}
}
```

## Producing intended output

The previous example resulted in manifesting the completely expanded JSON representation of the Jsonnet file.
It is often the case that one or more fields should not be present in the manifested JSON. In this example, the `_config: {}` field is not a desired output.
The `deployment.proto.libsonnet` file is what determines the final output of the Jsonnet evaluation.

```console
$ jsonnet -e "(import 'deployment.proto.libsonnet') + (import 'deployment.jsonnet') { _config+: { image: 'foo', name: 'bar' } }"
{
"deployment": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"labels": {
"name": "bar"
},
"name": "bar"
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"name": "bar"
},
"template": {
"metadata": {
"labels": {
"name": "bar"
}
},
"spec": {
"containers": [
{
"image": "foo",
"name": "bar"
}
]
}
}
}
}
}
}
```

## Downsides

- Changes to the library structure must be reflected in two places. Though this is only true of the top level objects.
- The prototype file is not actually able to be fully manifested without additional Jsonnet code:
```console
$ jsonnet -e "(import 'deployment.proto.libsonnet')"
{
"deployment": { }
}
$ # Workaround to show all fields.
$ jsonnet --tla-code "expr=(import 'deployment.proto.libsonnet')" prototype.jsonnet
{
"_config": "hidden object",
"deployment": "object"
}
```
38 changes: 38 additions & 0 deletions deployment.jsonnet
@@ -0,0 +1,38 @@
{
_config: {
name: error 'a name must be provided as $._config.name',
image: error 'an image must be provided as $._config.image',
},

deployment: {
local name = $._config.name,
local image = $._config.image,

apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: {
name: name,
labels: {
name: name,
},
},
spec: {
replicas: 1,
selector: {
matchLabels: {
name: name,
},
template: {
metadata: {
labels: {
name: name,
},
},
spec: {
containers: [{ name: name, image: image }],
},
},
},
},
},
}
8 changes: 8 additions & 0 deletions deployment.proto.libsonnet
@@ -0,0 +1,8 @@
// deployment.proto.libsonnet describes the prototypical structure of the Jsonnet library.
{
// The `_config+:: {}` field is hidden as it is considered "input" to the library.
_config:: {},

// The `deployment: {}` field is visible as it is considered "output" of the library.
deployment: {},
}
13 changes: 13 additions & 0 deletions prototype.jsonnet
@@ -0,0 +1,13 @@
// prototype.jsonnet shows the prototypical structure of a file with hidden fields.
function(expr)
std.foldl(
function(acc, field) acc { [field]: 'hidden %s' % std.type(expr[field]) },
std.objectFieldsAll(expr),
{},
)
+
std.foldl(
function(acc, field) acc { [field]: std.type(expr[field]) },
std.objectFields(expr),
{},
)

0 comments on commit 7b5a3b0

Please sign in to comment.