mirror of
https://github.com/dhall-lang/dhall-kubernetes.git
synced 2024-09-17 10:27:08 +03:00
Extract and check examples (#27)
* We move all the example code from the readme to the `examples` folder. * We provide a `scripts/build-readme.sh` script that inlines referenced examples in `README.md.in` and outputs `README.md`. The script also verifies that the output readme is the same as in version control. * We provide a `scripts/build-examples.py` script that builds the Yaml output for all examples. The script also verifies that the generated Yaml files are the same as in version control.
This commit is contained in:
parent
e132545c34
commit
4a7f02cd0d
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/result
|
||||||
|
/result-*
|
125
README.md
125
README.md
@ -16,58 +16,62 @@ For a version compatible with a previous version, check out [this commit](https:
|
|||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
In the `types` folder you'll find the types for the Kubernetes definitions. E.g. [here's][Deployment] the type for a Deployment.
|
In the `types` folder you'll find the types for the Kubernetes definitions. E.g.
|
||||||
|
[here's][Deployment] the type for a Deployment.
|
||||||
|
|
||||||
Since _most_ of the fields in all definitions are optional, for better ergonomics while coding Dhall we also generate default values for all types, in the `default` folder.
|
Since _most_ of the fields in all definitions are optional, for better
|
||||||
When some fields are required, the default value is a function whose input is a record of required fields, that returns the object with these fields set. E.g. the default for the Deployment is [this function][Deployment-default].
|
ergonomics while coding Dhall we also generate default values for all types, in
|
||||||
|
the `default` folder. When some fields are required, the default value is a
|
||||||
|
function whose input is a record of required fields, that returns the object
|
||||||
|
with these fields set. E.g. the default for the Deployment is [this
|
||||||
|
function][Deployment-default].
|
||||||
|
|
||||||
Since this might sound a bit abstract, let's go with some examples.
|
Since this might sound a bit abstract, let's go with some examples. You can find
|
||||||
|
these examples in the [`./examples` folder](./examples) and evaluate them there.
|
||||||
|
|
||||||
### Example: Deployment
|
### Example: Deployment
|
||||||
|
|
||||||
Let's say we have several services, whose configuration has this type:
|
Let's say we have several services, whose configuration has this type:
|
||||||
```haskell
|
```haskell
|
||||||
-- Service.dhall
|
-- examples/Service.dhall
|
||||||
{ name : Text
|
{ name : Text
|
||||||
, host : Text
|
, host : Text
|
||||||
, version : Text
|
, version : Text
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
So a configuration for a service might look like this:
|
So a configuration for a service might look like this:
|
||||||
```haskell
|
```haskell
|
||||||
-- service-foo.dhall
|
-- examples/service-foo.dhall
|
||||||
{ name = "foo"
|
{ name = "foo"
|
||||||
, host = "foo.example.com"
|
, host = "foo.example.com"
|
||||||
, version = "1.0.1"
|
, version = "1.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
We can then make a Deployment object for this service:
|
We can then make a Deployment object for this service:
|
||||||
```haskell
|
```haskell
|
||||||
-- deployment.yaml.dhall
|
-- examples/deployment.dhall
|
||||||
|
|
||||||
-- Prelude imports
|
-- Prelude imports
|
||||||
let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map
|
let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map
|
||||||
in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some
|
in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some
|
||||||
in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None
|
in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None
|
||||||
|
|
||||||
-- import dhall-kubernetes types and defaults
|
-- import dhall-kubernetes types and defaults
|
||||||
in let Deployment = ./types/io.k8s.api.apps.v1beta2.Deployment.dhall
|
in let Deployment = ../types/io.k8s.api.apps.v1beta2.Deployment.dhall
|
||||||
in let Spec = ./types/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall
|
in let Spec = ../types/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall
|
||||||
in let PodSpec = ./types/io.k8s.api.core.v1.PodSpec.dhall
|
in let PodSpec = ../types/io.k8s.api.core.v1.PodSpec.dhall
|
||||||
in let ContainerPort = ./types/io.k8s.api.core.v1.ContainerPort.dhall
|
in let ContainerPort = ../types/io.k8s.api.core.v1.ContainerPort.dhall
|
||||||
in let defaultDeployment = ./default/io.k8s.api.apps.v1beta2.Deployment.dhall
|
in let defaultDeployment = ../default/io.k8s.api.apps.v1beta2.Deployment.dhall
|
||||||
in let defaultMeta = ./default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall
|
in let defaultMeta = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall
|
||||||
in let defaultSpec = ./default/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall
|
in let defaultSpec = ../default/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall
|
||||||
in let defaultTemplate = ./default/io.k8s.api.core.v1.PodTemplateSpec.dhall
|
in let defaultTemplate = ../default/io.k8s.api.core.v1.PodTemplateSpec.dhall
|
||||||
in let defaultPodSpec = ./default/io.k8s.api.core.v1.PodSpec.dhall
|
in let defaultPodSpec = ../default/io.k8s.api.core.v1.PodSpec.dhall
|
||||||
in let defaultSelector = ./default/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector.dhall
|
in let defaultSelector = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector.dhall
|
||||||
in let defaultContainer = ./default/io.k8s.api.core.v1.Container.dhall
|
in let defaultContainer = ../default/io.k8s.api.core.v1.Container.dhall
|
||||||
in let defaultContainerPort = ./default/io.k8s.api.core.v1.ContainerPort.dhall
|
in let defaultContainerPort = ../default/io.k8s.api.core.v1.ContainerPort.dhall
|
||||||
|
|
||||||
-- Our Service type
|
|
||||||
in let Service = ./Service.dhall
|
|
||||||
|
|
||||||
-- and our service
|
-- and our service
|
||||||
in let fooService = ./service-foo.dhall
|
in let fooService = ./service-foo.dhall
|
||||||
@ -85,15 +89,15 @@ in let spec = defaultSpec
|
|||||||
} //
|
} //
|
||||||
{ spec = Some PodSpec (defaultPodSpec
|
{ spec = Some PodSpec (defaultPodSpec
|
||||||
{ containers = [
|
{ containers = [
|
||||||
defaultContainer
|
defaultContainer
|
||||||
{ name = fooService.name } //
|
{ name = fooService.name } //
|
||||||
{ image = Some Text "your-container-service.io/${fooService.name}:${fooService.version}"
|
{ image = Some Text "your-container-service.io/${fooService.name}:${fooService.version}"
|
||||||
, imagePullPolicy = Some Text "Always"
|
, imagePullPolicy = Some Text "Always"
|
||||||
, ports = Some
|
, ports = Some
|
||||||
(List ContainerPort)
|
(List ContainerPort)
|
||||||
[(defaultContainerPort {containerPort = 8080})]
|
[(defaultContainerPort {containerPort = 8080})]
|
||||||
}
|
}
|
||||||
]})
|
]})
|
||||||
}
|
}
|
||||||
} //
|
} //
|
||||||
{ replicas = Some Natural 2
|
{ replicas = Some Natural 2
|
||||||
@ -111,12 +115,13 @@ in defaultDeployment
|
|||||||
We convert it to yaml with:
|
We convert it to yaml with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dhall-to-yaml --omitNull < deployment.yaml.dhall
|
dhall-to-yaml --omitNull < deployment.dhall
|
||||||
```
|
```
|
||||||
|
|
||||||
And we get:
|
And we get:
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: extensions/v1beta1
|
-- examples/out/deployment.yaml
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
spec:
|
spec:
|
||||||
revisionHistoryLimit: 10
|
revisionHistoryLimit: 10
|
||||||
@ -138,29 +143,31 @@ spec:
|
|||||||
replicas: 2
|
replicas: 2
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Example: Ingress
|
### Example: Ingress
|
||||||
|
|
||||||
Let's say we now want to generate an Ingress definition (for an Nginx Ingress) that contains TLS certs and routes for every service. It would be something like this:
|
Let's say we now want to generate an Ingress definition (for an Nginx Ingress)
|
||||||
|
that contains TLS certs and routes for every service. It would be something like
|
||||||
|
this:
|
||||||
```haskell
|
```haskell
|
||||||
-- ingress.yaml.dhall
|
-- examples/ingress.dhall
|
||||||
|
|
||||||
-- Prelude imports
|
-- Prelude imports
|
||||||
let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map
|
let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map
|
||||||
in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some
|
in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some
|
||||||
in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None
|
in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None
|
||||||
|
|
||||||
-- dhall-kubernetes types and defaults
|
-- dhall-kubernetes types and defaults
|
||||||
in let TLS = ./types/io.k8s.api.extensions.v1beta1.IngressTLS.dhall
|
in let TLS = ../types/io.k8s.api.extensions.v1beta1.IngressTLS.dhall
|
||||||
in let Rule = ./types/io.k8s.api.extensions.v1beta1.IngressRule.dhall
|
in let Rule = ../types/io.k8s.api.extensions.v1beta1.IngressRule.dhall
|
||||||
in let RuleVal = ./types/io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue.dhall
|
in let RuleVal = ../types/io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue.dhall
|
||||||
in let Spec = ./types/io.k8s.api.extensions.v1beta1.IngressSpec.dhall
|
in let Spec = ../types/io.k8s.api.extensions.v1beta1.IngressSpec.dhall
|
||||||
in let Ingress = ./types/io.k8s.api.extensions.v1beta1.Ingress.dhall
|
in let Ingress = ../types/io.k8s.api.extensions.v1beta1.Ingress.dhall
|
||||||
in let defaultIngress = ./default/io.k8s.api.extensions.v1beta1.Ingress.dhall
|
in let defaultIngress = ../default/io.k8s.api.extensions.v1beta1.Ingress.dhall
|
||||||
in let defaultMeta = ./default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall
|
in let defaultMeta = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall
|
||||||
in let defaultSpec = ./default/io.k8s.api.extensions.v1beta1.IngressSpec.dhall
|
in let defaultSpec = ../default/io.k8s.api.extensions.v1beta1.IngressSpec.dhall
|
||||||
|
|
||||||
-- Our Service type
|
-- Our Service type
|
||||||
in let Service = ./Service.dhall
|
in let Service = ./Service.dhall
|
||||||
@ -179,10 +186,14 @@ in let makeRule = \(service : Service) ->
|
|||||||
, servicePort = "80"
|
, servicePort = "80"
|
||||||
}
|
}
|
||||||
, path = None Text
|
, path = None Text
|
||||||
}]}}
|
}]}}
|
||||||
|
|
||||||
-- Nginx ingress requires a default service as a catchall
|
-- Nginx ingress requires a default service as a catchall
|
||||||
in let defaultService = { name = "default", host = "default.example.com" }
|
in let defaultService =
|
||||||
|
{ name = "default"
|
||||||
|
, host = "default.example.com"
|
||||||
|
, version = " 1.0"
|
||||||
|
}
|
||||||
|
|
||||||
-- List of services
|
-- List of services
|
||||||
in let fooService = ./service-foo.dhall
|
in let fooService = ./service-foo.dhall
|
||||||
@ -213,7 +224,7 @@ in defaultIngress
|
|||||||
{ name = "nginx" } //
|
{ name = "nginx" } //
|
||||||
{ annotations = annotations }
|
{ annotations = annotations }
|
||||||
} //
|
} //
|
||||||
{ spec = spec } : Ingress
|
{ spec = Some Spec spec } : Ingress
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -225,6 +236,7 @@ dhall-to-yaml --omitNull < ingress.yaml.dhall
|
|||||||
|
|
||||||
And we get:
|
And we get:
|
||||||
```yaml
|
```yaml
|
||||||
|
-- examples/out/ingress.yaml
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
spec:
|
spec:
|
||||||
@ -253,9 +265,26 @@ metadata:
|
|||||||
kubernetes.io/ingress.class: nginx
|
kubernetes.io/ingress.class: nginx
|
||||||
kubernetes.io/ingress.allow-http: 'false'
|
kubernetes.io/ingress.allow-http: 'false'
|
||||||
name: nginx
|
name: nginx
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
All tests are defined in `release.nix`. We run these tests in CI in a [Hydra
|
||||||
|
project][hydra-project]. You can run the tests locally with `nix build --no-link
|
||||||
|
release.nix`.
|
||||||
|
|
||||||
|
### Changing the README
|
||||||
|
|
||||||
|
We build `README.md` from `docs/README.md.dhall` and check it into source control.
|
||||||
|
The build script `./scripts/build-readme.sh` inlines source code from the
|
||||||
|
`examples` directory. If you make changes to the readme or the examples you need
|
||||||
|
to run `scripts/build-readme.sh`.
|
||||||
|
|
||||||
|
|
||||||
|
[hydra-project]: http://hydra.dhall-lang.org/project/dhall-kubernetes
|
||||||
[dhall-lang]: https://github.com/dhall-lang/dhall-lang
|
[dhall-lang]: https://github.com/dhall-lang/dhall-lang
|
||||||
[Deployment]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/types/io.k8s.api.apps.v1beta2.Deployment.dhall
|
[Deployment]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/types/io.k8s.api.apps.v1beta2.Deployment.dhall
|
||||||
[Deployment-default]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/default/io.k8s.api.apps.v1beta2.Deployment.dhall
|
[Deployment-default]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/default/io.k8s.api.apps.v1beta2.Deployment.dhall
|
||||||
|
108
docs/README.md.dhall
Normal file
108
docs/README.md.dhall
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
''
|
||||||
|
# `dhall-kubernetes`
|
||||||
|
|
||||||
|
Dhall bindings to Kubernetes.
|
||||||
|
This will let you typecheck, template and modularize your Kubernetes definitions with [Dhall][dhall-lang].
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
**NOTE**: `dhall-kubernetes` requires at least version `1.14.0` of [the interpreter](https://github.com/dhall-lang/dhall-haskell).
|
||||||
|
|
||||||
|
You can install the latest version with the following:
|
||||||
|
```bash
|
||||||
|
stack install dhall dhall-json --resolver=nightly
|
||||||
|
```
|
||||||
|
|
||||||
|
For a version compatible with a previous version, check out [this commit](https://github.com/dhall-lang/dhall-kubernetes/tree/b2357dcfa42a008efa203a850163d26f0d106e01).
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
In the `types` folder you'll find the types for the Kubernetes definitions. E.g.
|
||||||
|
[here's][Deployment] the type for a Deployment.
|
||||||
|
|
||||||
|
Since _most_ of the fields in all definitions are optional, for better
|
||||||
|
ergonomics while coding Dhall we also generate default values for all types, in
|
||||||
|
the `default` folder. When some fields are required, the default value is a
|
||||||
|
function whose input is a record of required fields, that returns the object
|
||||||
|
with these fields set. E.g. the default for the Deployment is [this
|
||||||
|
function][Deployment-default].
|
||||||
|
|
||||||
|
Since this might sound a bit abstract, let's go with some examples. You can find
|
||||||
|
these examples in the [`./examples` folder](./examples) and evaluate them there.
|
||||||
|
|
||||||
|
### Example: Deployment
|
||||||
|
|
||||||
|
Let's say we have several services, whose configuration has this type:
|
||||||
|
```haskell
|
||||||
|
-- examples/Service.dhall
|
||||||
|
${../examples/Service.dhall as Text}
|
||||||
|
```
|
||||||
|
|
||||||
|
So a configuration for a service might look like this:
|
||||||
|
```haskell
|
||||||
|
-- examples/service-foo.dhall
|
||||||
|
${../examples/service-foo.dhall as Text}
|
||||||
|
```
|
||||||
|
|
||||||
|
We can then make a Deployment object for this service:
|
||||||
|
```haskell
|
||||||
|
-- examples/deployment.dhall
|
||||||
|
${../examples/deployment.dhall as Text}
|
||||||
|
```
|
||||||
|
|
||||||
|
We convert it to yaml with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dhall-to-yaml --omitNull < deployment.dhall
|
||||||
|
```
|
||||||
|
|
||||||
|
And we get:
|
||||||
|
```yaml
|
||||||
|
-- examples/out/deployment.yaml
|
||||||
|
${../examples/out/deployment.yaml as Text}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Example: Ingress
|
||||||
|
|
||||||
|
Let's say we now want to generate an Ingress definition (for an Nginx Ingress)
|
||||||
|
that contains TLS certs and routes for every service. It would be something like
|
||||||
|
this:
|
||||||
|
```haskell
|
||||||
|
-- examples/ingress.dhall
|
||||||
|
${../examples/ingress.dhall as Text}
|
||||||
|
```
|
||||||
|
|
||||||
|
As usual we get the yaml out by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dhall-to-yaml --omitNull < ingress.yaml.dhall
|
||||||
|
```
|
||||||
|
|
||||||
|
And we get:
|
||||||
|
```yaml
|
||||||
|
-- examples/out/ingress.yaml
|
||||||
|
${../examples/out/ingress.yaml as Text}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
All tests are defined in `release.nix`. We run these tests in CI in a [Hydra
|
||||||
|
project][hydra-project]. You can run the tests locally with `nix build --no-link
|
||||||
|
release.nix`.
|
||||||
|
|
||||||
|
### Changing the README
|
||||||
|
|
||||||
|
We build `README.md` from `docs/README.md.dhall` and check it into source control.
|
||||||
|
The build script `./scripts/build-readme.sh` inlines source code from the
|
||||||
|
`examples` directory. If you make changes to the readme or the examples you need
|
||||||
|
to run `scripts/build-readme.sh`.
|
||||||
|
|
||||||
|
|
||||||
|
[hydra-project]: http://hydra.dhall-lang.org/project/dhall-kubernetes
|
||||||
|
[dhall-lang]: https://github.com/dhall-lang/dhall-lang
|
||||||
|
[Deployment]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/types/io.k8s.api.apps.v1beta2.Deployment.dhall
|
||||||
|
[Deployment-default]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/default/io.k8s.api.apps.v1beta2.Deployment.dhall
|
||||||
|
''
|
4
examples/Service.dhall
Normal file
4
examples/Service.dhall
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{ name : Text
|
||||||
|
, host : Text
|
||||||
|
, version : Text
|
||||||
|
}
|
55
examples/deployment.dhall
Normal file
55
examples/deployment.dhall
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
-- Prelude imports
|
||||||
|
let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map
|
||||||
|
in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some
|
||||||
|
in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None
|
||||||
|
|
||||||
|
-- import dhall-kubernetes types and defaults
|
||||||
|
in let Deployment = ../types/io.k8s.api.apps.v1beta2.Deployment.dhall
|
||||||
|
in let Spec = ../types/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall
|
||||||
|
in let PodSpec = ../types/io.k8s.api.core.v1.PodSpec.dhall
|
||||||
|
in let ContainerPort = ../types/io.k8s.api.core.v1.ContainerPort.dhall
|
||||||
|
in let defaultDeployment = ../default/io.k8s.api.apps.v1beta2.Deployment.dhall
|
||||||
|
in let defaultMeta = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall
|
||||||
|
in let defaultSpec = ../default/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall
|
||||||
|
in let defaultTemplate = ../default/io.k8s.api.core.v1.PodTemplateSpec.dhall
|
||||||
|
in let defaultPodSpec = ../default/io.k8s.api.core.v1.PodSpec.dhall
|
||||||
|
in let defaultSelector = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector.dhall
|
||||||
|
in let defaultContainer = ../default/io.k8s.api.core.v1.Container.dhall
|
||||||
|
in let defaultContainerPort = ../default/io.k8s.api.core.v1.ContainerPort.dhall
|
||||||
|
|
||||||
|
-- and our service
|
||||||
|
in let fooService = ./service-foo.dhall
|
||||||
|
|
||||||
|
-- Generate the DeploymentSpec for the service
|
||||||
|
in let selector = Some
|
||||||
|
(List { mapKey : Text, mapValue : Text })
|
||||||
|
[{ mapKey = "app", mapValue = fooService.name }]
|
||||||
|
|
||||||
|
in let spec = defaultSpec
|
||||||
|
{ selector = defaultSelector // { matchLabels = selector }
|
||||||
|
, template = defaultTemplate
|
||||||
|
{ metadata = defaultMeta
|
||||||
|
{ name = fooService.name } // { labels = selector }
|
||||||
|
} //
|
||||||
|
{ spec = Some PodSpec (defaultPodSpec
|
||||||
|
{ containers = [
|
||||||
|
defaultContainer
|
||||||
|
{ name = fooService.name } //
|
||||||
|
{ image = Some Text "your-container-service.io/${fooService.name}:${fooService.version}"
|
||||||
|
, imagePullPolicy = Some Text "Always"
|
||||||
|
, ports = Some
|
||||||
|
(List ContainerPort)
|
||||||
|
[(defaultContainerPort {containerPort = 8080})]
|
||||||
|
}
|
||||||
|
]})
|
||||||
|
}
|
||||||
|
} //
|
||||||
|
{ replicas = Some Natural 2
|
||||||
|
, revisionHistoryLimit = Some Natural 10
|
||||||
|
}
|
||||||
|
|
||||||
|
-- and here's the Deployment
|
||||||
|
in defaultDeployment
|
||||||
|
{ metadata = defaultMeta { name = fooService.name }
|
||||||
|
} //
|
||||||
|
{ spec = Some Spec spec } : Deployment
|
71
examples/ingress.dhall
Normal file
71
examples/ingress.dhall
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
-- Prelude imports
|
||||||
|
let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map
|
||||||
|
in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some
|
||||||
|
in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None
|
||||||
|
|
||||||
|
-- dhall-kubernetes types and defaults
|
||||||
|
in let TLS = ../types/io.k8s.api.extensions.v1beta1.IngressTLS.dhall
|
||||||
|
in let Rule = ../types/io.k8s.api.extensions.v1beta1.IngressRule.dhall
|
||||||
|
in let RuleVal = ../types/io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue.dhall
|
||||||
|
in let Spec = ../types/io.k8s.api.extensions.v1beta1.IngressSpec.dhall
|
||||||
|
in let Ingress = ../types/io.k8s.api.extensions.v1beta1.Ingress.dhall
|
||||||
|
in let defaultIngress = ../default/io.k8s.api.extensions.v1beta1.Ingress.dhall
|
||||||
|
in let defaultMeta = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall
|
||||||
|
in let defaultSpec = ../default/io.k8s.api.extensions.v1beta1.IngressSpec.dhall
|
||||||
|
|
||||||
|
-- Our Service type
|
||||||
|
in let Service = ./Service.dhall
|
||||||
|
|
||||||
|
-- Given a service, make a TLS definition with their host and certificate
|
||||||
|
in let makeTLS = \(service : Service) ->
|
||||||
|
{ hosts = Some (List Text) [ service.host ]
|
||||||
|
, secretName = Some Text "${service.name}-certificate"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Given a service, make an Ingress Rule
|
||||||
|
in let makeRule = \(service : Service) ->
|
||||||
|
{ host = Some Text service.host
|
||||||
|
, http = Some RuleVal
|
||||||
|
{ paths = [ { backend = { serviceName = service.name
|
||||||
|
, servicePort = "80"
|
||||||
|
}
|
||||||
|
, path = None Text
|
||||||
|
}]}}
|
||||||
|
|
||||||
|
-- Nginx ingress requires a default service as a catchall
|
||||||
|
in let defaultService =
|
||||||
|
{ name = "default"
|
||||||
|
, host = "default.example.com"
|
||||||
|
, version = " 1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- List of services
|
||||||
|
in let fooService = ./service-foo.dhall
|
||||||
|
in let services =
|
||||||
|
[ fooService
|
||||||
|
, defaultService
|
||||||
|
]
|
||||||
|
|
||||||
|
-- Some metadata annotations
|
||||||
|
-- NOTE: `dhall-to-yaml` will generate a record with arbitrary keys from a list
|
||||||
|
-- of records where mapKey is the key and mapValue is the value of that key
|
||||||
|
in let genericRecord = List { mapKey : Text, mapValue : Text }
|
||||||
|
in let kv = \(k : Text) -> \(v : Text) -> { mapKey = k, mapValue = v }
|
||||||
|
|
||||||
|
in let annotations = Some genericRecord
|
||||||
|
[ kv "kubernetes.io/ingress.class" "nginx"
|
||||||
|
, kv "kubernetes.io/ingress.allow-http" "false"
|
||||||
|
]
|
||||||
|
|
||||||
|
-- Generate spec from services
|
||||||
|
in let spec = defaultSpec //
|
||||||
|
{ tls = Some (List TLS) (map Service TLS makeTLS services)
|
||||||
|
, rules = Some (List Rule) (map Service Rule makeRule services)
|
||||||
|
}
|
||||||
|
|
||||||
|
in defaultIngress
|
||||||
|
{ metadata = defaultMeta
|
||||||
|
{ name = "nginx" } //
|
||||||
|
{ annotations = annotations }
|
||||||
|
} //
|
||||||
|
{ spec = Some Spec spec } : Ingress
|
22
examples/out/deployment.yaml
Normal file
22
examples/out/deployment.yaml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
spec:
|
||||||
|
revisionHistoryLimit: 10
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: foo
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: your-container-service.io/foo:1.0.1
|
||||||
|
imagePullPolicy: Always
|
||||||
|
name: foo
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
labels:
|
||||||
|
app: foo
|
||||||
|
replicas: 2
|
||||||
|
metadata:
|
||||||
|
name: foo
|
28
examples/out/ingress.yaml
Normal file
28
examples/out/ingress.yaml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
servicePort: '80'
|
||||||
|
serviceName: foo
|
||||||
|
host: foo.example.com
|
||||||
|
- http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
servicePort: '80'
|
||||||
|
serviceName: default
|
||||||
|
host: default.example.com
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- foo.example.com
|
||||||
|
secretName: foo-certificate
|
||||||
|
- hosts:
|
||||||
|
- default.example.com
|
||||||
|
secretName: default-certificate
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: nginx
|
||||||
|
kubernetes.io/ingress.allow-http: 'false'
|
||||||
|
name: nginx
|
4
examples/service-foo.dhall
Normal file
4
examples/service-foo.dhall
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{ name = "foo"
|
||||||
|
, host = "foo.example.com"
|
||||||
|
, version = "1.0.1"
|
||||||
|
}
|
67
release.nix
67
release.nix
@ -1,34 +1,55 @@
|
|||||||
let
|
let
|
||||||
pkgs = import ./nixpkgs.nix;
|
pkgs = import ./nixpkgs.nix;
|
||||||
|
|
||||||
dhall-kubernetes-smoketests =
|
nativeBuildInputs = [
|
||||||
pkgs.runCommand
|
pkgs.git
|
||||||
"all-generated-files-compile"
|
pkgs.python3
|
||||||
{ nativeBuildInputs = [
|
pkgs.bash
|
||||||
pkgs.python3
|
pkgs.dhall
|
||||||
pkgs.bash
|
pkgs.dhall-json
|
||||||
pkgs.dhall
|
pkgs.dhall-text
|
||||||
pkgs.glibcLocales
|
pkgs.glibcLocales
|
||||||
];
|
];
|
||||||
}
|
|
||||||
|
runCommand = name: script:
|
||||||
|
pkgs.runCommand name { inherit nativeBuildInputs; } script;
|
||||||
|
|
||||||
|
generatedFilesCompile =
|
||||||
|
runCommand
|
||||||
|
"generated-files-compile"
|
||||||
''
|
''
|
||||||
cd ${./.}
|
cd ${./.}
|
||||||
LC_ALL=en_US.UTF-8 python3 ${./check-source.py}
|
LC_ALL=en_US.UTF-8 ./scripts/check-source.py
|
||||||
|
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Derivation that trivially depends on the current directory so that Hydra's
|
buildReadme =
|
||||||
# pull request builder always posts a GitHub status on each revision
|
runCommand
|
||||||
pwd = pkgs.runCommand "pwd" { here = ./.; } "touch $out";
|
"build-readme"
|
||||||
|
''
|
||||||
|
cd ${./.}
|
||||||
|
./scripts/build-readme.sh $out
|
||||||
|
|
||||||
in
|
diff --unified --color=always README.md $out
|
||||||
{ dhall-kubernetes = pkgs.releaseTools.aggregate {
|
'';
|
||||||
name = "dhall-kubernetes";
|
|
||||||
|
|
||||||
constituents = [
|
validateExamples =
|
||||||
dhall-kubernetes-smoketests
|
runCommand
|
||||||
pwd
|
"validate-examples"
|
||||||
];
|
''
|
||||||
};
|
cd ${./.}
|
||||||
}
|
mkdir $out
|
||||||
|
LC_ALL=en_US.UTF-8 ./scripts/build-examples.py $out
|
||||||
|
'';
|
||||||
|
|
||||||
|
in {
|
||||||
|
dhall-kubernetes = pkgs.releaseTools.aggregate {
|
||||||
|
name = "dhall-kubernetes";
|
||||||
|
constituents = [
|
||||||
|
generatedFilesCompile
|
||||||
|
validateExamples
|
||||||
|
buildReadme
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
71
scripts/build-examples.py
Executable file
71
scripts/build-examples.py
Executable file
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Build YAML files from the example Dhall sources.
|
||||||
|
#
|
||||||
|
# An optional second argument determines the directory to write the YAML
|
||||||
|
# files to. Defaults to the exmaples directory.
|
||||||
|
#
|
||||||
|
# Also checks that the generated files do not differ from what is
|
||||||
|
# checked into source control. We list all generated files that differ
|
||||||
|
# and exit with status code 1 if the list is non-empty.
|
||||||
|
|
||||||
|
import os
|
||||||
|
from subprocess import run, DEVNULL
|
||||||
|
import sys
|
||||||
|
|
||||||
|
examples_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'examples'))
|
||||||
|
examples = [
|
||||||
|
'deployment',
|
||||||
|
'ingress'
|
||||||
|
]
|
||||||
|
|
||||||
|
TERM_FAIL = '\033[91m'
|
||||||
|
TERM_CLEAR = '\033[0m'
|
||||||
|
|
||||||
|
|
||||||
|
def check_clean(path_1, path_2):
|
||||||
|
if path_1 == path_2:
|
||||||
|
cmd = ['git', 'diff', '--exit-code', '--', path_1]
|
||||||
|
else:
|
||||||
|
cmd = ['diff', '--unified', '--', path_1, path_2]
|
||||||
|
|
||||||
|
result = run(cmd, check=False, stdout=DEVNULL)
|
||||||
|
if result.returncode == 0:
|
||||||
|
return True
|
||||||
|
elif result.returncode == 1:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# Raises a `CalledProcessError`
|
||||||
|
result.check_returncode()
|
||||||
|
|
||||||
|
|
||||||
|
def build_yaml (example, out_dir):
|
||||||
|
source = os.path.join(examples_dir, example + '.dhall')
|
||||||
|
print('Building {}'.format(source))
|
||||||
|
target = os.path.join(out_dir, example + '.yaml')
|
||||||
|
expected = os.path.join(examples_dir, 'out', example + '.yaml')
|
||||||
|
cmd = 'dhall-to-yaml --omitNull <<< "./{source}" >{target}'.format(source=source, target=target)
|
||||||
|
run(cmd, shell=True, executable='bash', check=True)
|
||||||
|
return check_clean(expected, target)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) >= 2:
|
||||||
|
out_dir = sys.argv[1]
|
||||||
|
else:
|
||||||
|
out_dir = os.path.join(examples_dir, 'out')
|
||||||
|
|
||||||
|
clean_failures = []
|
||||||
|
for example in examples:
|
||||||
|
is_clean = build_yaml(example, out_dir)
|
||||||
|
if is_clean == False:
|
||||||
|
clean_failures.append(example + '.yaml')
|
||||||
|
|
||||||
|
if len(clean_failures) > 0:
|
||||||
|
print(TERM_FAIL + 'Clean check failed. The following files differ' + TERM_CLEAR)
|
||||||
|
for failure in clean_failures:
|
||||||
|
print(' ' + failure)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
9
scripts/build-readme.sh
Executable file
9
scripts/build-readme.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ -z $1 ]]; then
|
||||||
|
output=README.md
|
||||||
|
else
|
||||||
|
output="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LC_ALL=en_US.UTF-8 dhall-to-text <<< './docs/README.md.dhall' > $output
|
Loading…
Reference in New Issue
Block a user