Search code examples
jsonnet

How to call parameterized Jsonnet from bash?


I can't understand how to best to parameterize a Jsonnet file so that I may call the same file from bash and from another Jsonnet file.

Assuming I have a simple template called template.jsonnet:

{
  // Required arguments
  name:: error "'name' must be specified",
  port:: error "'port' must be specified",

  ...,
}

I can readily incorporate this into another Jsonnet file and provide its required parameter values with:

{
  local template = = import "./template.jsonnet";

  template + {
    name:: "service",
    port:: 8080,
}

I'm struggling to determine the intended way that I could call template.jsonnet from bash to achieve the same result.

I can use --ext-str but this appears to require std.extVar(x)

A GitHub issue suggests that --tla-code may be an alternative to std.extVar() but I don't understand how to use it for my needs.

A follow-up question is: how do do this for a parameter this is an array:

{
  local template = = import "./template.jsonnet";

  template + {
    name:: "service",
    port:: [8080,8081,8082],
}

Solution

  • The most direct way is to use some inline jsonnet:

    jsonnet -e '(import "template.jsonnet") + { name: "service", port: 8080 }'
    

    For easier parametrization, you can use extVars or top-level-arguments (TLAs).

    jsonnet -e 'function(name, port) (import "template.jsonnet") + { name: name, port: port }' --tla-str name="blah" --tla-code port='[8080, 8081]'
    

    or

    jsonnet -e '(import "template.jsonnet") + { name: std.extVar("name"), port: std.extVar("port") }' --ext-str name="blah" --ext-code port='[8080, 8081]'
    

    A better way is to make template.jsonnet a function and use --tla-code/ --tla-str:

      function(name, port) {
        name:: name,
        port:: port
        // Sidenote: the fields are hidden here, because they use ::,
        // use : to make them appear when the object is manifested.
        // Sidenote 2: You can provide default argument values. 
        // For example function(name, port=8080) makes port optional.
      }
    

    Then in another jsonnet file you can use it as follows:

    local template = import "./template.jsonnet";
    {
    
      filled_template: template(
        name="service",
        port=8080 // or port=[8080,8081,8082]
      )
    }
    

    And you can use the template from shell as follows:

    jsonnet --tla-code name='"some name"' --tla-code port=8080 template.jsonnet
    

    Note how quotation marks are necessary for name (without outer ' they would be interpreted by shell). That's because you can pass any jsonnet code that evaluates to any type in tla-code.

    If you want to pass a string verbatim you can use --tla-str:

    jsonnet --tla-str name="some name" --tla-code port=8080 template.jsonnet
    

    On the other hand you can pass an array (or an object, or any jsonnet code) to --tla-code:

    jsonnet --tla-code name='"some name"' --tla-code port='[8080, 8081, 8082]' template.jsonnet
    

    Alternatively, if you don't want to change your template.jsonnet you can use a wrapper file to provide the interface that I described:

    template_func.jsonnet:

    local template = import "./template.jsonnet";
    function(name, port) template + {
      name: name,
      port: port
    }