10 ms·
Jsonnet – The Data Templating Language
- ynohat 4y agoSome of our customers use Jsonnet to generate JSON-based configurations, including Terraform and even Postman collections. They choose to use it because: - it allows them to keep things DRY when replicating a configuration artifact across environments - it is accessible for folks who are not as comfortable with program control flow and other programming constructs. They can start by copying and pasting JSON. - it provides more advanced control flow and logic features for those who need them - it provides features for template parameter validation - it is not that hard to learn Some things could be improved for sure. Better IDE support, error messages which are not the best. Maybe also a more confident community. Overall, Jsonnet does seem to hit a pragmatic sweet spot for a bunch of use cases, regardless of the merits of other approaches mentioned in this great discussion!
- jjgreen 4y agoRuby's hashes have escaped!
- aliasxneo 4y agoWe use Jsonnet with Tanka (by Grafana) for managing our Kubernetes manifests. It does take a bit to pick it up properly. The language itself is straight-forward (coming from Nix it was easy), however, learning how to use ksonnet is the challenging part. I'm still tinkering with how to organize things. I don't think I've fully grasped the purpose of mixins yet.
- capableweb 4y agoIt always bothered me that the "tutorial" (https://jsonnet.org/learning/tutorial.html https://jsonnet.org/learning/tutorial.html) is using mixing cocktails as examples, rather than actual networks/infrastructure which is a much more likely use cases for using jsonnet (or, I'm much less into mixing than my fellow developers/operators).
- TechBro8615 4y agoIf you didn't want a cocktail before working with jsonnet, you will want one after.
- drewdrewdrewbie 4y agoI'll take a Moscow Mule please.
- Leo_Verto 4y agoThe excellent Jsonnet training course [1] by u/Duologic somewhat fills this niche by having you play around with k8s resources and using external packages. [1]: https://jsonnet-libs.github.io/jsonnet-training-course/ https://jsonnet-libs.github.io/jsonnet-training-course/
- chewbacha 4y agoNothing I dread more than needing to edit the tangled web of jsonnet files that constitute our grafana dashboards. A jumble of spaghetti with hard to find documentation.
- parcec 4y agoAren’t you supposed to edit grafana dashboards via the UI?
- TechBro8615 4y agoI'm not the person you're replying to, but in our company we edit them in the UI but check the results into source control (so that we can re-deploy the dashboards consistently). I imagine for more complicated dashboards you could get stuck editing them manually, and it's probably the kind of thing where once you do it one time, you're stuck doing it for the rest of time.
- sleepybrett 4y agoThere are a few very specific situations in which I might consider generating a dashboard without the ui, but even then i wouldn't use jsonnet.
- return_to_monke 4y agoThe nix language, albeit for a package manager, is really similar. You get json-like "attrsets" and can have variables and functions. Then of course you can export it to different formats as well. https://nixos.org https://nixos.org There is also the Dhall and Nickel languages providing features alike
- rssoconnor 4y agoMy discussions with David Cunningham resulting in David producing jsonnet and me creating what is now known as overlays in Nix.
- baq 4y agoSee also https://dhall-lang.org/ https://dhall-lang.org/ > Dhall is a programmable configuration language that you can think of as: JSON + functions + types + imports
- jordemort 4y agoThere's also CUE: https://cuelang.org/ https://cuelang.org/
- IshKebab 4y agoCue is a little different I think. It doesn't attempt to make writing configs less tedious through the use of functions. It is more about schemas and reliably merging documents as far as I can tell. But weirdly given its focus on schemas I couldn't find any way for a document to link to a schema in-band!
- joshuamorton 4y agoIt makes writing configs less tedious using templates. Not really sure what you mean by "documents" though.
- evancordell 4y ago> But weirdly given its focus on schemas I couldn't find any way for a document to link to a schema in-band! In cue there's no real distinction between "schemas" and "documents". If you say: value: string value: "abc" then cue "unifies" the definitions for `value`, sees that "abc" is a string, and therefore `value` is valid.
- bdsa 4y agoThe Prometheus Operator and Thanos projects only officially provide jsonnet libraries and example implementations, rather than Helm charts (which are provided by the community, but I detest Helm charts anyway) so I've picked up Jsonnet in the last few weeks to build our new monitoring stack and I'm really impressed. It's a massive learning curve but it has become really powerful and flexible. I started trying to use Tanka but found the mental model a bit strange and struggled passing parameters into environments, so I tried Kapitan instead and that has been much more productive.
- brancz 4y agoOriginal creator of both of those here. I recommend just generating things out straight with the `jsonnet -m` command, and then doing `kubectl diff` on a PR and apply on merge. Scaled huge amount of infra this way and it’s easily understandable and extensible. To clean up the diff a bit I recommend using: https://github.com/sh0rez/kubectl-neat-diff https://github.com/sh0rez/kubectl-neat-diff Hope it’s helpful!
- bdsa 4y agoI have to admit I'm a little starstruck to see your username replying to me. Thanks for all the effort and expertise you bring to bear on observability! We're using exactly that diff-on-PR/apply-on-merge approach, checking the generated manifests in for easier reviews, but I found just using jsonnet -m made it difficult to DRY things out (e.g. set some URL values differently per environment) so I'd be curious to hear how you approach that? Would you wrap the customised kube-thanos/prometheus-operator "calls" in another library?
- deleted 4y ago[deleted]
- graypegg 4y agoHonest question, why not just use… JavaScript? I get that this is scoped to only templating json, but the syntax is already diverging pretty heavily from actual JavaScript, while also trying to pretend to be similar? You’re using operators and a self/this keyword and things, but I can only assume they obey different rules. Could just be config.js. (Or just generate JSON in your app programmatically, it’s a pretty simple well-defined syntax and there’s already a hash-to-json library for whatever platform you’re using)
- gjulianm 4y agoI honestly... don't know. I have been using Jsonnet to programatically generate Grafana dashboards, because AFAIK the only official library to generate them is written in Jsonnet [1]. In my experience, it works, but it doesn't really give me any distinct advantage and instead it gives me some headaches. Maybe for things that look almost like JSON it'd be helpful, but the moment you start dealing with more complex generations you start finding lack of typing, lack of IDE support, lack of easy debugging, etc, fairly problematic. For example, something I really dislike is that due to how the expressions are evaluated, the only way to add debug/trace statements is to use them to "transform" a value you're going to use, if you don't use the result of the trace in the final output, the trace does not appear. Also, I really dislike the error messages. Again, due to the lazy evaluation design, when you mess up something the error message might appear deep in some unrelated call stack and with a jarring lack of context. Debugging it is a real pain. 1: https://github.com/grafana/grafonnet-lib https://github.com/grafana/grafonnet-lib
- ithkuil 4y ago> debugging is a real pain yes! As always, it's mostly a tooling issue. Surely, some aspects of the language like lazy evaluation, make it even harder, but fundamentally there is no reason we cannot significantly improve on the overall experience. And this is not just about debugging when things go wrong. It's also hard to navigate the codebase and understand where are the templates that you care about. Things can happen at multiple levels (that's the feature!) and so it can be quite hard to figure out which files you need to touch if you want to change something in the output. I'm working on a tool to answer that question: "if I wanted to change this field in the generated out put, what are the places in the input files that contribute to produce this value" $ ursonnet testdata/child.jsonnet '$.deployment.spec.template.spec.containers[0].resources.limits.cpu' testdata/common.libsonnet:27 testdata/base.jsonnet:5 testdata/common.libsonnet:23 testdata/common.libsonnet:22 testdata/config.libsonnet:5 The tool lives in https://github.com/mkmik/ursonnet https://github.com/mkmik/ursonnet . I got the basics working but it doesn't work on my larger codebases due to some bug I didn't have yet time hunting down. Having some interest/feedback/help from the community would help making this a reality.
- ithkuil 4y agoshameless plug: https://github.com/kubecfg/kubecfg https://github.com/kubecfg/kubecfg
- tnisonoff 4y agoThanks for kubecfg! Happy user :)
- aliasxneo 4y agoHow does this differ from Tanka? At first glance, they seem very similar.
- q3k 4y agokubecfg is much less opinionated. It will just take a jsonnet file, no need to follow any set directory format or hierarchy of environments.
- ithkuil 4y agokubecfg does add some features, like https:// https:// imports, oci:// imports (oci bundles in OCI registries, transitively bundling all imported files with jsonnet-deps). But yes, I strive to keep the "one file, one target, import whatever you need but explicitly" as much as possible. I'm pouring some more time into the project and trying to implement some ideas I had for a long time but never managed to get them out. For example "Flags From Files" (https://github.com/kubecfg/kubecfg/blob/flagspec/docs/rfcs/rfc02.md https://github.com/kubecfg/kubecfg/blob/flagspec/docs/rfcs/r...) or "Caching + optional vendoring of immutable external deps".
- samgranieri 4y agoMy company uses this, but not for kubernetes. It's a nifty product
- jonfw 4y agoI love go templates for this sort of thing, but obviously only works if you’re using golang
- mdaniel 4y agoThe discoverability of golang templates is terrible, IMHO, since it's missing a "dir(locals())" equivalent and every execution environment gets to make its own rules about what pipelines/functions are exposed Look at helm as an example: https://helm.sh/docs/chart_template_guide/function_list/ https://helm.sh/docs/chart_template_guide/function_list/ is some of them, https://helm.sh/docs/chart_template_guide/accessing_files/#path-helpers https://helm.sh/docs/chart_template_guide/accessing_files/#p... are some others, but they also glued in some version of https://masterminds.github.io/sprig/ https://masterminds.github.io/sprig/ So, short of (a) knowing that's the case (b) having 3+ bookmarks in your favorite browser to refer to those reference pages, how would anyone know what pipelines are available? Separately, I dooooo nooooooot understand why every joker has to invent their own new thing when we have like 50 or so templating languages already. Golang may be an outlier in that competition due to the Google Promotion Packet Effect(tm) but how they came up with `{{ range }}{{ end }}` as sane syntax is some true facepalm, to say nothing of the same landmine that ansible stepped on by not switching jinja2's default characters: `{{` is not _yaml safe_
- eviks 4y agowha's the best of the 50 in your opinion?
- verdverm 4y agoown-project plug: https://docs.hofstadter.io https://docs.hofstadter.io
- jonfw 4y agoHelm does a poor job of documenting their functions, and when you use helm, you're not really using go templates, you're using helm's wrapper around go templates. If you use go templates directly- it's very obvious what functions are available, there's the default ones from the Go template package, and there's whatever you pass explicitly. The go template package is very small and well documented. What I love about Go templates, is that I can pass any function I want, using good old Go code (meaning I can use SDKs and whatever else). This is really powerful- we're able to have functions that go out to APIs and return data and it's very easy to manage
- deleted 4y ago[deleted]
- kazinator 4y agoself should refer to the immediately containing object, the outermost object should be called top (or whatever). Or perhaps found relatively with self.out.out.out... Imagine having to take a nested configuration fragment that refers to its own pieces via the absolute self, and trying to plant it in some other configuration, at a different nesting level.
- q3k 4y ago> Or perhaps found relatively with self.out.out.out Jsonnet’s internal predecessor /counterpart/inspiration (Google BCL/GCL) has something like this and not including it in Jsonnet is a feature :).
- ithkuil 4y agoyep, mixing lexical and dynamic scope was the hell indeed. A convention I use with jsonnet is to use locals to "anchor" some important objects in the lexical scope. My rule is to name the locals with exactly the same name as the field whose object they refer to. Example: { deployment: { local deployment = self, spec+: { replicas: 3, template+: { spec+: { local spec = self, containers: [ { name: 'foo', env: [ { name: 'REP', value: std.toString(deployment.spec.replicas) }, { name: 'SA', value: spec.serviceAccountName }, ], }, ], serviceAccountName: 'foo', } }, }, }, } Adding these locals gives the right amount of friction to avoid using this all the time, while at the same time having a predictable naming for the locals. The jsonnet LSP tool can easily resolve that local since it's only a lexical thing
- CSDude 4y agoWe've been using Jsonnet to generate Pods for our complex DAGs at Opsgenie, wrote a similar one using Tekton: https://mustafaakin.dev/posts/2020-04-26-using-jsonnet-to-generate-dynamic-tekton-pipelines-in-kubernetes/ https://mustafaakin.dev/posts/2020-04-26-using-jsonnet-to-ge... But if I was doing it again now, I would just use https://cuelang.org/ https://cuelang.org/ or https://dagger.io https://dagger.io Jsonnet is really hard to debug.
- ithkuil 4y ago> Jsonnet is really hard to debug. yes! As always, it's mostly a tooling issue. Surely, some aspects of the language like lazy evaluation, make it even harder, but fundamentally there is no reason we cannot significantly improve on the overall experience. And this is not just about debugging when things go wrong. It's also hard to navigate the codebase and understand where are the templates that you care about. Things can happen at multiple levels (that's the feature!) and so it can be quite hard to figure out which files you need to touch if you want to change something in the output. I'm working on a tool to answer that question: "if I wanted to change this field in the generated out put, what are the places in the input files that contribute to produce this value" $ ursonnet testdata/child.jsonnet '$.deployment.spec.template.spec.containers[0].resources.limits.cpu' testdata/common.libsonnet:27 testdata/base.jsonnet:5 testdata/common.libsonnet:23 testdata/common.libsonnet:22 testdata/config.libsonnet:5 The tool lives in https://github.com/mkmik/ursonnet https://github.com/mkmik/ursonnet . I got the basics working but it doesn't work on my larger codebases due to some bug I didn't have yet time hunting down. Having some interest/feedback/help from the community would help making this a reality.
- sleepybrett 4y agoAgreed. I dislike that it's json to transform json, it becomes a rats nest. I had similar experiences with xslt back in the day.
- spion 4y agoIts not JSON though, its more of a stripped down purely functional language - functions are quite central.
- ackfoobar 4y agoThe first time I have heard of Jsonnet is that the Scala implementation is faster than Google's. I wonder if that is still the case.
- jorpal 4y agoYes the go version takes way longer materialize our configs compared at my work compared to sJsonnet (from Databricks). The first time you switch it adds (or removes? I forget) a bunch of newlines at the end of files which is annoying to handle in git but otherwise it’s faster and the same.
- ackfoobar 4y agoGood to hear. Scala is amazing when used pragmatically.
- rstrg 4y agoTemplate languages are odd. Just use normal programming language to generate whatewer you want.
- IshKebab 4y agoThe issue with normal programming languages is that they are rarely properly sandboxed and deterministic. Jsonnet can't wipe your hard disk or steal your passwords. Something like Starlark is probably a good option.
- redleader55 4y agoConfiguration as code, but in a quirky language - how about no? I'm sure someone uses this, someone created this, and so it might seem like it's useful, but we have enough clownishness out there with yaml, json combined with js, json with eval and so on. Create a framework in a normal language, but one which is very limited by design with regard to what is able to access - Skylark/Starlark is a good example[0], "limited Python" and use that. The rest works the same way, take an input in this language/framework, generate outputs. [0] - https://bazel.build/rules/language https://bazel.build/rules/language
- dilyevsky 4y agoTried it[0], worked reasonably well. Be prepared for strong opposition from traditional “devops” folks (really just rebranded sysadmins) and boneheaded management “who don’t mind yaml” and will drag everyone down. [0] - https://github.com/cruise-automation/isopod https://github.com/cruise-automation/isopod
- GlickWick 4y agoMy employer (who I don’t speak for) used this for a long time. Im not normally particularly opinionated about tech choice, but I can very strongly say do not use jsonnet unless you enjoy adding an esoteric unsupported language to your job configs for no reason.
- spion 4y agoWhat would you use instead?
- GlickWick 4y agoAssuming you need something beyond basic JSON, a well-understood scripting language like JS or Python. Bazel if you want something more build-focused.
- techhazard 4y agoNot OP, but I can highly recommend just using the programming language you already use to make key-value map to and then converting that to JSON. That way you only have 1 syntax to worry about. In my case that’s Python (using dictionaries and json.dumps) You could also try for a templating framework (like jinja2) but then you have 3 syntaxes colliding: the programming language you call the templating engine with (e.g. Python for jinja2) the templating language itself (e.g. jinja2) and then JSON as well.
- nikolay 4y agoJsonnet had a huge opportunity, but due to the extremely slow and heavily opinionated lead, this project literally exists only at Grafana now!
- deleted 4y ago[deleted]
- gillh 4y agoWe use Jsonnet extensively in Aperture project for generating control policies. - Policy spec is expressed in protobuf format - GRPC gateway plugin is used to generate Swagger (OpenAPI v2) spec from proto files - Jsonnet bindings are generated from Swagger spec. - Blueprints are implemented using Jsonnet bindings. The users generate policies from blueprints by providing configuration in yaml (using aperturectl CLI) or via jsonnet mixin. Relevant links: - Aperture Blueprints (Jsonnet): https://github.com/fluxninja/aperture/tree/main/blueprints https://github.com/fluxninja/aperture/tree/main/blueprints - Generated doc example: https://docs.fluxninja.com/development/reference/policies/bundled-blueprints/policies/latency-aimd-concurrency-limiting https://docs.fluxninja.com/development/reference/policies/bu... - CLI for generating policies from blueprints: https://docs.fluxninja.com/development/reference/aperturectl/blueprints/ https://docs.fluxninja.com/development/reference/aperturectl... - Policy spec: https://docs.fluxninja.com/development/reference/policies/spec https://docs.fluxninja.com/development/reference/policies/sp...
- Duologic 4y agoI'm interested in the code that processes the swagger/openapi into jsonnet, can you link to that?
- gillh 4y agoIt’s customized to our policy spec. But you can learn from this and adapt it to your spec. https://github.com/fluxninja/aperture/blob/main/scripts/jsonnet-lib-gen.py https://github.com/fluxninja/aperture/blob/main/scripts/json...
- the-alchemist 4y ago"Any sufficiently complicated data templating language contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of a Lisp." Change my mind. :-P
- prashantv 4y agoI evaluated Jsonnet and Starlark for configuration generation at a previous job, and found Starlark more practical -- the similarity to Python made it much simpler for everyone to pick up and use.