mirror of
https://github.com/dhall-lang/dhall-kubernetes.git
synced 2024-08-14 16:30:28 +03:00
Use generated docs
This commit is contained in:
parent
4778e2d41e
commit
a3414f21a6
7
docs/BUILD.md
Normal file
7
docs/BUILD.md
Normal file
@ -0,0 +1,7 @@
|
||||
# How to build docs
|
||||
```
|
||||
$ nix-shell
|
||||
$ make
|
||||
$ mkdocs build
|
||||
$ mkdocs gh-deploy
|
||||
```
|
24
docs/Makefile
Normal file
24
docs/Makefile
Normal file
@ -0,0 +1,24 @@
|
||||
docs := $(patsubst %.md.dhall,%.md, $(wildcard docs/*.md.dhall))
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
.PHONY: build default deploy install serve
|
||||
default: build
|
||||
|
||||
%.md: %.md.dhall
|
||||
dhall-to-text < $< > $@
|
||||
|
||||
build: mkdocs.yml ${docs}
|
||||
mkdocs build
|
||||
|
||||
serve: ${docs}
|
||||
mkdocs serve
|
||||
|
||||
deploy: build
|
||||
mkdocs gh-deploy
|
||||
|
||||
install: build
|
||||
cp -r site/* ${out}
|
||||
|
||||
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
# Apply
|
@ -1 +0,0 @@
|
||||
# Config
|
@ -1,341 +0,0 @@
|
||||
# Images
|
||||
# Index
|
||||
# `dhall-kubernetes`
|
||||
|
||||
`dhall-kubernetes` contains [Dhall][dhall-lang] bindings to [Kubernetes][kubernetes],
|
||||
so you can generate Kubernetes objects definitions from Dhall expressions.
|
||||
This will let you easily typecheck, template and modularize your Kubernetes definitions.
|
||||
|
||||
## Why do I need this
|
||||
|
||||
Once you build a slightly non-trivial Kubernetes setup, with many objects floating
|
||||
around, you'll encounter several issues:
|
||||
1. Writing the definitions in YAML is really verbose, and the actually important
|
||||
things don't stand out that much
|
||||
2. Ok I have a bunch of objects that'll need to be configured together, how do I share data?
|
||||
3. I'd like to reuse an object for different environments, but I cannot make it parametric..
|
||||
4. In general, I'd really love to reuse parts of some definitions in other definitions
|
||||
5. Oh no, I typoed a key and I had to wait until I pushed to the cluster to get an error back :(
|
||||
|
||||
The natural tendency is to reach for a templating language + a programming language to orchestrate that + some more configuration for it...
|
||||
But this is just really messy (been there), and we can do better.
|
||||
|
||||
Dhall solves all of this, being a programming language with builtin templating,
|
||||
all while being non-Turing complete, strongly typed and [strongly normalizing][normalization]
|
||||
(i.e.: reduces everything to a normal form, no matter how much abstraction you build),
|
||||
so saving you from the *"oh-noes-I-made-my-config-in-code-and-now-its-too-abstract"* nightmare.
|
||||
|
||||
For a Dhall Tutorial, see the [readme of the project][dhall-lang],
|
||||
or the [full tutorial][dhall-tutorial].
|
||||
|
||||
## Prerequisites
|
||||
|
||||
**NOTE**: `dhall-kubernetes` requires at least version `1.20.1` of [the interpreter](https://github.com/dhall-lang/dhall-haskell)
|
||||
(version `5.0.0` of the language).
|
||||
|
||||
You can install the latest version with the following:
|
||||
```bash
|
||||
stack install dhall-1.20.1 dhall-json-1.2.6 --resolver=nightly-2019-01-17
|
||||
```
|
||||
|
||||
## Quickstart - main API
|
||||
|
||||
We provide a simple API for the most common cases (For a list, see the [api](./api) folder).
|
||||
|
||||
Let's say we'd like to configure a Deployment exposing an `nginx` webserver.
|
||||
In the following example, we:
|
||||
1. Define a `config` for our service, by merging a [default config][default-deployment]
|
||||
(with the Dhall record-merge operator `//`) with a record with our parameters.
|
||||
2. In there we define the details of the Deployment we care about (note that we do the same
|
||||
"merging with defaults" operation for our container as well, so we don't have to specify
|
||||
all the parameters)
|
||||
3. We call the [`mkDeployment`][mkDeployment] function on our `config`
|
||||
|
||||
```haskell
|
||||
-- examples/deployment.dhall
|
||||
let config =
|
||||
../api/Deployment/default
|
||||
//
|
||||
{ name = "nginx"
|
||||
, replicas = 2
|
||||
, containers =
|
||||
[ ../api/Deployment/defaultContainer
|
||||
//
|
||||
{ name = "nginx"
|
||||
, imageName = "nginx"
|
||||
, imageTag = "1.15.3"
|
||||
, port = [ 80 ] : Optional Natural
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
in ../api/Deployment/mkDeployment config
|
||||
|
||||
```
|
||||
|
||||
We then run this through `dhall-to-yaml` to generate our Kubernetes definition:
|
||||
|
||||
```bash
|
||||
dhall-to-yaml --omitNull < deployment.dhall
|
||||
```
|
||||
|
||||
And we get:
|
||||
```yaml
|
||||
## examples/out/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
revisionHistoryLimit: 20
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 5
|
||||
maxUnavailable: 0
|
||||
type: RollingUpdate
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.15.3
|
||||
imagePullPolicy: Always
|
||||
env: []
|
||||
volumeMounts: []
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
requests:
|
||||
cpu: 10m
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
volumes: []
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app: nginx
|
||||
replicas: 2
|
||||
metadata:
|
||||
name: nginx
|
||||
|
||||
```
|
||||
|
||||
## Advanced usage - raw API
|
||||
|
||||
If the main API is not enough (e.g. the object you'd like to generate is not in the list),
|
||||
you can just fall back on using the raw Types and defaults the library provides
|
||||
(and Pull Request here your program afterwards!).
|
||||
|
||||
Let's say we want to generate an Ingress definition (for an [Nginx Ingress][nginx-ingress])
|
||||
that contains TLS certs and routes for every service.
|
||||
For more examples of using this API see the [`./examples` folder](./examples).
|
||||
|
||||
In the [`types`](./types) folder you'll find the types for the Kubernetes definitions. E.g.
|
||||
[here's][Ingress] the type for the Ingress.
|
||||
|
||||
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`](./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 Ingress is [this
|
||||
function][Ingress-default].
|
||||
|
||||
Let's say we have a Service with the following configuration:
|
||||
|
||||
```haskell
|
||||
-- examples/myConfig.dhall
|
||||
{ name = "foo"
|
||||
, host = "foo.example.com"
|
||||
, version = "1.0.1"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
That has the following type:
|
||||
```haskell
|
||||
-- examples/Config.dhall
|
||||
{ name : Text
|
||||
, host : Text
|
||||
, version : Text
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We can now expose this service out to the world with the Ingress:
|
||||
|
||||
```haskell
|
||||
-- examples/ingressRaw.dhall
|
||||
|
||||
|
||||
-- Prelude imports
|
||||
let map = (../Prelude.dhall).`List`.map
|
||||
|
||||
-- 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
|
||||
in let IntOrString = ../types/io.k8s.apimachinery.pkg.util.intstr.IntOrString.dhall
|
||||
|
||||
-- Our Service type
|
||||
in let Service = ./Config.dhall
|
||||
in let Config = { services : List Service }
|
||||
|
||||
-- A function to generate an ingress given a configuration
|
||||
in let mkIngress : Config -> Ingress =
|
||||
|
||||
\(config : Config) ->
|
||||
|
||||
-- Given a service, make a TLS definition with their host and certificate
|
||||
let makeTLS = \(service : Service) ->
|
||||
{ hosts = Some [ service.host ]
|
||||
, secretName = Some "${service.name}-certificate"
|
||||
}
|
||||
|
||||
-- Given a service, make an Ingress Rule
|
||||
in let makeRule = \(service : Service) ->
|
||||
{ host = Some service.host
|
||||
, http = Some
|
||||
{ paths = [ { backend =
|
||||
{ serviceName = service.name
|
||||
, servicePort = IntOrString.Int 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 services = config.services # [ 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
|
||||
[ kv "kubernetes.io/ingress.class" "nginx"
|
||||
, kv "kubernetes.io/ingress.allow-http" "false"
|
||||
]
|
||||
|
||||
-- Generate spec from services
|
||||
in let spec = defaultSpec //
|
||||
{ tls = Some (map Service TLS makeTLS services)
|
||||
, rules = Some (map Service Rule makeRule services)
|
||||
}
|
||||
|
||||
in defaultIngress
|
||||
{ metadata = defaultMeta
|
||||
{ name = "nginx" } //
|
||||
{ annotations = annotations }
|
||||
} //
|
||||
{ spec = Some spec }
|
||||
|
||||
|
||||
-- Here we import our example service, and generate the ingress with it
|
||||
in mkIngress { services = [ ./myConfig.dhall ] }
|
||||
|
||||
```
|
||||
|
||||
As before we get the yaml out by running:
|
||||
|
||||
```bash
|
||||
dhall-to-yaml --omitNull < ingress.yaml.dhall
|
||||
```
|
||||
|
||||
Result:
|
||||
```yaml
|
||||
## examples/out/ingressRaw.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
|
||||
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Updating the nixpkgs snapshot (and kubernetes version)
|
||||
|
||||
Run
|
||||
```bash
|
||||
./scripts/update-nixpkgs.sh
|
||||
./generate.sh
|
||||
```
|
||||
If the tests fail, rollback. If they don't then you have sucessfully upgraded!
|
||||
|
||||
### 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 the following command:
|
||||
|
||||
```bash
|
||||
nix build --file ./release.nix
|
||||
```
|
||||
|
||||
### Generating `types` `default` and `README.md`
|
||||
|
||||
Running `scripts/generate.sh` will generate all dhall files from the kubernetes
|
||||
swagger specification, and copy them to `types` and `default`. It will also
|
||||
generate `README.md` from `docs/README.md.dhall`.
|
||||
|
||||
If you make changes to `scripts/convert.py` or `docs/README.md.dhall`, you need
|
||||
to run this command afterwards.
|
||||
|
||||
|
||||
## Projects Using `dhall-kubernetes`
|
||||
|
||||
* [dhall-prometheus-operator][dhall-prometheus-operator]: Provides types and default records for [Prometheus Operators][prometheus-operator].
|
||||
|
||||
|
||||
[hydra-project]: http://hydra.dhall-lang.org/project/dhall-kubernetes
|
||||
[dhall-lang]: https://github.com/dhall-lang/dhall-lang
|
||||
[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.17.0/docs/Dhall-Tutorial.html
|
||||
[default-deployment]: ./api/Deployment/default
|
||||
[mkDeployment]: ./api/Deployment/mkDeployment
|
||||
[Ingress]: ./types/io.k8s.api.extensions.v1beta1.Ingress.dhall
|
||||
[Ingress-default]: ./default/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
|
@ -1,4 +1,3 @@
|
||||
# Index
|
||||
# `dhall-kubernetes`
|
||||
|
||||
`dhall-kubernetes` contains [Dhall][dhall-lang] bindings to [Kubernetes][kubernetes],
|
||||
|
1
docs/docs/index.md.dhall
Normal file
1
docs/docs/index.md.dhall
Normal file
@ -0,0 +1 @@
|
||||
./README.md.dhall
|
@ -1 +0,0 @@
|
||||
# Intro
|
@ -1 +0,0 @@
|
||||
# Projects
|
@ -6,9 +6,7 @@ theme:
|
||||
custom_dir: theme
|
||||
nav:
|
||||
- Overview: index.md
|
||||
- User Guide:
|
||||
- Introduction: intro.md
|
||||
- Apply: apply.md
|
||||
- Secrets and ConfigMaps: config.md
|
||||
- Container Images: images.md
|
||||
- Projects using Dhall: projects.md
|
||||
# - Tutorial:
|
||||
# - Step 1:
|
||||
# - Step 2:
|
||||
#
|
||||
|
1
docs/theme/dhall-kubernetes-logo.svg
vendored
Symbolic link
1
docs/theme/dhall-kubernetes-logo.svg
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../../logo/dhall-kubernetes-logo.svg
|
Loading…
Reference in New Issue
Block a user