mirror of
https://github.com/dhall-lang/dhall-kubernetes.git
synced 2024-09-17 10:27:08 +03:00
Merge pull request #9 from dhall-lang/f-f/add-readme
Add some documentation and examples
This commit is contained in:
commit
dd2515c88a
251
README.md
251
README.md
@ -1,13 +1,254 @@
|
||||
# Usage
|
||||
# `dhall-kubernetes`
|
||||
|
||||
## Typed
|
||||
Dhall bindings to Kubernetes.
|
||||
This will let you typecheck, template and modularize your Kubernetes definitions with [Dhall][dhall-lang].
|
||||
|
||||
## 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.
|
||||
|
||||
### Example: Deployment
|
||||
|
||||
Let's say we have several services, whose configuration has this type:
|
||||
```haskell
|
||||
-- Service.dhall
|
||||
{ name : Text
|
||||
, host : Text
|
||||
, version : Text
|
||||
}
|
||||
```
|
||||
|
||||
So a configuration for a service might look like this:
|
||||
```haskell
|
||||
-- service-foo.dhall
|
||||
{ name = "foo"
|
||||
, host = "foo.example.com"
|
||||
, version = "1.0.1"
|
||||
}
|
||||
```
|
||||
|
||||
We can then make a Deployment object for this service:
|
||||
```haskell
|
||||
-- deployment.yaml.dhall
|
||||
|
||||
-- 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
|
||||
|
||||
-- Our Service type
|
||||
in let Service = ./Service.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 Integer 2
|
||||
, revisionHistoryLimit = Some Integer 10
|
||||
}
|
||||
|
||||
-- and here's the Deployment
|
||||
in defaultDeployment
|
||||
{ apiVersion = "extensions/v1beta1"
|
||||
, kind = "Deployment"
|
||||
, metadata = defaultMeta { name = fooService.name }
|
||||
} //
|
||||
{ spec = Some Spec spec } : Deployment
|
||||
|
||||
```
|
||||
|
||||
We convert it to yaml with:
|
||||
|
||||
```bash
|
||||
cat example-deployment.dhall | dhall-to-yaml | kubectl apply -f -
|
||||
dhall-to-yaml --omitNull < deployment.yaml.dhall
|
||||
```
|
||||
|
||||
## Untyped
|
||||
And we get:
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
### 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
|
||||
-- ingress.yaml.dhall
|
||||
|
||||
-- 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" }
|
||||
|
||||
-- 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
|
||||
{ apiVersion = "extensions/v1beta1"
|
||||
, kind = "Ingress"
|
||||
, metadata = defaultMeta
|
||||
{ name = "nginx" } //
|
||||
{ annotations = annotations }
|
||||
} //
|
||||
{ spec = spec } : Ingress
|
||||
|
||||
```
|
||||
cat example-deployment-no-types.dhall | dhall-to-yaml | kubectl apply -f -
|
||||
|
||||
As usual we get the yaml out by running:
|
||||
|
||||
```bash
|
||||
dhall-to-yaml --omitNull < ingress.yaml.dhall
|
||||
```
|
||||
|
||||
And we get:
|
||||
```yaml
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
[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
|
||||
|
@ -1,25 +0,0 @@
|
||||
let name = "nginx" in
|
||||
let port = 80 in
|
||||
{
|
||||
apiVersion = "apps/v1beta1",
|
||||
kind = "Deployment",
|
||||
metadata = { name = name },
|
||||
spec = {
|
||||
template = {
|
||||
metadata = {
|
||||
labels = {
|
||||
app = name,
|
||||
},
|
||||
},
|
||||
spec = {
|
||||
containers = [
|
||||
{
|
||||
name = name,
|
||||
image = "${name}:1.7.9",
|
||||
ports = [ {containerPort = port } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
apiVersion = ["v1beta2"] : Optional Text,
|
||||
kind = ["Deployment"] : Optional Text,
|
||||
metadata = [] : Optional ./out/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall ,
|
||||
spec = [] : Optional ./out/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall ,
|
||||
status = [] : Optional ./out/io.k8s.api.apps.v1beta2.DeploymentStatus.dhall ,
|
||||
} : ./out/io.k8s.api.apps.v1beta2.Deployment.dhall
|
Loading…
Reference in New Issue
Block a user