Ir al contenido

Jsonnet Cheat Sheet

Overview

Jsonnet is a data templating language that extends JSON with variables, conditionals, functions, imports, and object-oriented features. It generates JSON or YAML output, making it ideal for producing complex configuration files for Kubernetes, Terraform, CI/CD pipelines, and other infrastructure tools. Jsonnet code is purely functional with no side effects.

Jsonnet comes in two implementations: the C++ jsonnet binary and the Go-based go-jsonnet. Both produce identical output. The language has been widely adopted in the Kubernetes ecosystem through projects like ksonnet, Tanka (by Grafana), and the jsonnet-bundler package manager.

Installation

# macOS
brew install jsonnet

# Go implementation
go install github.com/google/go-jsonnet/cmd/jsonnet@latest
go install github.com/google/go-jsonnet/cmd/jsonnetfmt@latest

# Ubuntu/Debian
sudo apt install jsonnet

# Jsonnet-bundler (package manager)
go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest

# Verify
jsonnet --version

Core Commands

CommandDescription
jsonnet file.jsonnetEvaluate and output JSON
jsonnet -y file.jsonnetOutput as YAML stream
jsonnet -m outdir/ file.jsonnetMulti-file output
jsonnet -S file.jsonnetOutput as raw string
jsonnet -e 'expr'Evaluate expression
jsonnetfmt file.jsonnetFormat Jsonnet code
jsonnetfmt -i file.jsonnetFormat in place

Language Basics

Values and Variables

// config.jsonnet
local app_name = 'my-app';
local version = '2.0.0';
local port = 8080;
local debug = false;

{
  name: app_name,
  version: version,
  port: port,
  debug: debug,
  url: 'http://localhost:%d' % port,
  fullName: '%s-v%s' % [app_name, version],
}

Functions

// Helper function
local container(name, image, port) = {
  name: name,
  image: image,
  ports: [{ containerPort: port }],
};

// With default parameters
local service(name, port=80, type='ClusterIP') = {
  apiVersion: 'v1',
  kind: 'Service',
  metadata: { name: name },
  spec: {
    type: type,
    ports: [{ port: port, targetPort: port }],
    selector: { app: name },
  },
};

{
  container: container('web', 'nginx:latest', 80),
  service: service('web'),
  serviceNodePort: service('api', 8080, 'NodePort'),
}

Conditionals and Comprehensions

local env = 'production';

{
  replicas: if env == 'production' then 3 else 1,
  debug: env != 'production',

  // Array comprehension
  ports: [p for p in [80, 443, 8080] if p != 443],

  // Object comprehension
  labels: {
    ['app/%s' % k]: v
    for k in ['name', 'version', 'team']
    for v in [self.name, self.version, 'platform']
  },

  name: 'my-app',
  version: '1.0',
}

Object Inheritance

local base = {
  apiVersion: 'apps/v1',
  kind: 'Deployment',
  metadata: {
    labels: { managed: 'jsonnet' },
  },
  spec: {
    replicas: 1,
    template: {
      spec: {
        containers: [],
      },
    },
  },
};

// Merge with +
local frontend = base {
  metadata+: { name: 'frontend' },
  spec+: {
    replicas: 2,
    template+: {
      spec+: {
        containers: [{
          name: 'frontend',
          image: 'frontend:v1',
          ports: [{ containerPort: 3000 }],
        }],
      },
    },
  },
};

frontend

Configuration

Imports and Libraries

// lib/k8s.libsonnet
{
  deployment(name, image, port, replicas=1): {
    apiVersion: 'apps/v1',
    kind: 'Deployment',
    metadata: {
      name: name,
      labels: { app: name },
    },
    spec: {
      replicas: replicas,
      selector: { matchLabels: { app: name } },
      template: {
        metadata: { labels: { app: name } },
        spec: {
          containers: [{
            name: name,
            image: image,
            ports: [{ containerPort: port }],
          }],
        },
      },
    },
  },
}
// main.jsonnet
local k8s = import 'lib/k8s.libsonnet';
local config = import 'config.json';  // Import JSON directly

{
  frontend: k8s.deployment('frontend', 'frontend:v2', 3000, 2),
  backend: k8s.deployment('backend', config.backendImage, 8080, 3),
}

External Variables

# Pass external variables
jsonnet --ext-str env=production --ext-code replicas=3 config.jsonnet

# Top-level arguments
jsonnet --tla-str env=production config.jsonnet
// Using external variables
local env = std.extVar('env');
local replicas = std.extVar('replicas');

// Using top-level arguments
function(env='dev', replicas=1) {
  environment: env,
  spec: { replicas: replicas },
}

Standard Library Highlights

{
  // String operations
  upper: std.asciiUpper('hello'),
  split: std.split('a,b,c', ','),
  format: std.format('Hello %s, you are %d', ['world', 42]),

  // Array operations
  sorted: std.sort([3, 1, 2]),
  unique: std.uniq(std.sort([1, 2, 2, 3])),
  filtered: std.filter(function(x) x > 2, [1, 2, 3, 4]),
  mapped: std.map(function(x) x * 2, [1, 2, 3]),

  // Object operations
  keys: std.objectFields({ a: 1, b: 2 }),
  merged: std.mergePatch({ a: 1 }, { b: 2 }),

  // Type checks
  isStr: std.isString('hello'),
  isNum: std.isNumber(42),
}

Advanced Usage

Multi-File Output

// multi.jsonnet
local apps = ['frontend', 'backend', 'worker'];

{
  [app + '-deployment.json']: {
    kind: 'Deployment',
    metadata: { name: app },
  }
  for app in apps
}
jsonnet -m output/ multi.jsonnet
# Creates: output/frontend-deployment.json, output/backend-deployment.json, etc.

Jsonnet Bundler (Package Manager)

# Initialize
jb init

# Install a package
jb install github.com/grafana/jsonnet-libs/ksonnet-util

# Install specific version
jb install github.com/grafana/grafonnet-lib/grafonnet@master

# Update dependencies
jb update
// Use installed package
local ksonnet = import 'ksonnet-util/kausal.libsonnet';
local k = ksonnet { _config+:: { namespace: 'myapp' } };

Grafana Tanka Integration

# Initialize Tanka project
tk init

# Apply Jsonnet to cluster
tk apply environments/default

# Diff changes
tk diff environments/default

# Show generated YAML
tk show environments/default

Troubleshooting

IssueSolution
RUNTIME ERROR: max stack frames exceededCheck for infinite recursion in self-references
Import not foundCheck JSONNET_PATH or use -J flag for lib paths
Unexpected field mergeUse +: for deep merge, : for override
Output is not valid JSONCheck for trailing commas or syntax errors
Slow evaluationReduce deep recursion; cache imported libraries
Hidden fields in outputUse :: for hidden; ::: for visible override
# Format code
jsonnetfmt -i *.jsonnet

# Add library search path
jsonnet -J vendor/ -J lib/ main.jsonnet

# Evaluate single expression
jsonnet -e 'std.sort([3,1,2])'

# YAML output
jsonnet -y main.jsonnet

# Verbose error output
jsonnet --max-trace 20 main.jsonnet