Jsonnet

Reference documentation for Jsonnet libraries.

Jsonnet is a configuration language for building JSON. It provides capabilities for defining functions, pulling in external data, and other useful features to create dynamic JSON configuration files.

Format

Jsonnet looks very similar to JSON:

{
  person1: {
    name: 'Alice',
    welcome: 'Hello ' + self.name + '!',
  },
  person2: self.person1 { name: 'Bob' },
}
{
  "person1": {
    "name": "Alice",
    "welcome": "Hello Alice!"
  },
  "person2": {
    "name": "Bob",
    "welcome": "Hello Bob!"
  }
}

You can learn more about the formatting for Jsonnet on the Jsonnet website: https://jsonnet.org/.

Key Concepts

Standard Library

Jsonnet ships with a number of standard functions. You can read about them here: https://jsonnet.org/ref/stdlib.html.

Importing Jsonnet

Jsonnet can import other jsonnet files (typically with the file extension “.libsonnet”). These files can contain functions, other JSON data, and even other imports:

local func = import 'func.libsonnet';

{
  person1: {
    name: 'Alice',
    welcome: 'Hello ' + self.name + '!',
  },
  person2: func('Bob'),
}
function(name)
  {
    name: name,
    welcome: 'Hello ' + self.name + '!',
  }

Immutable

Jsonnet values are immutable. You cannot change them, you must define a new value. Here is how to handle common scenarios where you might want to change something:

local object1 = {
  hello: 'world'
}

object1 + {
  hello: 'person'
}
local something = if true then 'else' else 'other';

Native Functions

Jsonnet can be extended with custom functions via Native Functions. These functions are non-standard and are supported only within the current Jsonnet implementation. Any of these functions can be disabled using the jsonnet configuration.

get(object=null, filter, default=null, prefix='') any

This function is similar to std.get, except it uses jq filtering instead oa field, and it caches the return value for future get functions. The key is <prefix>:<field>, so prefix should be set if field may be duplicated.

Due to the way filtering works, the default value will be returned if the filter doesn’t match anything or if the found value is null. If you want this function to return null, keep the default set to null.

local get() = std.native('get')(object=null, field, default=null, prefix='');

get(object={
  a: 'b',
}, field='a', default='a', prefix='b')

getArch() string

This function returns the current architecture based on GOARCH.

local getArch() = std.native('getArch')();

getArch()

getConfig() object

This function returns the current configuration for the application as a Jsonnet object.

local getConfig() = std.native('getConfig')();

config()

getCmd(command, fallback=null, cache=false) string

This function executes a command as the current user and returns the stdout of it. Any errors will cause rendering to fail, unless a fallback is provided.

This function is disabled by default. Set jsonnet.disableGetCmd to false to enable it.

local getCmd(command, fallback=null) = std.native('getCmd')(command, fallback);

getCmd('ls -al mydir/', 'mydir')

getEnv(key, fallback=null, cache=false) string

This function returns the string value of the environment variable. If the environment variable is not defined or does not exist, it returns an empty string or a fallback value if provided.

local getEnv(key) = std.native('getEnv')(key);

getEnv('PWD')

getFile(path, fallback=null, cache=false) string

This function returns the string value of a path (local or http/https via GET). The results are cached for repeat lookups within the current render cycle. For HTTP or HTTPS paths, you can set headers for your request using a #, the header as a k:v, and deliminiated by a newline \r\n, e.g. getEnv('https://example.com/api#myHeader:myValue\r\nmyOtherHeader:myOtherValue'.

In addition to adding headers, a few “special headers” can be set that will change the underlying client transport:

  • clientSkipVerify Disable TLS verification (: is optional).

If the path is unreachable, an error will be thrown and rendering will halt. You can optionally provide a fallback value to prevent this, this value will be returned instead on failure.

Using this function with URLs is disabled by default. Set jsonnet.disableGetFileHTTP to false to enable it.

local getFile(path, fallback=null) = std.native('getFile')(path, fallback);

getFile('~/.bashrc', 'fallback')

getOS () string

This function returns the current architecture based on GOOS.

local getOS() = std.native('getOS')();

getOS()

getPath() string

This function returns the string value of the full directory path containing the target jsonnet file. This value may be an empty string if the exact value cannot be determined.

local getPath() = std.native('getPath')();

getPath()

getRecord(type, name, fallback=null) []string

This function returns a list of sorted string values of a DNS record with type and name. The results are cached for repeat lookups within the current render cycle. The currently supported values for type are a, aaaa, cname, and txt.

This function is disabled by default. Set jsonnet.disableGetRecord to false to enable it.

local getRecord(type, name, fallback=null) = std.native('getRecord')(type, name, fallback);

getRecord('a', 'candid.dev', 'fallback')

randStr(length) string

This function returns a random string of length length. Will panic if it cannot generate a cryptographically secure value.

local randStr(length) = std.native('randStr')(length);

randStr(10)

regexMatch(regex, string) bool

This function returns a bool if string matches regex. Will throw an error if regex is invalid/doesn’t compile.

local regexMatch(regex, string) = std.native('regexMatch')(regex, string);

regexMatch('^hello world$', 'hello world')

render(string) object

This function renders string using Jsonnet.

std.native('render')(|||
  local regexMatch(regex, string) = std.native('regexMatch')(regex, string);

  regexMatch('^hello world$', 'hello world')
|||)

Best Practices

Always Wrap Your Ifs

ifs can be wrapped with parenthesis in Jsonnet or not wrapped. By keeping ifs always wrapped, it makes it easier to understand where they end:

local noWrap = if 'this' then 'that' else 'that' + 'yep'
local withWrap = (if 'this' then 'that' else 'that') + 'yep'

Formatting

“Proper” Jsonnet format/linting recommends:

  • Single quotes for strings
  • Two spaces, no tabs