diff --git a/Dockerfile b/Dockerfile index 28e24ef..898ee5b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,10 @@ FROM xena/dhall-yaml AS dyaml -FROM xena/alpine AS drone +FROM xena/alpine COPY --from=dyaml /bin/dhall-to-yaml-ng /usr/local/bin/dhall-to-yaml WORKDIR /drone COPY ./drone . RUN dhall-to-yaml --file pipeline.dhall +WORKDIR /k8s +COPY ./k8s . +RUN cd apps && dhall-to-yaml --file hlang.dhall diff --git a/k8s/Prelude.dhall b/k8s/Prelude.dhall new file mode 100644 index 0000000..b15a871 --- /dev/null +++ b/k8s/Prelude.dhall @@ -0,0 +1 @@ +https://prelude.dhall-lang.org/package.dhall sha256:4aa8581954f7734d09b7b21fddbf5d8df901a44b54b4ef26ea71db92de0b1a12 diff --git a/k8s/app/config.dhall b/k8s/app/config.dhall new file mode 100644 index 0000000..1f0a6d4 --- /dev/null +++ b/k8s/app/config.dhall @@ -0,0 +1,19 @@ +let kubernetes = ../kubernetes.dhall + +in { Type = + { name : Text + , appPort : Natural + , image : Text + , domain : Text + , leIssuer : Text + , envVars : List kubernetes.EnvVar.Type + } + , default = + { name = "" + , appPort = 5000 + , image = "" + , domain = "" + , leIssuer = "staging" + , envVars = [] : List kubernetes.EnvVar.Type + } + } diff --git a/k8s/app/list.dhall b/k8s/app/list.dhall new file mode 100644 index 0000000..66d57e0 --- /dev/null +++ b/k8s/app/list.dhall @@ -0,0 +1,6 @@ +let typesUnion = ../typesUnion.dhall + +in { Type = { apiVersion : Text, kind : Text, items : List typesUnion } + , default = + { apiVersion = "v1", kind = "List", items = [] : List typesUnion } + } diff --git a/k8s/app/make.dhall b/k8s/app/make.dhall new file mode 100644 index 0000000..0f67cbb --- /dev/null +++ b/k8s/app/make.dhall @@ -0,0 +1,33 @@ +let Prelude = ../Prelude.dhall + +let kubernetes = ../kubernetes.dhall + +let typesUnion = ../typesUnion.dhall + +let deployment = ../http/deployment.dhall + +let ingress = ../http/ingress.dhall + +let service = ../http/service.dhall + +let Config = ../app/config.dhall + +let K8sList = ../app/list.dhall + +let buildService = + λ(config : Config.Type) + → let myService = service config + + let myDeployment = deployment config + + let myIngress = ingress config + + in K8sList::{ + , items = + [ typesUnion.Service myService + , typesUnion.Deployment myDeployment + , typesUnion.Ingress myIngress + ] + } + +in buildService diff --git a/k8s/apps/hlang.dhall b/k8s/apps/hlang.dhall new file mode 100644 index 0000000..9322542 --- /dev/null +++ b/k8s/apps/hlang.dhall @@ -0,0 +1,14 @@ +let makeApp = ../app/make.dhall + +let Config = ../app/config.dhall + +let cfg = + Config::{ + , name = "hlang" + , appPort = 5000 + , image = "xena/hlang:latest" + , domain = "h.christine.website" + , leIssuer = "prod" + } + +in makeApp cfg diff --git a/k8s/http/deployment.dhall b/k8s/http/deployment.dhall new file mode 100644 index 0000000..c079e9e --- /dev/null +++ b/k8s/http/deployment.dhall @@ -0,0 +1,51 @@ +let Prelude = ../Prelude.dhall + +let kubernetes = ../kubernetes.dhall + +let kv = Prelude.JSON.keyText + +let Config = ../app/config.dhall + +let selector = ./selector.dhall + +let podSpec + : Config.Type → kubernetes.PodSpec.Type + = λ(config : Config.Type) + → kubernetes.PodSpec::{ + , containers = + [ kubernetes.Container::{ + , name = "web" + , env = config.envVars + , image = Some config.image + , imagePullPolicy = Some "Always" + , ports = + [ kubernetes.ContainerPort::{ containerPort = config.appPort } ] + } + ] + , imagePullSecrets = + [ kubernetes.LocalObjectReference::{ name = Some "regcred" } ] + } + +let spec = + λ(config : Config.Type) + → kubernetes.DeploymentSpec::{ + , selector = kubernetes.LabelSelector::{ + , matchLabels = selector config.name + } + , template = kubernetes.PodTemplateSpec::{ + , metadata = kubernetes.ObjectMeta::{ + , name = config.name + , labels = selector config.name + } + , spec = Some (podSpec config) + } + } + +let deployment = + λ(config : Config.Type) + → kubernetes.Deployment::{ + , metadata = kubernetes.ObjectMeta::{ name = config.name } + , spec = Some (spec config) + } + +in deployment diff --git a/k8s/http/ingress.dhall b/k8s/http/ingress.dhall new file mode 100644 index 0000000..2e1dfe7 --- /dev/null +++ b/k8s/http/ingress.dhall @@ -0,0 +1,63 @@ +let Prelude = ../Prelude.dhall + +let kubernetes = ../kubernetes.dhall + +let kv = Prelude.JSON.keyText + +let Config = ../app/config.dhall + +let annotations + : Config.Type → List { mapKey : Text, mapValue : Text } + = λ(config : Config.Type) + → [ kv "kubernetes.io/ingress.class" "nginx" + , kv + "certmanager.k8s.io/cluster-issuer" + "letsencrypt-${config.leIssuer}" + ] + +let metadata + : Config.Type → kubernetes.ObjectMeta.Type + = λ(config : Config.Type) + → kubernetes.ObjectMeta::{ + , name = config.name + , labels = [ kv "app" config.name ] + , annotations = annotations config + } + +let tls + : Config.Type → kubernetes.IngressTLS.Type + = λ(config : Config.Type) + → kubernetes.IngressTLS::{ + , hosts = [ config.domain ] + , secretName = Some "${config.leIssuer}-certs-${config.name}" + } + +let rule + : Config.Type → kubernetes.IngressRule.Type + = λ(config : Config.Type) + → kubernetes.IngressRule::{ + , host = Some config.domain + , http = Some kubernetes.HTTPIngressRuleValue::{ + , paths = + [ kubernetes.HTTPIngressPath::{ + , backend = kubernetes.IngressBackend::{ + , serviceName = config.name + , servicePort = kubernetes.IntOrString.Int config.appPort + } + } + ] + } + } + +let ingress + : Config.Type → kubernetes.Ingress.Type + = λ(config : Config.Type) + → kubernetes.Ingress::{ + , metadata = metadata config + , spec = Some kubernetes.IngressSpec::{ + , tls = [ tls config ] + , rules = [ rule config ] + } + } + +in ingress diff --git a/k8s/http/selector.dhall b/k8s/http/selector.dhall new file mode 100644 index 0000000..582610e --- /dev/null +++ b/k8s/http/selector.dhall @@ -0,0 +1,9 @@ +let Prelude = ../Prelude.dhall + +let kv = Prelude.JSON.keyText + +let selector + : Text → List { mapKey : Text, mapValue : Text } + = λ(appName : Text) → [ kv "app" appName ] + +in selector diff --git a/k8s/http/service.dhall b/k8s/http/service.dhall new file mode 100644 index 0000000..cdc561f --- /dev/null +++ b/k8s/http/service.dhall @@ -0,0 +1,45 @@ +let Prelude = ../Prelude.dhall + +let Config = ../app/config.dhall + +let kubernetes = ../kubernetes.dhall + +let kv = Prelude.JSON.keyText + +let annotations + : Config.Type → List { mapKey : Text, mapValue : Text } + = λ(config : Config.Type) + → [ kv "external-dns.alpha.kubernetes.io/hostname" config.domain + , kv "external-dns.alpha.kubernetes.io/ttl" "120" + , kv "external-dns.alpha.kubernetes.io/cloudflare-proxied" "false" + ] + +let selector = ./selector.dhall + +let spec + : Config.Type → kubernetes.ServiceSpec.Type + = λ(config : Config.Type) + → kubernetes.ServiceSpec::{ + , selector = selector config.name + , type = Some "ClusterIP" + , ports = + [ kubernetes.ServicePort::{ + , targetPort = Some (kubernetes.IntOrString.Int config.appPort) + , port = config.appPort + } + ] + } + +let service + : Config.Type → kubernetes.Service.Type + = λ(config : Config.Type) + → kubernetes.Service::{ + , metadata = kubernetes.ObjectMeta::{ + , name = config.name + , labels = [ kv "app" config.name ] + , annotations = annotations config + } + , spec = Some (spec config) + } + +in service diff --git a/k8s/kubernetes.dhall b/k8s/kubernetes.dhall new file mode 100644 index 0000000..8e63946 --- /dev/null +++ b/k8s/kubernetes.dhall @@ -0,0 +1 @@ +https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/1.15/package.dhall sha256:4bd5939adb0a5fc83d76e0d69aa3c5a30bc1a5af8f9df515f44b6fc59a0a4815 diff --git a/k8s/main.dhall b/k8s/main.dhall new file mode 100644 index 0000000..348839f --- /dev/null +++ b/k8s/main.dhall @@ -0,0 +1,42 @@ +let Prelude = + https://prelude.dhall-lang.org/package.dhall sha256:4aa8581954f7734d09b7b21fddbf5d8df901a44b54b4ef26ea71db92de0b1a12 + +let kubernetes = ./kubernetes.dhall + +let kv = Prelude.JSON.keyText + +let appName = env:APP_NAME as Text ? "nginx" + +let imageName = env:IMAGE_NAME as Text ? "nginx" + +let imageTag = env:IMAGE_TAG as Text ? "latest" + +let deployment = + kubernetes.Deployment::{ + , metadata = kubernetes.ObjectMeta::{ name = appName } + , spec = Some kubernetes.DeploymentSpec::{ + , replicas = Some 2 + , revisionHistoryLimit = Some 10 + , selector = kubernetes.LabelSelector::{ + , matchLabels = [ kv "app" appName ] + } + , template = kubernetes.PodTemplateSpec::{ + , metadata = kubernetes.ObjectMeta::{ + , name = "nginx" + , labels = [ kv "app" appName ] + } + , spec = Some kubernetes.PodSpec::{ + , containers = + [ kubernetes.Container::{ + , name = "web" + , image = Some "${imageName}:${imageTag}" + , imagePullPolicy = Some "Always" + , ports = [ kubernetes.ContainerPort::{ containerPort = 80 } ] + } + ] + } + } + } + } + +in deployment diff --git a/k8s/typesUnion.dhall b/k8s/typesUnion.dhall new file mode 100644 index 0000000..a9d5ef6 --- /dev/null +++ b/k8s/typesUnion.dhall @@ -0,0 +1 @@ +https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/1.15/typesUnion.dhall sha256:930a34021bd380209b6763d4a98fb16e1f5608fe5e8d1403d0ec03f6c4a08f00