From bbfec3d8548b605f1c9628f34029ab4a7d928839 Mon Sep 17 00:00:00 2001 From: Ari Becker Date: Sat, 7 Dec 2019 23:41:54 +0200 Subject: [PATCH] Add package.dhall (#95) * Add a `package.dhall` file, which re-exports `schemas.dhall` and adds `IntOrString`. The expected hash of `package.dhall` is currently: sha256:3ea8628b704704de295261dfc7626c15247c589c10a266f970cade262543fdda * Update documentation and examples to clarify that expected usage is through the `package.dhall` file. * README.md cleanup: Update tutorial reference URL to the latest `1.28` release of `dhall-haskell`, and change code language hints from `haskell` to `dhall`. --- README.md | 55 +++--- docs/README.md.dhall | 30 ++-- examples/aws-iam-authenticator-chart.dhall | 191 +++++++++++---------- examples/deployment.dhall | 9 +- examples/deploymentSimple.dhall | 2 +- examples/ingress.dhall | 23 +-- examples/service.dhall | 9 +- package.dhall | 5 + 8 files changed, 167 insertions(+), 157 deletions(-) create mode 100644 package.dhall diff --git a/README.md b/README.md index 29f7cf4c..15b91767 100644 --- a/README.md +++ b/README.md @@ -38,26 +38,22 @@ or the [full tutorial][dhall-tutorial]. Let's say we'd like to configure a Deployment exposing an `nginx` webserver. In the following example, we: -1. Import the Kubernetes definitions as Dhall Types (the `types.dhall` file) from the local repo. +1. Import the Kubernetes definitions as a Dhall package (the `package.dhall` file) from the local repo. In your case you will want to replace the local path with a remote one, e.g. - `https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/0a4f0b87fbdd4b679853c81ff804bde7b44336cf/types.dhall`. + `https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/package.dhall` Note: the `sha256:..` is applied to some imports so that: 1. the import is cached locally after the first evaluation, with great time savings (and avoiding network calls) 2. prevent execution if the content of the file changes. This is a security feature, and you can read more [in Dhall's "Security Guarantees" document][security-hashes] -2. Import the defaults for the above types. - Since _most_ of the fields in any definition are optional, for better ergonomics while - coding Dhall we have generated default values for all types, so we can just use the `//` - operator (right-biased record merge) to add our data to the default configuration. - The pattern looks something like this: `defaultValue // { ourDataHere = ..}` -3. Define the [Deployment][deployment] using this pattern (see the default [here][default-deployment]) - and hardcoding the deployment details: + Note: instead of using the `package.dhall` from the `master` branch, you may want to use a tagged release, + as the contents of the `master` branch are liable to change without warning. +2. Define the [Deployment][deployment] using the schema pattern and hardcoding the deployment details: -```haskell +```dhall -- examples/deploymentSimple.dhall let kubernetes = - ../schemas.dhall sha256:9704063d1e2d17050cb18afae199a24f4cd1264e6c8e696ca94781309e213785 + ../package.dhall sha256:3ea8628b704704de295261dfc7626c15247c589c10a266f970cade262543fdda let deployment = kubernetes.Deployment::{ @@ -134,7 +130,7 @@ and reuse those objects for configuring other things (e.g. configuring the servi templating documentation, configuring Terraform deployments, you name it). As an example of that, next we'll define an Ingress (an [Nginx Ingress][nginx-ingress] in this case), -containing stuff like TLS certs and routes for every service - see the [type][Ingress] and [default][Ingress-default] for it. +containing stuff like TLS certs and routes for every service - see the [schema][Ingress]. Things to note in the following example: - we define the `Service` type inline in the file, but in your case you'll want to have a @@ -145,7 +141,7 @@ Things to note in the following example: `mkIngress` function instead of applying it, so you can do something like `dhall-to-yaml --omitEmpty <<< "./mkIngress.dhall ./myServices.dhall"` -```haskell +```dhall -- examples/ingress.dhall let Prelude = ../Prelude.dhall @@ -154,25 +150,22 @@ let map = Prelude.List.map let kv = Prelude.JSON.keyText -let types = - ../types.dhall sha256:e48e21b807dad217a6c3e631fcaf3e950062310bfb4a8bbcecc330eb7b2f60ed - let kubernetes = - ../schemas.dhall sha256:9704063d1e2d17050cb18afae199a24f4cd1264e6c8e696ca94781309e213785 + ../package.dhall sha256:3ea8628b704704de295261dfc7626c15247c589c10a266f970cade262543fdda let Service = { name : Text, host : Text, version : Text } let services = [ { name = "foo", host = "foo.example.com", version = "2.3" } ] let makeTLS - : Service → types.IngressTLS + : Service → kubernetes.IngressTLS.Type = λ(service : Service) → { hosts = [ service.host ] , secretName = Some "${service.name}-certificate" } let makeRule - : Service → types.IngressRule + : Service → kubernetes.IngressRule.Type = λ(service : Service) → { host = Some service.host , http = @@ -180,7 +173,7 @@ let makeRule { paths = [ { backend = { serviceName = service.name - , servicePort = types.IntOrString.Int 80 + , servicePort = kubernetes.IntOrString.Int 80 } , path = None Text } @@ -189,7 +182,7 @@ let makeRule } let mkIngress - : List Service → types.Ingress + : List Service → kubernetes.Ingress.Type = λ(inputServices : List Service) → let annotations = [ kv "kubernetes.io/ingress.class" "nginx" @@ -206,8 +199,14 @@ let mkIngress let spec = kubernetes.IngressSpec::{ - , tls = map Service types.IngressTLS makeTLS ingressServices - , rules = map Service types.IngressRule makeRule ingressServices + , tls = + map Service kubernetes.IngressTLS.Type makeTLS ingressServices + , rules = + map + Service + kubernetes.IngressRule.Type + makeRule + ingressServices } in kubernetes.Ingress::{ @@ -284,7 +283,7 @@ it's possible to use it together with the [union type of all k8s types that we g So if we want to deploy e.g. a Deployment and a Service together, we can do: -```haskell +```dhall let k8s = ./typesUnion.dhall in @@ -344,10 +343,8 @@ to run this command afterwards. [kubernetes]: https://kubernetes.io/ [normalization]: https://en.wikipedia.org/wiki/Normalization_property_(abstract_rewriting) [nginx-ingress]: https://github.com/kubernetes/ingress-nginx -[dhall-tutorial]: http://hackage.haskell.org/package/dhall-1.21.0/docs/Dhall-Tutorial.html -[default-deployment]: ./defaults/io.k8s.api.apps.v1.Deployment.dhall -[deployment]: ./types/io.k8s.api.apps.v1.Deployment.dhall -[Ingress]: ./types/io.k8s.api.extensions.v1beta1.Ingress.dhall -[Ingress-default]: ./default/io.k8s.api.extensions.v1beta1.Ingress.dhall +[dhall-tutorial]: http://hackage.haskell.org/package/dhall-1.28.0/docs/Dhall-Tutorial.html +[deployment]: ./schemas/io.k8s.api.apps.v1.Deployment.dhall +[Ingress]: ./schemas/io.k8s.api.extensions.v1beta1.Ingress.dhall [prometheus-operator]: https://github.com/coreos/prometheus-operator [dhall-prometheus-operator]: https://github.com/coralogix/dhall-prometheus-operator diff --git a/docs/README.md.dhall b/docs/README.md.dhall index 54a8a4de..bffd772c 100644 --- a/docs/README.md.dhall +++ b/docs/README.md.dhall @@ -39,22 +39,18 @@ or the [full tutorial][dhall-tutorial]. Let's say we'd like to configure a Deployment exposing an `nginx` webserver. In the following example, we: -1. Import the Kubernetes definitions as Dhall Types (the `types.dhall` file) from the local repo. +1. Import the Kubernetes definitions as a Dhall package (the `package.dhall` file) from the local repo. In your case you will want to replace the local path with a remote one, e.g. - `https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/0a4f0b87fbdd4b679853c81ff804bde7b44336cf/types.dhall`. + `https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/package.dhall` Note: the `sha256:..` is applied to some imports so that: 1. the import is cached locally after the first evaluation, with great time savings (and avoiding network calls) 2. prevent execution if the content of the file changes. This is a security feature, and you can read more [in Dhall's "Security Guarantees" document][security-hashes] -2. Import the defaults for the above types. - Since _most_ of the fields in any definition are optional, for better ergonomics while - coding Dhall we have generated default values for all types, so we can just use the `//` - operator (right-biased record merge) to add our data to the default configuration. - The pattern looks something like this: `defaultValue // { ourDataHere = ..}` -3. Define the [Deployment][deployment] using this pattern (see the default [here][default-deployment]) - and hardcoding the deployment details: + Note: instead of using the `package.dhall` from the `master` branch, you may want to use a tagged release, + as the contents of the `master` branch are liable to change without warning. +2. Define the [Deployment][deployment] using the schema pattern and hardcoding the deployment details: -```haskell +```dhall -- examples/deploymentSimple.dhall ${../examples/deploymentSimple.dhall as Text} @@ -87,7 +83,7 @@ and reuse those objects for configuring other things (e.g. configuring the servi templating documentation, configuring Terraform deployments, you name it). As an example of that, next we'll define an Ingress (an [Nginx Ingress][nginx-ingress] in this case), -containing stuff like TLS certs and routes for every service - see the [type][Ingress] and [default][Ingress-default] for it. +containing stuff like TLS certs and routes for every service - see the [schema][Ingress]. Things to note in the following example: - we define the `Service` type inline in the file, but in your case you'll want to have a @@ -98,7 +94,7 @@ Things to note in the following example: `mkIngress` function instead of applying it, so you can do something like `dhall-to-yaml --omitEmpty <<< "./mkIngress.dhall ./myServices.dhall"` -```haskell +```dhall -- examples/ingress.dhall ${../examples/ingress.dhall as Text} @@ -137,7 +133,7 @@ it's possible to use it together with the [union type of all k8s types that we g So if we want to deploy e.g. a Deployment and a Service together, we can do: -```haskell +```dhall let k8s = ./typesUnion.dhall in @@ -197,11 +193,9 @@ to run this command afterwards. [kubernetes]: https://kubernetes.io/ [normalization]: https://en.wikipedia.org/wiki/Normalization_property_(abstract_rewriting) [nginx-ingress]: https://github.com/kubernetes/ingress-nginx -[dhall-tutorial]: http://hackage.haskell.org/package/dhall-1.21.0/docs/Dhall-Tutorial.html -[default-deployment]: ./defaults/io.k8s.api.apps.v1.Deployment.dhall -[deployment]: ./types/io.k8s.api.apps.v1.Deployment.dhall -[Ingress]: ./types/io.k8s.api.extensions.v1beta1.Ingress.dhall -[Ingress-default]: ./default/io.k8s.api.extensions.v1beta1.Ingress.dhall +[dhall-tutorial]: http://hackage.haskell.org/package/dhall-1.28.0/docs/Dhall-Tutorial.html +[deployment]: ./schemas/io.k8s.api.apps.v1.Deployment.dhall +[Ingress]: ./schemas/io.k8s.api.extensions.v1beta1.Ingress.dhall [prometheus-operator]: https://github.com/coreos/prometheus-operator [dhall-prometheus-operator]: https://github.com/coralogix/dhall-prometheus-operator '' diff --git a/examples/aws-iam-authenticator-chart.dhall b/examples/aws-iam-authenticator-chart.dhall index d5475188..50fd7ea9 100644 --- a/examples/aws-iam-authenticator-chart.dhall +++ b/examples/aws-iam-authenticator-chart.dhall @@ -1,4 +1,5 @@ -let kubernetes = ../schemas.dhall +let kubernetes = + ../package.dhall sha256:3ea8628b704704de295261dfc7626c15247c589c10a266f970cade262543fdda let release = "wintering-rodent" @@ -13,92 +14,108 @@ let chart = "${name}-${version}" let heritage = "dhall" in kubernetes.DaemonSet::{ - , metadata = kubernetes.ObjectMeta::{ - , name = fullName - , labels = toMap - { app = name - , chart = chart - , release = release - , heritage = heritage + , metadata = + kubernetes.ObjectMeta::{ + , name = fullName + , labels = + toMap + { app = name + , chart = chart + , release = release + , heritage = heritage + } } - } - , spec = Some kubernetes.DaemonSetSpec::{ - , updateStrategy = Some kubernetes.DaemonSetUpdateStrategy::{ - , type = Some "RollingUpdate" - } - , template = kubernetes.PodTemplateSpec::{ - , metadata = kubernetes.ObjectMeta::{ - , name = name - , annotations = toMap - { `scheduler.alpha.kubernetes.io/critical-pod` = "" - } - , labels = toMap - { app = name - , release = release - } + , spec = + Some + kubernetes.DaemonSetSpec::{ + , updateStrategy = + Some + kubernetes.DaemonSetUpdateStrategy::{ + , type = Some "RollingUpdate" + } + , template = + kubernetes.PodTemplateSpec::{ + , metadata = + kubernetes.ObjectMeta::{ + , name = name + , annotations = + toMap + { `scheduler.alpha.kubernetes.io/critical-pod` = "" } + , labels = toMap { app = name, release = release } + } + , spec = + Some + kubernetes.PodSpec::{ + , hostNetwork = Some True + , nodeSelector = + toMap { `node-role.kubernetes.io/master` = "" } + , tolerations = + [ kubernetes.Toleration::{ + , effect = Some "NoSchedule" + , key = Some "node-role.kubernetes.io/master" + } + , kubernetes.Toleration::{ + , effect = Some "CriticalAddonsOnly" + , key = Some "Exists" + } + ] + , containers = + [ kubernetes.Container::{ + , name = fullName + , image = + Some "gcr.io/heptio-images/authenticator:v0.1.0" + , args = + [ "server" + , "--config=/etc/aws-iam-authenticator/config.yaml" + , "--state-dir=/var/aws-iam-authenticator" + , "--generate-kubeconfig=/etc/kubernetes/aws-iam-authenticator/kubeconfig.yaml" + ] + , volumeMounts = + [ kubernetes.VolumeMount::{ + , name = "config" + , mountPath = "/etc/aws-iam-authenticator/" + } + , kubernetes.VolumeMount::{ + , name = "state" + , mountPath = "/var/aws-iam-authenticator/" + } + , kubernetes.VolumeMount::{ + , name = "output" + , mountPath = + "/etc/kubernetes/aws-iam-authenticator/" + } + ] + } + ] + , volumes = + [ kubernetes.Volume::{ + , name = "config" + , configMap = + Some + kubernetes.ConfigMapVolumeSource::{ + , name = Some fullName + } + } + , kubernetes.Volume::{ + , name = "output" + , hostPath = + Some + kubernetes.HostPathVolumeSource::{ + , path = + "/srv/kubernetes/aws-iam-authenticator/" + } + } + , kubernetes.Volume::{ + , name = "state" + , hostPath = + Some + kubernetes.HostPathVolumeSource::{ + , path = + "/srv/kubernetes/aws-iam-authenticator/" + } + } + ] + } + } } - , spec = Some kubernetes.PodSpec::{ - , hostNetwork = Some True - , nodeSelector = toMap - { `node-role.kubernetes.io/master` = "" - } - , tolerations = - [ kubernetes.Toleration::{ - , effect = Some "NoSchedule" - , key = Some "node-role.kubernetes.io/master" - } - , kubernetes.Toleration::{ - , effect = Some "CriticalAddonsOnly" - , key = Some "Exists" - } - ] - , containers = - [ kubernetes.Container::{ - , name = fullName - , image = Some "gcr.io/heptio-images/authenticator:v0.1.0" - , args = - [ "server" - , "--config=/etc/aws-iam-authenticator/config.yaml" - , "--state-dir=/var/aws-iam-authenticator" - , "--generate-kubeconfig=/etc/kubernetes/aws-iam-authenticator/kubeconfig.yaml" - ] - , volumeMounts = - [ kubernetes.VolumeMount::{ - , name = "config" - , mountPath = "/etc/aws-iam-authenticator/" - } - , kubernetes.VolumeMount::{ - , name = "state" - , mountPath = "/var/aws-iam-authenticator/" - } - , kubernetes.VolumeMount::{ - , name = "output" - , mountPath = "/etc/kubernetes/aws-iam-authenticator/" - } - ] - } - ] - , volumes = - [ kubernetes.Volume::{ - , name = "config" - , configMap = Some kubernetes.ConfigMapVolumeSource::{ - , name = Some fullName - } - } - , kubernetes.Volume::{ - , name = "output" - , hostPath = Some kubernetes.HostPathVolumeSource::{ - , path = "/srv/kubernetes/aws-iam-authenticator/" - } - } - , kubernetes.Volume::{ - , name = "state" - , hostPath = Some kubernetes.HostPathVolumeSource::{ - , path = "/srv/kubernetes/aws-iam-authenticator/" - } - } - ] - } - } - } } diff --git a/examples/deployment.dhall b/examples/deployment.dhall index 6e60ed24..ea9490a7 100644 --- a/examples/deployment.dhall +++ b/examples/deployment.dhall @@ -1,8 +1,5 @@ -let types = - ../types.dhall sha256:e48e21b807dad217a6c3e631fcaf3e950062310bfb4a8bbcecc330eb7b2f60ed - let kubernetes = - ../schemas.dhall sha256:9704063d1e2d17050cb18afae199a24f4cd1264e6c8e696ca94781309e213785 + ../package.dhall sha256:3ea8628b704704de295261dfc7626c15247c589c10a266f970cade262543fdda let kv = (../Prelude.dhall).JSON.keyText @@ -21,8 +18,8 @@ let deployment = kubernetes.DeploymentStrategy::{ , type = Some "RollingUpdate" , rollingUpdate = - { maxSurge = Some (types.IntOrString.Int 5) - , maxUnavailable = Some (types.IntOrString.Int 0) + { maxSurge = Some (kubernetes.IntOrString.Int 5) + , maxUnavailable = Some (kubernetes.IntOrString.Int 0) } } , template = diff --git a/examples/deploymentSimple.dhall b/examples/deploymentSimple.dhall index 8c8191c4..a5f00d3b 100644 --- a/examples/deploymentSimple.dhall +++ b/examples/deploymentSimple.dhall @@ -1,5 +1,5 @@ let kubernetes = - ../schemas.dhall sha256:9704063d1e2d17050cb18afae199a24f4cd1264e6c8e696ca94781309e213785 + ../package.dhall sha256:3ea8628b704704de295261dfc7626c15247c589c10a266f970cade262543fdda let deployment = kubernetes.Deployment::{ diff --git a/examples/ingress.dhall b/examples/ingress.dhall index c517df6e..aeaa952a 100644 --- a/examples/ingress.dhall +++ b/examples/ingress.dhall @@ -4,25 +4,22 @@ let map = Prelude.List.map let kv = Prelude.JSON.keyText -let types = - ../types.dhall sha256:e48e21b807dad217a6c3e631fcaf3e950062310bfb4a8bbcecc330eb7b2f60ed - let kubernetes = - ../schemas.dhall sha256:9704063d1e2d17050cb18afae199a24f4cd1264e6c8e696ca94781309e213785 + ../package.dhall sha256:3ea8628b704704de295261dfc7626c15247c589c10a266f970cade262543fdda let Service = { name : Text, host : Text, version : Text } let services = [ { name = "foo", host = "foo.example.com", version = "2.3" } ] let makeTLS - : Service → types.IngressTLS + : Service → kubernetes.IngressTLS.Type = λ(service : Service) → { hosts = [ service.host ] , secretName = Some "${service.name}-certificate" } let makeRule - : Service → types.IngressRule + : Service → kubernetes.IngressRule.Type = λ(service : Service) → { host = Some service.host , http = @@ -30,7 +27,7 @@ let makeRule { paths = [ { backend = { serviceName = service.name - , servicePort = types.IntOrString.Int 80 + , servicePort = kubernetes.IntOrString.Int 80 } , path = None Text } @@ -39,7 +36,7 @@ let makeRule } let mkIngress - : List Service → types.Ingress + : List Service → kubernetes.Ingress.Type = λ(inputServices : List Service) → let annotations = [ kv "kubernetes.io/ingress.class" "nginx" @@ -56,8 +53,14 @@ let mkIngress let spec = kubernetes.IngressSpec::{ - , tls = map Service types.IngressTLS makeTLS ingressServices - , rules = map Service types.IngressRule makeRule ingressServices + , tls = + map Service kubernetes.IngressTLS.Type makeTLS ingressServices + , rules = + map + Service + kubernetes.IngressRule.Type + makeRule + ingressServices } in kubernetes.Ingress::{ diff --git a/examples/service.dhall b/examples/service.dhall index 9cd005c7..2de34cef 100644 --- a/examples/service.dhall +++ b/examples/service.dhall @@ -1,8 +1,5 @@ -let types = - ../types.dhall sha256:e48e21b807dad217a6c3e631fcaf3e950062310bfb4a8bbcecc330eb7b2f60ed - let kubernetes = - ../schemas.dhall sha256:9704063d1e2d17050cb18afae199a24f4cd1264e6c8e696ca94781309e213785 + ../package.dhall sha256:3ea8628b704704de295261dfc7626c15247c589c10a266f970cade262543fdda let kv = (../Prelude.dhall).JSON.keyText @@ -11,14 +8,14 @@ let spec = , type = Some "NodePort" , ports = [ kubernetes.ServicePort::{ - , targetPort = Some (types.IntOrString.Int 80) + , targetPort = Some (kubernetes.IntOrString.Int 80) , port = 80 } ] } let service - : types.Service + : kubernetes.Service.Type = kubernetes.Service::{ , metadata = kubernetes.ObjectMeta::{ diff --git a/package.dhall b/package.dhall new file mode 100644 index 00000000..d403a5f4 --- /dev/null +++ b/package.dhall @@ -0,0 +1,5 @@ + ./schemas.dhall sha256:9704063d1e2d17050cb18afae199a24f4cd1264e6c8e696ca94781309e213785 +∧ { IntOrString = + ( ./types.dhall sha256:e48e21b807dad217a6c3e631fcaf3e950062310bfb4a8bbcecc330eb7b2f60ed + ).IntOrString + }