{"id":13442932,"url":"https://github.com/dhall-lang/dhall-kubernetes","last_synced_at":"2025-05-16T19:08:07.489Z","repository":{"id":41459736,"uuid":"104218323","full_name":"dhall-lang/dhall-kubernetes","owner":"dhall-lang","description":"Typecheck, template and modularize your Kubernetes definitions with Dhall","archived":false,"fork":false,"pushed_at":"2024-09-14T23:57:00.000Z","size":4846,"stargazers_count":629,"open_issues_count":14,"forks_count":62,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-05-16T19:07:35.873Z","etag":null,"topics":["dhall","dhall-lang","kubernetes","yaml"],"latest_commit_sha":null,"homepage":"","language":"Dhall","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dhall-lang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-09-20T13:21:44.000Z","updated_at":"2025-04-27T16:06:52.000Z","dependencies_parsed_at":"2022-08-19T03:20:20.572Z","dependency_job_id":"e90c4e48-b5a7-4548-a9b1-b24e22ea9805","html_url":"https://github.com/dhall-lang/dhall-kubernetes","commit_stats":{"total_commits":127,"total_committers":26,"mean_commits":4.884615384615385,"dds":0.6771653543307087,"last_synced_commit":"f305d4864949e0a946a9f3d4c59de20e524de5a8"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhall-lang%2Fdhall-kubernetes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhall-lang%2Fdhall-kubernetes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhall-lang%2Fdhall-kubernetes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhall-lang%2Fdhall-kubernetes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dhall-lang","download_url":"https://codeload.github.com/dhall-lang/dhall-kubernetes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254592395,"owners_count":22097013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dhall","dhall-lang","kubernetes","yaml"],"created_at":"2024-07-31T03:01:53.487Z","updated_at":"2025-05-16T19:08:02.480Z","avatar_url":"https://github.com/dhall-lang.png","language":"Dhall","funding_links":[],"categories":["Dhall","Libraries","Kubernetes","yaml"],"sub_categories":["Configuration"],"readme":"# `dhall-kubernetes`\n\n\u003cimg src=\"logo/dhall-kubernetes-logo.svg\" alt=\"dhall-kubernetes logo\" height=\"300px\"/\u003e\n\n`dhall-kubernetes` contains [Dhall][dhall-lang] bindings to [Kubernetes][kubernetes],\nso you can generate Kubernetes objects definitions from Dhall expressions.\nThis will let you easily typecheck, template and modularize your Kubernetes definitions.\n\n## Why do I need this\n\nOnce you build a slightly non-trivial Kubernetes setup, with many objects floating\naround, you'll encounter several issues:\n1. Writing the definitions in YAML is really verbose, and the actually important\n  things don't stand out that much\n2. Ok I have a bunch of objects that'll need to be configured together, how do I share data?\n3. I'd like to reuse an object for different environments, but I cannot make it parametric..\n4. In general, I'd really love to reuse parts of some definitions in other definitions\n5. Oh no, I typoed a key and I had to wait until I pushed to the cluster to get an error back :(\n\nThe natural tendency is to reach for a templating language + a programming language to orchestrate that + some more configuration for it...\nBut this is just really messy (been there), and we can do better.\n\nDhall solves all of this, being a programming language with builtin templating,\nall while being non-Turing complete, strongly typed and [strongly normalizing][normalization]\n(i.e.: reduces everything to a normal form, no matter how much abstraction you build),\nso saving you from the *\"oh-noes-I-made-my-config-in-code-and-now-its-too-abstract\"* nightmare.\n\nFor a Dhall Tutorial, see [the website][dhall-website], or the [readme of the project][dhall-lang],\nor the [full tutorial][dhall-tutorial].\n\n## Prerequisites\n\n**NOTE**: `dhall-kubernetes` requires at least version `1.27.0` of [the interpreter](https://github.com/dhall-lang/dhall-haskell)\n(version `11.0.0` of the language).\n\n## Quickstart - a simple Deployment\n\nLet's say we'd like to configure a Deployment exposing an `nginx` webserver.\n\nIn the following example, we:\n1. Import the Kubernetes definitions as a Dhall package (the `package.dhall` file) from the local repo.\n   In your case you will want to replace the local path with a remote one, e.g.\n   `https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/package.dhall`\n   Note: the `sha256:..` is applied to some imports so that:\n     1. the import is cached locally after the first evaluation, with great time savings (and avoiding network calls)\n     2. prevent execution if the content of the file changes. This is a security feature, and you\n        can read more [in Dhall's \"Security Guarantees\" document][security-hashes]\n   Note: instead of using the `package.dhall` from the `master` branch, you may want to use a tagged release,\n     as the contents of the `master` branch are liable to change without warning.\n2. Define the [Deployment][deployment] using the schema pattern and hardcoding the deployment details:\n\n```dhall\n-- examples/deploymentSimple.dhall\n\nlet kubernetes =\n      https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/package.dhall\n        sha256:263ee915ef545f2d771fdcd5cfa4fbb7f62772a861b5c197f998e5b71219112c\n\nlet deployment =\n      kubernetes.Deployment::{\n      , metadata = kubernetes.ObjectMeta::{ name = Some \"nginx\" }\n      , spec = Some kubernetes.DeploymentSpec::{\n        , selector = kubernetes.LabelSelector::{\n          , matchLabels = Some (toMap { name = \"nginx\" })\n          }\n        , replicas = Some 2\n        , template = kubernetes.PodTemplateSpec::{\n          , metadata = Some kubernetes.ObjectMeta::{ name = Some \"nginx\" }\n          , spec = Some kubernetes.PodSpec::{\n            , containers =\n              [ kubernetes.Container::{\n                , name = \"nginx\"\n                , image = Some \"nginx:1.15.3\"\n                , ports = Some\n                  [ kubernetes.ContainerPort::{ containerPort = 80 } ]\n                }\n              ]\n            }\n          }\n        }\n      }\n\nin  deployment\n\n```\n\nWe then run this through `dhall-to-yaml` to generate our Kubernetes definition:\n\n```bash\ndhall-to-yaml \u003c\u003c\u003c ./examples/deploymentSimple.dhall\n```\n\nAnd we get:\n```yaml\n## examples/out/deploymentSimple.yaml\n\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      name: nginx\n  template:\n    metadata:\n      name: nginx\n    spec:\n      containers:\n        - image: nginx:1.15.3\n          name: nginx\n          ports:\n            - containerPort: 80\n\n```\n\n## More modular: defining an Ingress\n\nThe above is cool, but hardcoding data is not that cool.\n\nSo in a more realistic deployment you'll probably want to define:\n- some `MyService` type that contains the config settings relevant to your deployments\n- some functions parametrized by this type, so that you can produce objects to send to k8s\n  by just applying these functions to `MyService` objects\n\nThis is useful because then you can define your `Service`s separately from the Kubernetes logic,\nand reuse those objects for configuring other things (e.g. configuring the services themselves, \ntemplating documentation, configuring Terraform deployments, you name it).\n\nAs an example of that, next we'll define an Ingress (an [Nginx Ingress][nginx-ingress] in this case),\ncontaining stuff like TLS certs and routes for every service - see the [schema][Ingress].\n\nThings to note in the following example:\n- we define the `Service` type inline in the file, but in your case you'll want to have a\n  separate `./Service.dhall` file (so you can share around the project)\n- we define functions to create the TLS definitions and the routes, so that we can `map`\n  them over the list of services.\n- we also defined the list of `services` inline, but you should instead return the\n  `mkIngress` function instead of applying it, so you can do something like\n  `dhall-to-yaml \u003c\u003c\u003c \"./mkIngress.dhall ./myServices.dhall\"`\n\n```dhall\n-- examples/ingress.dhall\n\nlet Prelude =\n      ../Prelude.dhall\n        sha256:10db3c919c25e9046833df897a8ffe2701dc390fa0893d958c3430524be5a43e\n\nlet map = Prelude.List.map\n\nlet kubernetes =\n      https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/package.dhall\n        sha256:263ee915ef545f2d771fdcd5cfa4fbb7f62772a861b5c197f998e5b71219112c\n\nlet Service = { name : Text, host : Text, version : Text }\n\nlet services = [ { name = \"foo\", host = \"foo.example.com\", version = \"2.3\" } ]\n\nlet makeTLS\n    : Service → kubernetes.IngressTLS.Type\n    = λ(service : Service) →\n        { hosts = Some [ service.host ]\n        , secretName = Some \"${service.name}-certificate\"\n        }\n\nlet makeRule\n    : Service → kubernetes.IngressRule.Type\n    = λ(service : Service) →\n        { host = Some service.host\n        , http = Some\n          { paths =\n            [ kubernetes.HTTPIngressPath::{\n              , backend = kubernetes.IngressBackend::{\n                , service = Some kubernetes.IngressServiceBackend::{\n                  , name = service.name\n                  , port = Some kubernetes.ServiceBackendPort::{\n                    , number = Some 80\n                    }\n                  }\n                }\n              , pathType = \"Exact\"\n              }\n            ]\n          }\n        }\n\nlet mkIngress\n    : List Service → kubernetes.Ingress.Type\n    = λ(inputServices : List Service) →\n        let annotations =\n              toMap\n                { `kubernetes.io/ingress.class` = \"nginx\"\n                , `kubernetes.io/ingress.allow-http` = \"false\"\n                }\n\n        let defaultService =\n              { name = \"default\"\n              , host = \"default.example.com\"\n              , version = \" 1.0\"\n              }\n\n        let ingressServices = inputServices # [ defaultService ]\n\n        let spec =\n              kubernetes.IngressSpec::{\n              , tls = Some\n                  ( map\n                      Service\n                      kubernetes.IngressTLS.Type\n                      makeTLS\n                      ingressServices\n                  )\n              , rules = Some\n                  ( map\n                      Service\n                      kubernetes.IngressRule.Type\n                      makeRule\n                      ingressServices\n                  )\n              }\n\n        in  kubernetes.Ingress::{\n            , metadata = kubernetes.ObjectMeta::{\n              , name = Some \"nginx\"\n              , annotations = Some annotations\n              }\n            , spec = Some spec\n            }\n\nin  mkIngress services\n\n```\n\nAs before we get the yaml out by running:\n\n```bash\ndhall-to-yaml \u003c\u003c\u003c ./examples/ingress.dhall\n```\n\nResult:\n```yaml\n## examples/out/ingress.yaml\n\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  annotations:\n    kubernetes.io/ingress.allow-http: 'false'\n    kubernetes.io/ingress.class: nginx\n  name: nginx\nspec:\n  rules:\n    - host: foo.example.com\n      http:\n        paths:\n          - backend:\n              service:\n                name: foo\n                port:\n                  number: 80\n            pathType: Exact\n    - host: default.example.com\n      http:\n        paths:\n          - backend:\n              service:\n                name: default\n                port:\n                  number: 80\n            pathType: Exact\n  tls:\n    - hosts:\n        - foo.example.com\n      secretName: foo-certificate\n    - hosts:\n        - default.example.com\n      secretName: default-certificate\n\n```\n\n## FAQ\n\n#### Can I generate a YAML file with many objects in it?\n\nKubernetes YAML files commonly include multiple resources as documents separated\nby `---`.  To generate a single file with a different resource type per\ndocument, you'll need to produce a `List Resource` (where `Resource` is a\nunion provided by `dhall-kubernetes` that can wrap any resource type), like\nthis:\n\n```dhall\nlet k8s = ./package.dhall\n\nin  [ k8s.Resource.Deployment k8s.Deployment::{\n        , …\n        }\n    , k8s.Resource.Service k8s.Service::{\n        , …\n        }\n    ]\n```\n\n… and then render the `List` of `Resource`s using the `--documents` flag, like\nthis:\n\n```bash\ndhall-to-yaml --documents --file ./resources.dhall\n```\n\n#### Can I use my existing charts as a starting point?\n\nYou can turn any YAML document into Dhall code with `yaml-to-dhall`.\nHowever, you need a clean YAML file without Helm's templating logic.\nYou can execute the templating logic locally with `helm template`\nor get the installed manifest with `helm get manifest`.\n\nOnce you have a clean YAML file, you can convert it to Dhall like so:\n```bash\nyaml-to-dhall --file deployment.yaml --output deployment.dhall\n```\n\nHowever, the result will not be of type `k8s.Deployment.Type` because any optional\nproperties that are missing from the YAML document will be missing from the\nDhall code as well. In other words, this will most likely fail:\n```bash\necho \"./deployment.dhall : (./package.dhall).Deployment.Type\" | dhall\n```\n\nThe missing properties need to be translated to empty optionals instead.\n`yaml-to-dhall` needs to know which fields exist in the type to achieve this.\nYou can specify the target type like so:\n```bash\nyaml-to-dhall '(./package.dhall).Deployment.Type' --file deployment.yaml --output deployment.dhall\n```\n\nNow the type-check above should succeed.\nHowever, the generated Dhall file will be very large and filled with redundant\ninformation. The schemas in `dhall-kubernetes` specify plenty of default values\nand we would like to use those to keep our own code short.\nWe can rewrite the code with schemas like so:\n```bash\ndhall rewrite-with-schemas --schemas '(./schemas.dhall)' --inplace deployment.dhall\n```\n\nNow the Dhall code should be correct and compact.\n\n## Projects Using `dhall-kubernetes`\n\n* [dhall-prometheus-operator][dhall-prometheus-operator]: Provides types and default records for [Prometheus Operators][prometheus-operator].\n\n## Development\n\nYou will need to install Nix in order to run the file-generation scripts\nprovided by this repository.  You can obtain Nix by following the instructions\nhere:\n\n* [`nixos.org` - Getting Nix](https://nixos.org/download.html)\n\n### Updating the `README.md`\n\nThe top-level `README.md` is generated from `./docs/README.md.dhall` so that\nthe examples within the `./examples` directory stay in sync with the\n`README.md`.  That means that in order to update the `README.md` you need to\nfirst edit `./docs/README.md.dhall` and then run:\n\n```bash\n$ ./scripts/generate readme\n```\n\n### Updating the examples\n\nIf you want to author new examples, add them to the `./examples` directory and\nrun:\n\n```bash\n$ ./scripts/generate examples\n```\n\n… which will freeze and type-check each example and generate the matching YAML\noutput.\n\nThe `./examples` directory is only built against one version of the Kubernetes\nAPI (the \"preferred\" version).  To change the preferred version, run:\n\n```bash\n$ echo \"${VERSION}\" \u003e ./nix/preferred.txt\n```\n\n… and then re-run the example generation script:\n\n```bash\n$ ./scripts/generate examples\n```\n\n### Adding a new Kubernetes releases\n\nTo add a new supported Kubernetes release, run:\n\n```bash\n./scripts/add-kubernetes-release \"${VERSION}\"\n```\n\n### Changing how the Kubernetes bindings are generated\n\nThe logic for generating the Dhall code doesn't reside within this\nrepository but actually resides within the\n[`dhall-openapi`](https://github.com/dhall-lang/dhall-haskell/tree/master/dhall-openapi)\nsubproject of the [`dhall-haskell`](https://github.com/dhall-lang/dhall-haskell)\nrepository.  That means that if you want to change the generated code you will\nneed to do so in two steps:\n\n* Make a pull request against the upstream `dhall-haskell` repository to\n  change the code generated by `dhall-openapi`\n\n* Make a pull request against this repository to pick up a newer reference\n  to the `dhall-haskell` repository incorporating the change to `dhall-openapi`\n\nIf you try to create a pull request to amend the generated Dhall files directly\nthen CI will reject the pull request since it verifies that the Dhall code\nstored in version control matches what `dhall-openapi` would generate from the\nKubernetes OpenAPI specification.\n\nOnce you update the `dhall-openapi` dependency you can regenerate the\nKubernetes bindings by running:\n\n```bash\n$ ./scripts/generate kubernetes\n```\n\n### Updating the `dhall-openapi` dependency\n\nThe `dhall-openapi` dependency is a subproject of the `dhall-haskell`\nrepository, so in order to upgrade `dhall-openapi` you need to update the\nreference to the `dhall-haskell` repository.\n\nIf you're not prepared to make a pull request to change the `dhall-haskell`\nproject then you can generate code for this project using a local checkout of\nthe `dhall-haskell` repository by editing the Nix code like this:\n\n```diff\n--- a/nix/nixpkgs.nix\n+++ b/nix/nixpkgs.nix\n                    json =\n                      builtins.fromJSON (builtins.readFile ./dhall-haskell.json);\n \n-                   dhall-haskell = pkgsNew.fetchFromGitHub {\n-                     owner = \"dhall-lang\";\n-                     repo = \"dhall-haskell\";\n-                     inherit (json) rev sha256 fetchSubmodules;\n-                   };\n+                   dhall-haskell = ~/path/to/dhall-haskell;\n \n                  in\n                    (import \"${dhall-haskell}/default.nix\").dhall-openapi;\n```\n\nOnce you do change the upstream `dhall-openapi` project, then you can pick up\nthe change here by runing:\n\n```bash\n$ nix-prefetch-git --fetch-submodules https://github.com/dhall-lang/dhall-haskell.git \u003e ./nix/dhall-haskell.json\n```\n\n### Generating everything\n\nIf you're not sure what files you need to regenerate then you can generate\neverything by running the `generate` script with no arguments:\n\n```bash\n$ ./scripts/generate\n```\n\n### Upgrading Nixpkgs\n\nIf you want to upgrade to a newer revision of Nixpkgs, then run:\n\n```bash\n$ nix-prefetch-git https://github.com/NixOS/nixpkgs.git \"${REVISION}\" \u003e ./nix/nixpkgs.json\n```\n\n### Tests\n\nAll tests are defined in `release.nix`. We run these tests in CI in a [Hydra\nproject][hydra-project].\n\nYou can run the tests locally with the following command:\n\n```bash\nnix build --file ./release.nix\n```\n\n[stack]: https://haskellstack.org/\n[hydra-project]: http://hydra.dhall-lang.org/project/dhall-kubernetes\n[dhall-lang]: https://github.com/dhall-lang/dhall-lang\n[dhall-website]: https://dhall-lang.org/\n[security-hashes]: https://github.com/dhall-lang/dhall-lang/wiki/Safety-guarantees#code-injection\n[typesUnion]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/typesUnion.dhall\n[kubernetes]: https://kubernetes.io/\n[normalization]: https://en.wikipedia.org/wiki/Normalization_property_(abstract_rewriting)\n[nginx-ingress]: https://github.com/kubernetes/ingress-nginx\n[dhall-tutorial]: http://hackage.haskell.org/package/dhall-1.28.0/docs/Dhall-Tutorial.html\n[deployment]: ./schemas/io.k8s.api.apps.v1.Deployment.dhall\n[Ingress]: ./schemas/io.k8s.api.extensions.v1beta1.Ingress.dhall\n[prometheus-operator]: https://github.com/coreos/prometheus-operator\n[dhall-prometheus-operator]: https://github.com/coralogix/dhall-prometheus-operator\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdhall-lang%2Fdhall-kubernetes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdhall-lang%2Fdhall-kubernetes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdhall-lang%2Fdhall-kubernetes/lists"}