Compare commits
131 commits
Author | SHA1 | Date | |
---|---|---|---|
f24b7418e6 | |||
6a6e51d694 | |||
cf0403b9d7 | |||
b37661f155 | |||
f4f2e97ebb | |||
fa6a5011d8 | |||
d7e2c2536a | |||
94bfec3da9 | |||
|
5ae28c6af5 | ||
|
a94e8165de | ||
|
14fe8d8ee0 | ||
485dd40abd | |||
|
fb16a34a75 | ||
|
1bcbca58c8 | ||
|
b62fa7b9ec | ||
|
2154bed190 | ||
|
57a9340109 | ||
|
74d5a63bea | ||
|
7fc4380bb0 | ||
|
52e3ba6ae1 | ||
|
ca8ab8d708 | ||
0085111abe | |||
19106937a0 | |||
baaf26a0d6 | |||
30d7404ce9 | |||
30be8cf4d3 | |||
743418e6b1 | |||
8d124bbf81 | |||
b0faeab079 | |||
1f9b434154 | |||
4743d8f434 | |||
d8ed73cb00 | |||
4dfdfdcbb5 | |||
4cbaac8d25 | |||
220aef9d5c | |||
08fc72a406 | |||
081f137414 | |||
b35caa02f1 | |||
7c4dc29651 | |||
3756a5bc63 | |||
acb8b1e7bf | |||
169a51456b | |||
a9fa3f21e7 | |||
65e9c154a7 | |||
d2bf694e4e | |||
dd897c7364 | |||
0b647ed4d8 | |||
2008322368 | |||
b4eb47d584 | |||
b11f3edd61 | |||
f33b86125a | |||
b2ce1fa75e | |||
1c0537f8e9 | |||
50888233e0 | |||
f2925a2991 | |||
efdb2051c5 | |||
704b5cdcc8 | |||
4fdf7d7de3 | |||
468c76e902 | |||
99cbc09340 | |||
7469fc1cd4 | |||
552dcbd1d8 | |||
5d4f022145 | |||
|
c56efba544 | ||
|
515813ce5c | ||
|
4f071f14c8 | ||
|
26ca2acb6b | ||
|
231002017f | ||
|
8449548d56 | ||
|
3026900b98 | ||
4bc93285b8 | |||
f334756d91 | |||
0594cfbec4 | |||
b21e377168 | |||
73a9965f96 | |||
c1f2cefd14 | |||
a1a49e10d0 | |||
0cd4f22823 | |||
7e9c813e33 | |||
674045eba3 | |||
cca93311e4 | |||
8626b91b16 | |||
a14b7640da | |||
ab2473bdf4 | |||
b4718500b1 | |||
6794a24f0f | |||
73e2ebd12f | |||
866ef84f12 | |||
fe43c0ec58 | |||
781ae8b0c5 | |||
a334dff58b | |||
c7fab83c39 | |||
6b79f2599d | |||
a68ff0bd14 | |||
ef4d35b6f5 | |||
6b59d288d8 | |||
0a84b7ad63 | |||
b706139d4f | |||
a81d5b1efc | |||
b5965cee3e | |||
0e20df8ff0 | |||
8b4e431418 | |||
7c21211eac | |||
b8345402d1 | |||
0caec73a1d | |||
71d0ea7d8e | |||
|
0564dd4850 | ||
|
e0e6360853 | ||
1227a8a76f | |||
d7ed1e00bd | |||
c8b3574ef7 | |||
bfa2007459 | |||
a9fa5e4a35 | |||
abfe467382 | |||
52d5263b86 | |||
0da8216e6c | |||
dfc65cacc4 | |||
db7e4a391d | |||
2c3525cab6 | |||
f147947558 | |||
966ecf1009 | |||
8189206ec6 | |||
7eae8766aa | |||
d7c2852f28 | |||
4b182a618c | |||
c549f2d5fd | |||
6d96d0e466 | |||
|
f143587c95 | ||
630ad9b3d6 | |||
9500ed2ebf | |||
fdb24a4ad5 |
65 changed files with 1919 additions and 606 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -27,3 +27,4 @@ my-auth.edn
|
|||
|
||||
.clj-kondo/
|
||||
.lsp/
|
||||
.eastwood
|
||||
|
|
|
@ -5,7 +5,7 @@ stages:
|
|||
- upload
|
||||
|
||||
.cljs-job: &cljs
|
||||
image: domaindrivenarchitecture/shadow-cljs
|
||||
image: "domaindrivenarchitecture/ddadevops-clj-cljs:4.11.4"
|
||||
cache:
|
||||
key: ${CI_COMMIT_REF_SLUG}
|
||||
paths:
|
||||
|
@ -13,46 +13,49 @@ stages:
|
|||
- .shadow-cljs/
|
||||
- .m2
|
||||
before_script:
|
||||
- export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW
|
||||
- echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
|
||||
- npm install
|
||||
|
||||
.clj-job: &clj
|
||||
image: domaindrivenarchitecture/lein
|
||||
image: "domaindrivenarchitecture/ddadevops-clj:4.11.4"
|
||||
cache:
|
||||
key: ${CI_COMMIT_REF_SLUG}
|
||||
paths:
|
||||
- .m2
|
||||
before_script:
|
||||
- export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW
|
||||
- mkdir -p /root/.lein
|
||||
- echo "{:auth {:repository-auth {#\"clojars\" {:username \"${CLOJARS_USER}\" :password \"${CLOJARS_TOKEN_DOMAINDRIVENARCHITECTURE}\" }}}}" > ~/.lein/profiles.clj
|
||||
|
||||
.tag_only: &tag_only
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
when: never
|
||||
- if: '$CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+$/'
|
||||
|
||||
test-clj:
|
||||
<<: *clj
|
||||
stage: build_and_test
|
||||
script:
|
||||
- lein test
|
||||
- pyb test_clj
|
||||
|
||||
test-cljs:
|
||||
<<: *cljs
|
||||
stage: build_and_test
|
||||
script:
|
||||
- shadow-cljs compile test
|
||||
- node target/node-tests.js
|
||||
- pyb test_cljs
|
||||
|
||||
upload-clj-release:
|
||||
<<: *clj
|
||||
<<: *tag_only
|
||||
stage: upload
|
||||
rules:
|
||||
- if: '$CI_COMMIT_TAG != null'
|
||||
script:
|
||||
- lein deploy
|
||||
- pyb upload_clj
|
||||
|
||||
upload-cljs-release:
|
||||
<<: *clj
|
||||
<<: *tag_only
|
||||
stage: upload
|
||||
rules:
|
||||
- if: '$CI_COMMIT_TAG != null'
|
||||
script:
|
||||
- mv project.clj project-clj.clj && mv project-cljs.clj project.clj
|
||||
- lein deploy
|
||||
|
||||
- pyb upload_cljs
|
||||
|
|
458
README.md
458
README.md
|
@ -1,86 +1,408 @@
|
|||
# convention 4 kubernetes: c4k-common
|
||||
[![Clojars Project](https://img.shields.io/clojars/v/org.domaindrivenarchitecture/c4k-common-clj.svg)](https://clojars.org/org.domaindrivenarchitecture/c4k-common-clj) [![Clojars Project](https://img.shields.io/clojars/v/org.domaindrivenarchitecture/c4k-common-cljs.svg)](https://clojars.org/org.domaindrivenarchitecture/c4k-common-cljs) [![pipeline status](https://gitlab.com/domaindrivenarchitecture/c4k-common/badges/master/pipeline.svg)](https://gitlab.com/domaindrivenarchitecture/c4k-common/-/commits/master)
|
||||
|
||||
[<img src="https://domaindrivenarchitecture.org/img/delta-chat.svg" width=20 alt="DeltaChat"> chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [<img src="https://meissa-gmbh.de/img/community/Mastodon_Logotype.svg" width=20 alt="team@social.meissa-gmbh.de"> team@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@team) | [Website & Blog](https://domaindrivenarchitecture.org)
|
||||
|
||||
## Purpose
|
||||
|
||||
c4k-common provides the foundation for all our c4k modules.
|
||||
|
||||
It is now possible to generate a working prometheus monitoring file in yaml format.
|
||||
Your config.edn and your auth.edn should at least contain the following fields:
|
||||
|
||||
config.edn - minimal example
|
||||
|
||||
```clojure
|
||||
{:k3s-cluster-name "your-cluster-name"
|
||||
:k3s-cluster-stage :prod
|
||||
:grafana-cloud-url "your-url"}
|
||||
```
|
||||
|
||||
auth.edn - minimal example
|
||||
|
||||
```clojure
|
||||
{:grafana-cloud-user "user"
|
||||
:grafana-cloud-password "password"}
|
||||
```
|
||||
|
||||
call (with jarwrapper installed):
|
||||
|
||||
```bash
|
||||
c4k-common-standalone.jar config.edn auth.edn > monitoring.yaml
|
||||
```
|
||||
|
||||
[<img src="https://domaindrivenarchitecture.org/img/delta-chat.svg" width=20 alt="DeltaChat"> chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [<img src="https://meissa.de/images/parts/contact/mastodon36_hue9b2464f10b18e134322af482b9c915e_5501_filter_14705073121015236177.png" width=20 alt="M"> meissa@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@meissa) | [Blog](https://domaindrivenarchitecture.org) | [Website](https://meissa.de)
|
||||
|
||||
## Rationale
|
||||
|
||||
There are many comparable solutions for creating c4k deployments like helm or kustomize. Why do we need another one?
|
||||
* We like the simplicity of kustomize. Yaml in, yaml out, the ability to lint the result and the option to split large yaml files into objects. But a simple overwriting per environment may not be enough ...
|
||||
* We like helm packages. A package encapsulates the setup for an application. On the one hand, but on the other hand we don't like the idea of having to program and debug in a template language. We can program much better in real programming languages.
|
||||
There are many comparable solutions for creating c4k deployments like `helm` or `kustomize`.
|
||||
`kustomize` is great to manage your k8s manifests by splitting huge files into handy parts.
|
||||
`helm` is great because of its large community.
|
||||
|
||||
Our convention 4 kubernetes c4k-* tools combine the advantages of both approaches:
|
||||
* Packages for one application
|
||||
* Programming in clojure
|
||||
* yaml / edn as input and output, no more magic
|
||||
* good validation, integration as api, cli or in the browser
|
||||
Why do we need another one? Why do you continue the reading here?
|
||||
|
||||
## Usage
|
||||
We combine the simplicity of `kustomize` with the ability to do real programming like software developers would do.
|
||||
|
||||
c4k-common provides the basic functionality for our c4k-modules.
|
||||
Following the principle
|
||||
|
||||
"Use programming language for programming"
|
||||
|
||||
we clearly enjoy writing Kubernetes manifests with Clojure. In comparison to helms templating, things such as business logic, conventions, input validation, versions, dependencies and reuse are much easier and much more reliable to implement with c4k.
|
||||
|
||||
By the way, c4k means "convention for kubernetes".
|
||||
|
||||
### Features
|
||||
|
||||
c4k-common supports the following use cases:
|
||||
|
||||
- [convention 4 kubernetes: c4k-common](#convention-4-kubernetes-c4k-common)
|
||||
- [Rationale](#rationale)
|
||||
- [Features](#features)
|
||||
- [Target CLI and Web Frontend](#target-cli-and-web-frontend)
|
||||
- [Separate Configuration From Credentials](#separate-configuration-from-credentials)
|
||||
- [Input as EDN or Yaml](#input-as-edn-or-yaml)
|
||||
- [Inline k8s Resources for Versioning \& Dependencies](#inline-k8s-resources-for-versioning--dependencies)
|
||||
- [Work on Structured Data Instead of Flat Templating](#work-on-structured-data-instead-of-flat-templating)
|
||||
- [Validate Your Inputs](#validate-your-inputs)
|
||||
- [Namespaces](#namespaces)
|
||||
- [Ingress](#ingress)
|
||||
- [Postgres Database](#postgres-database)
|
||||
- [Monitoring With Grafana Cloud](#monitoring-with-grafana-cloud)
|
||||
- [Refactoring \& Module Overview](#refactoring--module-overview)
|
||||
- [Development \& Mirrors](#development--mirrors)
|
||||
- [License](#license)
|
||||
|
||||
#### Target CLI and Web Frontend
|
||||
|
||||
To create your own c4k module set up your cli analogous to the following:
|
||||
|
||||
```clojure
|
||||
(defn -main [& cmd-args]
|
||||
(uberjar/main-common
|
||||
"c4k-forgejo" ;; name of your app
|
||||
core/config? ;; schema for config validation
|
||||
core/auth? ;; schema for credential validation
|
||||
core/config-defaults ;; want to set default values?
|
||||
core/k8s-objects ;; the function generate the k8s manifest
|
||||
cmd-args ;; command line arguments given
|
||||
))
|
||||
```
|
||||
|
||||
The full example can be found here: https://repo.prod.meissa.de/meissa/c4k-forgejo/src/branch/main/src/main/clj/dda/c4k_forgejo/uberjar.clj
|
||||
|
||||
With c4k instead of using cli to generate manifests, you can also generate your manifests via web-application (using page local js without server interaction)
|
||||
|
||||
```html
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link href="bootstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="c4k-content"></div>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
[![Try it out](doc/tryItOut.png "Try out yourself")](https://domaindrivenarchitecture.org/pages/dda-provision/c4k-forgejo/)
|
||||
|
||||
See: https://repo.prod.meissa.de/meissa/c4k-forgejo/src/branch/main/public/index.html
|
||||
|
||||
and: https://repo.prod.meissa.de/meissa/c4k-forgejo/src/branch/main/src/main/cljs/dda/c4k_forgejo/browser.cljs
|
||||
|
||||
#### Separate Configuration From Credentials
|
||||
|
||||
We think it is a good idea to have credentials separated from configuration. All our functions, cli and frontend are following this principle. Thus, for executing separated config and authentication infos have to be provided.
|
||||
|
||||
The following command line yields the resulting k8s manifests in `k8s-manifest.yaml`:
|
||||
```bash
|
||||
java -jar c4k-common.jar config.edn auth.edn > k8s-manifest.yaml
|
||||
```
|
||||
|
||||
Using the tool `jarwrapper` the command line can even be shortened to:
|
||||
```bash
|
||||
c4k-common config.edn auth.edn > k8s-manifest.yaml
|
||||
```
|
||||
|
||||
#### Input as EDN or Yaml
|
||||
|
||||
c4k-common supports yaml and edn as format for all of its resources (input and output).
|
||||
Hence, the following command line will also work:
|
||||
|
||||
```bash
|
||||
c4k-common config.yaml auth.yaml > k8s-manifest.yaml
|
||||
```
|
||||
|
||||
#### Inline k8s Resources for Versioning & Dependencies
|
||||
|
||||
We inline all resources used in our libraries & applications. You can generate k8s manifests everywhere without additional external dependencies.
|
||||
|
||||
In case of
|
||||
* java: Resources are included in the jar-file out of the box (see https://repo.prod.meissa.de/meissa/c4k-forgejo/src/branch/main/project.clj#L13).
|
||||
* js: With a slim macro call we inline resources to the resulting js file (see https://repo.prod.meissa.de/meissa/c4k-forgejo/src/branch/main/src/main/cljc/dda/c4k_forgejo/forgejo.cljc#L72-L74)
|
||||
* native: On native builds we also inline resources (see https://repo.prod.meissa.de/meissa/c4k-forgejo/src/branch/main/build.py#L126)
|
||||
|
||||
#### Work on Structured Data Instead of Flat Templating
|
||||
|
||||
To keep things simple, we also do templating. But we convert given k8s resources to structured data.
|
||||
This allows us to have more control and do unit tests:
|
||||
|
||||
k8s-resource:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: ratelimit
|
||||
spec:
|
||||
rateLimit:
|
||||
average: AVG
|
||||
burst: BRS
|
||||
```
|
||||
|
||||
Replace values:
|
||||
|
||||
```clojure
|
||||
(defn-spec generate-rate-limit-middleware pred/map-or-seq?
|
||||
[config rate-limit-config?]
|
||||
(let [{:keys [max-rate max-concurrent-requests]} config]
|
||||
(->
|
||||
(yaml/load-as-edn "forgejo/middleware-ratelimit.yaml")
|
||||
(cm/replace-key-value :average max-rate)
|
||||
(cm/replace-key-value :burst max-concurrent-requests))))
|
||||
```
|
||||
|
||||
Have a unit-test:
|
||||
|
||||
```clojure
|
||||
(deftest should-generate-middleware-ratelimit
|
||||
(is (= {:apiVersion "traefik.containo.us/v1alpha1",
|
||||
:kind "Middleware",
|
||||
:metadata {:name "ratelimit"},
|
||||
:spec {:rateLimit {:average 10, :burst 5}}}
|
||||
(cut/generate-rate-limit-middleware {:max-rate 10, :max-concurrent-requests 5}))))
|
||||
```
|
||||
|
||||
#### Validate Your Inputs
|
||||
|
||||
Have you recognized the `defn-spec` macro above? We use allover validation, e.g.
|
||||
|
||||
```clojure
|
||||
(def rate-limit-config? (s/keys :req-un [::max-rate
|
||||
::max-concurrent-requests]))
|
||||
|
||||
(defn-spec generate-rate-limit-middleware pred/map-or-seq?
|
||||
[config rate-limit-config?]
|
||||
...)
|
||||
```
|
||||
|
||||
#### Namespaces
|
||||
|
||||
We support namespaces for ingress & postgres (monitoring lives in it's own namespace `monitoring`).
|
||||
|
||||
```clojure
|
||||
(dda.c4k-common.namespace/generate {:namespace "myapp"})
|
||||
```
|
||||
|
||||
yields:
|
||||
|
||||
```clojure
|
||||
[{:apiVersion "v1"
|
||||
:kind "Namespace"
|
||||
:metadata {:name "myapp"}}]
|
||||
```
|
||||
|
||||
which renders to:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: myapp
|
||||
```
|
||||
|
||||
#### Ingress
|
||||
|
||||
In most cases we use `generate-ingress-and-cert` which generates an ingress in combination with letsencrypt cert for a named service.
|
||||
|
||||
```clojure
|
||||
(dda.c4k-common.ingress/generate-ingress-and-cert
|
||||
{:fqdns ["test.jit.si"]
|
||||
:service-name "web"
|
||||
:service-port 80})
|
||||
```
|
||||
|
||||
yields:
|
||||
|
||||
```clojure
|
||||
[{:apiVersion "cert-manager.io/v1",
|
||||
:kind "Certificate",
|
||||
...
|
||||
:spec
|
||||
{:secretName "web",
|
||||
:commonName "test.jit.si",
|
||||
:duration "2160h",
|
||||
:renewBefore "720h",
|
||||
:dnsNames ["test.jit.si"],
|
||||
:issuerRef {:name "staging", :kind "ClusterIssuer"}}}
|
||||
{:apiVersion "networking.k8s.io/v1",
|
||||
:kind "Ingress",
|
||||
...
|
||||
:spec
|
||||
{:tls [{:hosts ["test.jit.si"], :secretName "web"}],
|
||||
:rules
|
||||
[{:host "test.jit.si",
|
||||
:http {:paths [{:path "/",
|
||||
:pathType "Prefix",
|
||||
:backend
|
||||
{:service {:name "web",
|
||||
:port {:number 80}}}}]}}]}}]
|
||||
```
|
||||
|
||||
which renders to:
|
||||
|
||||
```yaml
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
...
|
||||
spec:
|
||||
secretName: web
|
||||
commonName: test.jit.si
|
||||
duration: 2160h
|
||||
renewBefore: 720h
|
||||
dnsNames:
|
||||
- test.jit.si
|
||||
issuerRef:
|
||||
name: staging
|
||||
kind: ClusterIssuer
|
||||
|
||||
---
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
...
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- test.jit.si
|
||||
secretName: web
|
||||
rules:
|
||||
- host: test.jit.si
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: /
|
||||
backend:
|
||||
service:
|
||||
name: web
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
#### Postgres Database
|
||||
|
||||
If your application needs a database, we often use postgres:
|
||||
|
||||
```clojure
|
||||
(cut/generate-deployment {:postgres-image "postgres:16"})
|
||||
```
|
||||
|
||||
yields:
|
||||
|
||||
```clojure
|
||||
{:apiVersion "apps/v1",
|
||||
:kind "Deployment",
|
||||
...
|
||||
:spec
|
||||
{:selector {:matchLabels {:app "postgresql"}},
|
||||
:strategy {:type "Recreate"},
|
||||
:template
|
||||
{:metadata {:labels {:app "postgresql"}},
|
||||
:spec
|
||||
{:containers
|
||||
[{:image "postgres:16",
|
||||
:name "postgresql",
|
||||
:env
|
||||
[{:name "POSTGRES_USER", :valueFrom {:secretKeyRef {:name "postgres-secret", :key "postgres-user"}}}
|
||||
{:name "POSTGRES_PASSWORD", :valueFrom {:secretKeyRef {:name "postgres-secret", :key "postgres-password"}}}
|
||||
{:name "POSTGRES_DB", :valueFrom {:configMapKeyRef {:name "postgres-config", :key "postgres-db"}}}],
|
||||
:ports [{:containerPort 5432, :name "postgresql"}],
|
||||
:volumeMounts
|
||||
[...],
|
||||
:volumes
|
||||
[...]}}}}
|
||||
```
|
||||
|
||||
which renders to:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
...
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: postgresql
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: postgresql
|
||||
spec:
|
||||
containers:
|
||||
- image: postgres:16
|
||||
name: postgresql
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-secret
|
||||
key: postgres-user
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-secret
|
||||
key: postgres-password
|
||||
- name: POSTGRES_DB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: postgres-config
|
||||
key: postgres-db
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
name: postgresql
|
||||
volumeMounts:
|
||||
...
|
||||
volumes:
|
||||
...
|
||||
```
|
||||
|
||||
We optimized our db installation to run between 2Gb anf 16Gb Ram usage.
|
||||
|
||||
#### Monitoring With Grafana Cloud
|
||||
|
||||
With minimal config of
|
||||
|
||||
```clojure
|
||||
(def conf
|
||||
{:k3s-cluster-name "your-cluster-name"
|
||||
:k3s-cluster-stage :prod
|
||||
:grafana-cloud-url "your-url"})
|
||||
|
||||
(def auth
|
||||
{:grafana-cloud-user "user"
|
||||
:grafana-cloud-password "password"})
|
||||
|
||||
(monitoring/generate conf auth)
|
||||
```
|
||||
|
||||
You can connect your application to grafana cloud.
|
||||
|
||||
## Refactoring & Module Overview
|
||||
|
||||
<!---
|
||||
1. version
|
||||
2. configs as EDN and YAML
|
||||
3. renamed test-helper
|
||||
4. common load-as-edn
|
||||
5. standardized uberjar
|
||||
6. groups for webview
|
||||
7. use common ingress
|
||||
-->
|
||||
| Module | Version | [inline-macro to load resources][macro] | [native build][native] | [namespaces][ns] | [split config and auth][split] |
|
||||
|---------------|---------|:---------------------------------------:|:----------------------:|:----------------:|:------------------------------:|
|
||||
| c4k-keycloak | 1.2.1 | x | x | x | |
|
||||
| c4k-taiga | 1.1.1 | x | x | | |
|
||||
| c4k-nextcloud | 10.2 | x | x | x | |
|
||||
| c4k-jitsi | 2.1 | x | x | x | x |
|
||||
| c4k-forgejo | 3.5.0 | x | x | x | x |
|
||||
| c4k-shynet | 1.0 | x | x | | x |
|
||||
| c4k-website | 2.0 | x | x | | |
|
||||
|
||||
| Module | Version | [renamed test-helper][th1] | [common load-as-edn][edn1] | [standardized uberjar][ujar1] | [groups for webview][bgrp1] | [use common ingress][ing1] | [use common monitoring][mon1] | [validate examples][val1] | [repo model][repo1] |
|
||||
|---------------|---------|:--------------------------:|:--------------------------:|:-----------------------------:|:---------------------------:|:--------------------------:|:-----------------------------:|:-------------------------:|:-------------------:|
|
||||
| c4k-keycloak | 0.2 | - | x | x | x | x | x | x | x |
|
||||
| c4k-jira | 1.1 | - | | | | | | | x |
|
||||
| c4k-nextcloud | 4.0 | - | x | x | x | x | x | x | x |
|
||||
| c4k-jitsi | 1.3 | - | x | x | x | x | x | x | x |
|
||||
| c4k-forgejo | 2.0 | x | x | x | x | x | x | x | x |
|
||||
| c4k-shynet | 1.0 | - | | x | | | | | x |
|
||||
| c4k-website | 1.1 | x | x | x | x | x | x | x | |
|
||||
[macro]: https://repo.prod.meissa.de/meissa/c4k-jitsi/commit/61d05ceedb6dcbc6bb96b96fe6f03598e2878195
|
||||
[native]: https://repo.prod.meissa.de/meissa/c4k-forgejo/pulls/4/files
|
||||
[split]: https://repo.prod.meissa.de/meissa/c4k-jitsi/commit/d4fb8ca9e2ab44f9f9923d2e09c81a61e44b39b2
|
||||
[ns]: https://repo.prod.meissa.de/meissa/c4k-keycloak/commit/3639f3d5e6d5b364822a05b3d5d569bbc556a68b
|
||||
|
||||
[th1]: https://gitlab.com/domaindrivenarchitecture/c4k-gitea/-/merge_requests/1
|
||||
[edn1]: https://gitlab.com/domaindrivenarchitecture/c4k-website/-/merge_requests/1
|
||||
[ing1]: https://repo.prod.meissa.de/meissa/c4k-jitsi/commit/214aa41c28662fbf7a49998e17404e7ac9216430
|
||||
[ujar1]: https://repo.prod.meissa.de/meissa/c4k-jitsi/commit/b852a74dc561c3ab619e4f4d0748ab51e75edc13
|
||||
[bgrp1]: https://repo.prod.meissa.de/meissa/c4k-jitsi/commit/7ea442adaef727d5b48b242fd0baaaf51902d06e
|
||||
[mon1]: https://repo.prod.meissa.de/meissa/c4k-jitsi/commit/19e580188ea56ea26ff3a0bfb08ca428b881ad9a
|
||||
[val1]: https://repo.prod.meissa.de/meissa/c4k-jitsi/commit/5f08a108072569473463fb8f19150a12e564e54f
|
||||
[repo1]: https://repo.prod.meissa.de/meissa/c4k-forgejo/commit/e9ee6136f3347d5fccefa6b5b4a02d30c4dc42e1
|
||||
## Development & Mirrors
|
||||
|
||||
Development happens at: https://repo.prod.meissa.de/meissa/c4k-common
|
||||
|
||||
Mirrors are:
|
||||
|
||||
* https://codeberg.org/meissa/c4k-common (Issues and PR)
|
||||
* https://gitlab.com/domaindrivenarchitecture/c4k-common (CI)
|
||||
* https://github.com/DomainDrivenArchitecture/c4k-common
|
||||
|
||||
For more details about our repository model see: https://repo.prod.meissa.de/meissa/federate-your-repos
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2022 meissa GmbH
|
||||
Copyright © 2022, 2023, 2024 meissa GmbH
|
||||
Licensed under the [Apache License, Version 2.0](LICENSE) (the "License")
|
||||
Pls. find licenses of our subcomponents [here](doc/SUBCOMPONENT_LICENSE)
|
||||
Pls. find licenses of our subcomponents [here](doc/SUBCOMPONENT_LICENSE)
|
||||
|
|
124
build.py
Normal file
124
build.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
from os import environ
|
||||
from subprocess import run
|
||||
from pybuilder.core import init, task
|
||||
from ddadevops import *
|
||||
|
||||
default_task = "dev"
|
||||
|
||||
name = "c4k-common"
|
||||
MODULE = "not-used"
|
||||
PROJECT_ROOT_PATH = "."
|
||||
|
||||
|
||||
@init
|
||||
def initialize(project):
|
||||
input = {
|
||||
"name": name,
|
||||
"module": MODULE,
|
||||
"stage": "notused",
|
||||
"project_root_path": PROJECT_ROOT_PATH,
|
||||
"build_types": [],
|
||||
"mixin_types": ["RELEASE"],
|
||||
"release_primary_build_file": "project.clj",
|
||||
"release_secondary_build_files": [
|
||||
"project-cljs.clj",
|
||||
],
|
||||
"release_main_branch": "main",
|
||||
}
|
||||
|
||||
build = ReleaseMixin(project, input)
|
||||
build.initialize_build_dir()
|
||||
|
||||
|
||||
@task
|
||||
def test_clj(project):
|
||||
run("lein test", shell=True, check=True)
|
||||
|
||||
|
||||
@task
|
||||
def test_cljs(project):
|
||||
run("shadow-cljs compile test", shell=True, check=True)
|
||||
run("node target/node-tests.js", shell=True, check=True)
|
||||
|
||||
|
||||
@task
|
||||
def upload_clj(project):
|
||||
run("lein deploy", shell=True, check=True)
|
||||
|
||||
|
||||
@task
|
||||
def upload_cljs(project):
|
||||
run(
|
||||
"mv project.clj project-clj.clj && mv project-cljs.clj project.clj",
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
run("lein deploy", shell=True, check=True)
|
||||
run(
|
||||
"mv project.clj project-cljs.clj && mv project-clj.clj project.clj",
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
@task
|
||||
def lint(project):
|
||||
# TODO: Do proper configuration
|
||||
"""run(
|
||||
"lein eastwood",
|
||||
shell=True,
|
||||
check=True,
|
||||
)"""
|
||||
run(
|
||||
"lein ancient check",
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
@task
|
||||
def patch(project):
|
||||
linttest(project, "PATCH")
|
||||
release(project)
|
||||
|
||||
|
||||
@task
|
||||
def minor(project):
|
||||
linttest(project, "MINOR")
|
||||
release(project)
|
||||
|
||||
|
||||
@task
|
||||
def major(project):
|
||||
linttest(project, "MAJOR")
|
||||
release(project)
|
||||
|
||||
|
||||
@task
|
||||
def dev(project):
|
||||
linttest(project, "NONE")
|
||||
|
||||
|
||||
@task
|
||||
def prepare(project):
|
||||
build = get_devops_build(project)
|
||||
build.prepare_release()
|
||||
|
||||
|
||||
@task
|
||||
def tag(project):
|
||||
build = get_devops_build(project)
|
||||
build.tag_bump_and_push_release()
|
||||
|
||||
|
||||
def release(project):
|
||||
prepare(project)
|
||||
tag(project)
|
||||
|
||||
|
||||
def linttest(project, release_type):
|
||||
build = get_devops_build(project)
|
||||
build.update_release_type(release_type)
|
||||
test_clj(project)
|
||||
test_cljs(project)
|
||||
lint(project)
|
|
@ -49,3 +49,20 @@ C4Context
|
|||
Rel(app-backup, app-db-storage, "*dbc")
|
||||
|
||||
```
|
||||
|
||||
# Layout of a component on example of namespace
|
||||
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
|
||||
class namespace {
|
||||
config? // the external representation
|
||||
default-config // static defaults
|
||||
generate(config, auth) seq
|
||||
}
|
||||
class namespace-internal {
|
||||
config? // the internal representation
|
||||
generate-namespace(config) map
|
||||
}
|
||||
```
|
BIN
doc/tryItOut.png
Normal file
BIN
doc/tryItOut.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
|
@ -1,4 +1,4 @@
|
|||
(defproject org.domaindrivenarchitecture/c4k-common-cljs "6.0.2"
|
||||
(defproject org.domaindrivenarchitecture/c4k-common-cljs "8.0.1-SNAPSHOT"
|
||||
:description "Contains predicates and tools for c4k"
|
||||
:url "https://domaindrivenarchitecture.org"
|
||||
:license {:name "Apache License, Version 2.0"
|
||||
|
|
23
project.clj
23
project.clj
|
@ -1,18 +1,19 @@
|
|||
(defproject org.domaindrivenarchitecture/c4k-common-clj "6.0.2"
|
||||
(defproject org.domaindrivenarchitecture/c4k-common-clj "8.0.1-SNAPSHOT"
|
||||
:description "Contains predicates and tools for c4k"
|
||||
:url "https://domaindrivenarchitecture.org"
|
||||
:license {:name "Apache License, Version 2.0"
|
||||
:url "https://www.apache.org/licenses/LICENSE-2.0.html"}
|
||||
:dependencies [[org.clojure/clojure "1.11.1" :scope "provided"]
|
||||
[org.clojure/tools.reader "1.3.6"]
|
||||
:dependencies [[org.clojure/clojure "1.11.3"]
|
||||
[org.clojure/tools.reader "1.4.2"]
|
||||
[aero "1.1.6"]
|
||||
[orchestra "2021.01.01-1"]
|
||||
[expound "0.9.0"]
|
||||
[clj-commons/clj-yaml "1.0.26"]]
|
||||
[clj-commons/clj-yaml "1.0.27"]]
|
||||
:target-path "target/%s/"
|
||||
:source-paths ["src/main/cljc"
|
||||
"src/main/clj"]
|
||||
:resource-paths ["src/main/resources"]
|
||||
:resource-paths ["src/main/resources"
|
||||
"project.clj"]
|
||||
:repositories [["snapshots" :clojars]
|
||||
["releases" :clojars]]
|
||||
:deploy-repositories [["snapshots" {:sign-releases false :url "https://clojars.org/repo"}]
|
||||
|
@ -25,17 +26,13 @@
|
|||
:uberjar {:aot :all
|
||||
:main dda.c4k-common.uberjar
|
||||
:uberjar-name "c4k-common-standalone.jar"
|
||||
:dependencies [[org.clojure/tools.cli "1.0.214"]
|
||||
[ch.qos.logback/logback-classic "1.4.6"
|
||||
:dependencies [[org.clojure/tools.cli "1.1.230"]
|
||||
[ch.qos.logback/logback-classic "1.5.6"
|
||||
:exclusions [com.sun.mail/javax.mail]]
|
||||
[org.slf4j/jcl-over-slf4j "2.0.7"]]}}
|
||||
[org.slf4j/jcl-over-slf4j "2.0.13"]]}}
|
||||
:release-tasks [["test"]
|
||||
["vcs" "assert-committed"]
|
||||
["change" "version" "leiningen.release/bump-version" "release"]
|
||||
["vcs" "commit"]
|
||||
["vcs" "tag" "v" "--no-sign"]
|
||||
["change" "version" "leiningen.release/bump-version"]]
|
||||
:aliases {"inst" ["shell"
|
||||
"sh"
|
||||
"-c"
|
||||
"lein uberjar && sudo install -m=755 target/uberjar/c4k-common-standalone.jar /usr/local/bin/c4k-common-standalone.jar"]})
|
||||
["change" "version" "leiningen.release/bump-version"]])
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
[input string?]
|
||||
(.encodeToString
|
||||
(Base64/getEncoder)
|
||||
(.getBytes input "UTF-8")))
|
||||
(.getBytes ^String input "UTF-8")))
|
||||
|
||||
(defn-spec decode string?
|
||||
[input string?]
|
||||
(String.
|
||||
(.decode (Base64/getDecoder) input)
|
||||
(.decode (Base64/getDecoder) ^String input)
|
||||
"UTF-8"))
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
[clojure.spec.alpha :as s]
|
||||
[clojure.string :as cs]
|
||||
[clojure.tools.reader.edn :as edn]
|
||||
[clojure.java.io :as io]
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[dda.c4k-common.common :as cm]
|
||||
[dda.c4k-common.core :as core]
|
||||
|
@ -12,10 +13,20 @@
|
|||
(defn usage [name]
|
||||
(str
|
||||
"usage:
|
||||
|
||||
|
||||
-v | --version : Shows project version
|
||||
-h : Shows help
|
||||
|
||||
The following options require the use of main-cm instead of main-common
|
||||
-c | --config : Only generate the config
|
||||
-a | --auth : Only generate the auth
|
||||
|
||||
" name " {your configuraton file} {your authorization file}"))
|
||||
|
||||
(s/def ::options (s/* #{"-h"}))
|
||||
(s/def ::options (s/* #{"-h"
|
||||
"-v" "--version"
|
||||
"-c" "--config"
|
||||
"-a" "--auth"}))
|
||||
(s/def ::filename (s/and string?
|
||||
#(not (cs/starts-with? % "-"))))
|
||||
(s/def ::cmd-args (s/cat :options ::options
|
||||
|
@ -28,7 +39,7 @@
|
|||
(s/explain spec args)
|
||||
(println (str "Bad commandline arguments\n" (usage name))))
|
||||
|
||||
(defn main-common [name config-spec? auth-spec? config-defaults k8s-objects cmd-args]
|
||||
(defn main-cm [name config-spec? auth-spec? config-defaults config-objects auth-objects cmd-args]
|
||||
(let [parsed-args-cmd (s/conform ::cmd-args cmd-args)]
|
||||
(if (= ::s/invalid parsed-args-cmd)
|
||||
(invalid-args-msg name ::cmd-args cmd-args)
|
||||
|
@ -37,6 +48,40 @@
|
|||
(cond
|
||||
(some #(= "-h" %) options)
|
||||
(println (usage name))
|
||||
(some #(or (= "-v" %) (= "--version" %)) options)
|
||||
(println (some-> (io/resource "project.clj") slurp edn/read-string (nth 2)))
|
||||
:else
|
||||
(let [config-str (slurp config)
|
||||
auth-str (slurp auth)
|
||||
config-parse-fn (if (yaml/is-yaml? config) yaml/from-string edn/read-string)
|
||||
auth-parse-fn (if (yaml/is-yaml? auth) yaml/from-string edn/read-string)
|
||||
config-edn (config-parse-fn config-str)
|
||||
auth-edn (auth-parse-fn auth-str)
|
||||
config-valid? (s/valid? config-spec? config-edn)
|
||||
auth-valid? (s/valid? auth-spec? auth-edn)
|
||||
only-config (some #(or (= "-c" %) (= "--config" %)) options)
|
||||
only-auth (some #(or (= "-a" %) (= "--auth" %)) options)]
|
||||
(if (and config-valid? auth-valid?)
|
||||
(println (cm/generate-cm config-edn auth-edn config-defaults config-objects auth-objects only-config only-auth))
|
||||
(do
|
||||
(when (not config-valid?)
|
||||
(println
|
||||
(expound/expound-str config-spec? config-edn {:print-specs? false})))
|
||||
(when (not auth-valid?)
|
||||
(println
|
||||
(expound/expound-str auth-spec? auth-edn {:print-specs? false})))))))))))
|
||||
|
||||
(defn ^{:deprecated "6.3.1"} main-common [name config-spec? auth-spec? config-defaults k8s-objects cmd-args]
|
||||
(let [parsed-args-cmd (s/conform ::cmd-args cmd-args)]
|
||||
(if (= ::s/invalid parsed-args-cmd)
|
||||
(invalid-args-msg name ::cmd-args cmd-args)
|
||||
(let [{:keys [options args]} parsed-args-cmd
|
||||
{:keys [config auth]} args]
|
||||
(cond
|
||||
(some #(= "-h" %) options)
|
||||
(println (usage name))
|
||||
(some #(or (= "-v" %) (= "--version" %)) options)
|
||||
(println (some-> (io/resource "project.clj") slurp edn/read-string (nth 2)))
|
||||
:else
|
||||
(let [config-str (slurp config)
|
||||
auth-str (slurp auth)
|
||||
|
@ -57,9 +102,11 @@
|
|||
(expound/expound-str auth-spec? auth-edn {:print-specs? false})))))))))))
|
||||
|
||||
(defn -main [& cmd-args]
|
||||
(main-common "c4k-common"
|
||||
core/config?
|
||||
core/auth?
|
||||
core/config-defaults
|
||||
core/k8s-objects
|
||||
cmd-args))
|
||||
(main-cm
|
||||
"c4k-common"
|
||||
core/config?
|
||||
core/auth?
|
||||
core/config-defaults
|
||||
core/config-objects
|
||||
core/auth-objects
|
||||
cmd-args))
|
|
@ -7,21 +7,6 @@
|
|||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
[dda.c4k-common.predicate :as cp]))
|
||||
|
||||
;; deprecated functions were moved to dda.c4k-common.predicate
|
||||
(defn ^{:deprecated "0.1"} bash-env-string?
|
||||
[input]
|
||||
(and (string? input)
|
||||
(not (re-matches #".*['\"\$]+.*" input))))
|
||||
|
||||
(defn ^{:deprecated "0.1"} fqdn-string?
|
||||
[input]
|
||||
(and (string? input)
|
||||
(some? (re-matches #"(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)" input))))
|
||||
|
||||
(defn ^{:deprecated "0.1"} letsencrypt-issuer?
|
||||
[input]
|
||||
(contains? #{:prod :staging} input))
|
||||
|
||||
(defn-spec replace-named-value cp/map-or-seq?
|
||||
[coll cp/map-or-seq?
|
||||
name string?
|
||||
|
@ -42,21 +27,47 @@
|
|||
%)
|
||||
coll))
|
||||
|
||||
(defn-spec replace-all-matching-values-by-new-value cp/map-or-seq?
|
||||
(defn-spec replace-all-matching cp/map-or-seq?
|
||||
[coll cp/map-or-seq?
|
||||
match-value string?
|
||||
replace-value cp/str-or-number?]
|
||||
(clojure.walk/postwalk #(if (and (= (type match-value) (type %))
|
||||
(= match-value %))
|
||||
replace-value
|
||||
%)
|
||||
coll))
|
||||
|
||||
|
||||
(defn-spec ^{:deprecated "6.2.4"} replace-all-matching-values-by-new-value cp/map-or-seq?
|
||||
"Use replace-all-matching instead"
|
||||
[coll cp/map-or-seq?
|
||||
value-to-match string?
|
||||
value-to-replace cp/str-or-number?]
|
||||
(clojure.walk/postwalk #(if (and (= (type value-to-match) (type %))
|
||||
(= value-to-match %))
|
||||
value-to-replace
|
||||
%)
|
||||
coll))
|
||||
(replace-all-matching coll value-to-match value-to-replace))
|
||||
|
||||
(defn-spec concat-vec vector?
|
||||
[& vs (s/* cp/string-sequence?)]
|
||||
(into []
|
||||
(apply concat vs)))
|
||||
|
||||
(defn generate-cm
|
||||
[my-config
|
||||
my-auth
|
||||
config-defaults
|
||||
config-objects
|
||||
auth-objects
|
||||
only-config
|
||||
only-auth]
|
||||
(let [resulting-config (merge config-defaults my-config)
|
||||
both (or (and only-config only-auth) (and (not only-config) (not only-auth)))
|
||||
res-vec (cond
|
||||
both (concat-vec (config-objects resulting-config) (auth-objects resulting-config my-auth))
|
||||
only-config (config-objects resulting-config)
|
||||
only-auth (auth-objects resulting-config my-auth))]
|
||||
(cs/join
|
||||
"\n---\n"
|
||||
res-vec)))
|
||||
|
||||
(defn generate-common
|
||||
[my-config
|
||||
my-auth
|
||||
|
|
|
@ -7,15 +7,22 @@
|
|||
|
||||
(def config-defaults {})
|
||||
|
||||
(def config? (s/keys :req-un []
|
||||
(def config? (s/keys :req-un [::monitoring/mon-cfg]
|
||||
:opt-un []))
|
||||
|
||||
(def auth? (s/keys :req-un []
|
||||
(def auth? (s/keys :req-un [::monitoring/mon-auth]
|
||||
:opt-un []))
|
||||
|
||||
(defn k8s-objects [config auth]
|
||||
(defn config-objects [config]
|
||||
(let []
|
||||
(map yaml/to-string
|
||||
(filter #(not (nil? %))
|
||||
(cm/concat-vec
|
||||
(monitoring/generate config auth))))))
|
||||
(monitoring/generate-config))))))
|
||||
|
||||
(defn auth-objects [config auth]
|
||||
(let []
|
||||
(map yaml/to-string
|
||||
(filter #(not (nil? %))
|
||||
(cm/concat-vec
|
||||
(monitoring/generate-auth (:mon-cfg config) (:mon-auth auth)))))))
|
|
@ -1,84 +1,80 @@
|
|||
(ns dda.c4k-common.ingress
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
#?(:cljs [shadow.resource :as rc])
|
||||
#?(:clj [orchestra.core :refer [defn-spec]]
|
||||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
#?(:clj [clojure.edn :as edn]
|
||||
:cljs [cljs.reader :as edn])
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[dda.c4k-common.common :as cm]
|
||||
[dda.c4k-common.predicate :as pred]))
|
||||
[dda.c4k-common.namespace :as ns]
|
||||
[dda.c4k-common.ingress.ingress-internal :as int]))
|
||||
|
||||
(s/def ::issuer pred/letsencrypt-issuer?)
|
||||
(s/def ::service-name string?)
|
||||
(s/def ::app-name string?)
|
||||
(s/def ::ingress-name string?)
|
||||
(s/def ::cert-name string?)
|
||||
(s/def ::service-port pos-int?)
|
||||
(s/def ::fqdns (s/coll-of pred/fqdn-string?))
|
||||
(s/def ::issuer ::int/issuer)
|
||||
(s/def ::service-name ::int/service-name)
|
||||
(s/def ::app-name ::int/app-name)
|
||||
(s/def ::ingress-name ::int/ingress-name)
|
||||
(s/def ::cert-name ::int/cert-name)
|
||||
(s/def ::service-port ::int/service-port)
|
||||
(s/def ::fqdns ::int/fqdns)
|
||||
(s/def ::average-rate ::int/average-rate)
|
||||
(s/def ::burst-rate ::int/burst-rate)
|
||||
|
||||
(def simple-ingress? (s/keys :req-un [::fqdns ::service-name ::service-port]
|
||||
:opt-un [::issuer]))
|
||||
:opt-un [::issuer ::average-rate ::burst-rate ::ns/namespace]))
|
||||
|
||||
(def ingress? (s/keys :req-un [::fqdns ::app-name ::ingress-name ::service-name ::service-port]
|
||||
:opt-un [::issuer ::cert-name]))
|
||||
:opt-un [::issuer ::cert-name ::rate-limit-name ::ns/namespace]))
|
||||
|
||||
(def certificate? (s/keys :req-un [::fqdns ::app-name ::cert-name]
|
||||
:opt-un [::issuer]))
|
||||
:opt-un [::issuer ::ns/namespace]))
|
||||
|
||||
(def ingress-defaults {:issuer "staging"})
|
||||
(def rate-limit-config? (s/keys :req-un [::rate-limit-name
|
||||
::average-rate
|
||||
::burst-rate]))
|
||||
|
||||
#?(:cljs
|
||||
(defmethod yaml/load-resource :ingress [resource-name]
|
||||
(case resource-name
|
||||
"ingress/host-rule.yaml" (rc/inline "ingress/host-rule.yaml")
|
||||
"ingress/certificate.yaml" (rc/inline "ingress/certificate.yaml")
|
||||
"ingress/ingress.yaml" (rc/inline "ingress/ingress.yaml")
|
||||
(throw (js/Error. "Undefined Resource!")))))
|
||||
(def default-config
|
||||
(merge ns/default-config
|
||||
{:issuer "staging"
|
||||
:average-rate 10
|
||||
:burst-rate 20}))
|
||||
|
||||
(defn-spec generate-host-rule pred/map-or-seq?
|
||||
[service-name ::service-name
|
||||
service-port ::service-port
|
||||
fqdn pred/fqdn-string?]
|
||||
(->
|
||||
(yaml/load-as-edn "ingress/host-rule.yaml")
|
||||
(cm/replace-all-matching-values-by-new-value "FQDN" fqdn)
|
||||
(cm/replace-all-matching-values-by-new-value "SERVICE_PORT" service-port)
|
||||
(cm/replace-all-matching-values-by-new-value "SERVICE_NAME" service-name)))
|
||||
|
||||
(defn-spec generate-ingress pred/map-or-seq?
|
||||
[config ingress?]
|
||||
(let [{:keys [ingress-name cert-name service-name service-port fqdns app-name]} config]
|
||||
(->
|
||||
(yaml/load-as-edn "ingress/ingress.yaml")
|
||||
(assoc-in [:metadata :name] ingress-name)
|
||||
(assoc-in [:metadata :labels :app.kubernetes.part-of] app-name)
|
||||
(assoc-in [:spec :tls 0 :secretName] cert-name)
|
||||
(assoc-in [:spec :tls 0 :hosts] fqdns)
|
||||
(assoc-in [:spec :rules] (mapv (partial generate-host-rule service-name service-port) fqdns)))))
|
||||
|
||||
(defn-spec generate-certificate pred/map-or-seq?
|
||||
(defn-spec generate-certificate map?
|
||||
[config certificate?]
|
||||
(let [{:keys [cert-name issuer fqdns app-name]
|
||||
:or {issuer "staging"}} config
|
||||
letsencrypt-issuer (name issuer)]
|
||||
(->
|
||||
(yaml/load-as-edn "ingress/certificate.yaml")
|
||||
(assoc-in [:metadata :name] cert-name)
|
||||
(assoc-in [:metadata :labels :app.kubernetes.part-of] app-name)
|
||||
(assoc-in [:spec :secretName] cert-name)
|
||||
(assoc-in [:spec :commonName] (first fqdns))
|
||||
(assoc-in [:spec :dnsNames] fqdns)
|
||||
(assoc-in [:spec :issuerRef :name] letsencrypt-issuer))))
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
(int/generate-certificate final-config)))
|
||||
|
||||
(defn-spec generate-ingress-and-cert any?
|
||||
[simple-ingress-config simple-ingress?]
|
||||
(let [{:keys [service-name]} simple-ingress-config
|
||||
config (merge {:app-name service-name
|
||||
:ingress-name service-name
|
||||
:cert-name service-name}
|
||||
ingress-defaults
|
||||
simple-ingress-config)]
|
||||
[(generate-certificate config)
|
||||
(generate-ingress config)]))
|
||||
|
||||
(defn-spec generate-ingress map?
|
||||
[config ingress?]
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
(int/generate-ingress final-config)))
|
||||
|
||||
|
||||
(defn-spec generate-ingress-and-cert seq?
|
||||
[config simple-ingress?]
|
||||
(let [{:keys [service-name]} config
|
||||
final-config (merge {:app-name service-name
|
||||
:ingress-name service-name
|
||||
:cert-name service-name}
|
||||
default-config
|
||||
config)]
|
||||
[(int/generate-certificate final-config)
|
||||
(int/generate-ingress final-config)]))
|
||||
|
||||
|
||||
(defn-spec generate-simple-ingress seq?
|
||||
[config simple-ingress?]
|
||||
(let [{:keys [service-name]} config
|
||||
final-config (merge {:app-name service-name
|
||||
:ingress-name service-name
|
||||
:cert-name service-name
|
||||
:rate-limit-name service-name}
|
||||
default-config
|
||||
config)
|
||||
{:keys [average-rate burst-rate]} final-config]
|
||||
[(int/generate-certificate final-config)
|
||||
(int/generate-rate-limit-middleware {:rate-limit-name service-name
|
||||
:namespace (:namespace final-config)
|
||||
:average-rate average-rate
|
||||
:burst-rate burst-rate})
|
||||
(int/generate-ingress final-config)]))
|
108
src/main/cljc/dda/c4k_common/ingress/ingress_internal.cljc
Normal file
108
src/main/cljc/dda/c4k_common/ingress/ingress_internal.cljc
Normal file
|
@ -0,0 +1,108 @@
|
|||
(ns dda.c4k-common.ingress.ingress-internal
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
#?(:cljs [shadow.resource :as rc])
|
||||
#?(:clj [orchestra.core :refer [defn-spec]]
|
||||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[dda.c4k-common.common :as cm]
|
||||
[dda.c4k-common.namespace :as ns]
|
||||
[dda.c4k-common.predicate :as pred]))
|
||||
|
||||
|
||||
#?(:cljs
|
||||
(defmethod yaml/load-resource :ingress [resource-name]
|
||||
(case resource-name
|
||||
"ingress/certificate.yaml" (rc/inline "ingress/certificate.yaml")
|
||||
"ingress/host-rule.yaml" (rc/inline "ingress/host-rule.yaml")
|
||||
"ingress/ingress.yaml" (rc/inline "ingress/ingress.yaml")
|
||||
"ingress/middleware-ratelimit.yaml" (rc/inline "ingress/middleware-ratelimit.yaml")
|
||||
(throw (js/Error. (str "Undefined Resource: " resource-name))))))
|
||||
|
||||
|
||||
(s/def ::issuer pred/letsencrypt-issuer?)
|
||||
(s/def ::service-name string?)
|
||||
(s/def ::app-name string?)
|
||||
(s/def ::ingress-name string?)
|
||||
(s/def ::cert-name string?)
|
||||
(s/def ::service-port pos-int?)
|
||||
(s/def ::fqdns (s/coll-of pred/fqdn-string?))
|
||||
(s/def ::average-rate pos-int?)
|
||||
|
||||
(s/def ::burst-rate pos-int?)
|
||||
|
||||
(def ingress? (s/keys :req-un [::ingress-name ::app-name
|
||||
::ns/namespace
|
||||
::service-name ::service-port
|
||||
::issuer ::cert-name
|
||||
::fqdns]
|
||||
:opt-un [::rate-limit-name]))
|
||||
|
||||
(def certificate? (s/keys :req-un [::fqdns ::app-name ::cert-name ::issuer ::ns/namespace]))
|
||||
|
||||
|
||||
(def rate-limit-config? (s/keys :req-un [::rate-limit-name
|
||||
::ns/namespace
|
||||
::average-rate
|
||||
::burst-rate]))
|
||||
|
||||
|
||||
(defn-spec generate-host-rule map?
|
||||
[service-name ::service-name
|
||||
service-port ::service-port
|
||||
fqdn pred/fqdn-string?]
|
||||
(->
|
||||
(yaml/load-as-edn "ingress/host-rule.yaml")
|
||||
(cm/replace-all-matching "FQDN" fqdn)
|
||||
(cm/replace-all-matching "SERVICE_PORT" service-port)
|
||||
(cm/replace-all-matching "SERVICE_NAME" service-name)))
|
||||
|
||||
|
||||
(defn-spec generate-certificate map?
|
||||
[config certificate?]
|
||||
(let [{:keys [cert-name issuer fqdns app-name namespace]} config
|
||||
letsencrypt-issuer (name issuer)]
|
||||
(->
|
||||
(yaml/load-as-edn "ingress/certificate.yaml")
|
||||
(assoc-in [:metadata :name] cert-name)
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(assoc-in [:metadata :labels :app.kubernetes.part-of] app-name)
|
||||
(assoc-in [:spec :secretName] cert-name)
|
||||
(assoc-in [:spec :commonName] (first fqdns))
|
||||
(assoc-in [:spec :dnsNames] fqdns)
|
||||
(assoc-in [:spec :issuerRef :name] letsencrypt-issuer))))
|
||||
|
||||
|
||||
(defn-spec generate-rate-limit-middleware map?
|
||||
[config rate-limit-config?]
|
||||
(let [{:keys [rate-limit-name average-rate burst-rate namespace]} config]
|
||||
(->
|
||||
(yaml/load-as-edn "ingress/middleware-ratelimit.yaml")
|
||||
(assoc-in [:metadata :name] (str rate-limit-name "-ratelimit"))
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(assoc-in [:spec :rateLimit :average] average-rate)
|
||||
(assoc-in [:spec :rateLimit :burst] burst-rate))))
|
||||
|
||||
|
||||
(defn-spec generate-ingress map?
|
||||
[config ingress?]
|
||||
(let [{:keys [ingress-name cert-name service-name service-port
|
||||
fqdns app-name rate-limit-name namespace]} config]
|
||||
(->
|
||||
(yaml/load-as-edn "ingress/ingress.yaml")
|
||||
(assoc-in [:metadata :name] ingress-name)
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(assoc-in [:metadata :labels :app.kubernetes.part-of] app-name)
|
||||
(assoc-in [:metadata :annotations]
|
||||
{:traefik.ingress.kubernetes.io/router.entrypoints
|
||||
"web, websecure"
|
||||
:traefik.ingress.kubernetes.io/router.middlewares
|
||||
(if rate-limit-name
|
||||
(str "default-redirect-https@kubernetescrd, "
|
||||
namespace "-" rate-limit-name "-ratelimit@kubernetescrd")
|
||||
"default-redirect-https@kubernetescrd")
|
||||
:metallb.universe.tf/address-pool "public"})
|
||||
(assoc-in [:spec :tls 0 :secretName] cert-name)
|
||||
(assoc-in [:spec :tls 0 :hosts] fqdns)
|
||||
(assoc-in [:spec :rules]
|
||||
(mapv (partial generate-host-rule service-name service-port) fqdns)))))
|
34
src/main/cljc/dda/c4k_common/macros.cljc
Normal file
34
src/main/cljc/dda/c4k_common/macros.cljc
Normal file
|
@ -0,0 +1,34 @@
|
|||
(ns dda.c4k-common.macros
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as str])
|
||||
(:import java.util.jar.JarFile))
|
||||
|
||||
(defn inline-resource-file [resource-url relative-resource-folder-path]
|
||||
(let [files (.listFiles (io/file resource-url))
|
||||
file-contents (map slurp files)
|
||||
file-names (map #(str relative-resource-folder-path "/" (.getName %)) files)]
|
||||
(zipmap file-names file-contents)))
|
||||
|
||||
(defn inline-resource-jar [resource-url]
|
||||
(let [resource-url-string (.toString resource-url)
|
||||
; Remove jar:file:
|
||||
start-absolute (str/replace-first resource-url-string "jar:file:" "")
|
||||
; Split path into jar base and search folder
|
||||
jar-split (str/split start-absolute #"!/")
|
||||
absolute-jar-path (first jar-split)
|
||||
relative-file-path (second jar-split)
|
||||
jar (JarFile. absolute-jar-path)
|
||||
files (->> (enumeration-seq (.entries jar))
|
||||
(filter #(str/starts-with? % relative-file-path))
|
||||
(filter #(not (.isDirectory %))))
|
||||
file-names (map #(.getName %) files)
|
||||
file-contents (map #(slurp (.getInputStream jar %)) files)]
|
||||
(zipmap file-names file-contents)))
|
||||
|
||||
(defmacro inline-resources [resource-path]
|
||||
(let [resource-url (io/resource resource-path)
|
||||
resource-protocol (.getProtocol resource-url)]
|
||||
(case resource-protocol
|
||||
"file" (inline-resource-file resource-url resource-path)
|
||||
"jar" (inline-resource-jar resource-url))))
|
||||
|
|
@ -1,128 +1,68 @@
|
|||
(ns dda.c4k-common.monitoring
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
#?(:cljs [shadow.resource :as rc])
|
||||
#?(:clj [orchestra.core :refer [defn-spec]]
|
||||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[dda.c4k-common.predicate :as cp]
|
||||
[dda.c4k-common.common :as cm]
|
||||
[clojure.string :as str]))
|
||||
[dda.c4k-common.monitoring.monitoring-internal :as int]))
|
||||
|
||||
(s/def ::grafana-cloud-user cp/bash-env-string?)
|
||||
(s/def ::grafana-cloud-password cp/bash-env-string?)
|
||||
(s/def ::grafana-cloud-url string?)
|
||||
(s/def ::cluster-name string?)
|
||||
(s/def ::cluster-stage cp/stage?)
|
||||
(s/def ::pvc-storage-class-name cp/pvc-storage-class-name?)
|
||||
(s/def ::node-regex string?)
|
||||
(s/def ::traefik-regex string?)
|
||||
(s/def ::kube-state-regex string?)
|
||||
(s/def ::grafana-cloud-user ::int/grafana-cloud-user)
|
||||
(s/def ::grafana-cloud-password ::int/grafana-cloud-password)
|
||||
(s/def ::grafana-cloud-url ::int/grafana-cloud-url)
|
||||
(s/def ::cluster-name ::int/cluster-name)
|
||||
(s/def ::cluster-stage ::int/cluster-stage)
|
||||
(s/def ::mon-cfg (s/keys :req-un [::grafana-cloud-url
|
||||
::cluster-name
|
||||
::cluster-stage]))
|
||||
(s/def ::mon-auth (s/keys :req-un [::grafana-cloud-user
|
||||
::grafana-cloud-password]))
|
||||
(s/def ::storage (s/keys :opt-un [::pvc-storage-class-name]))
|
||||
(s/def ::filter-regex (s/keys :req-un [::node-regex
|
||||
::traefik-regex
|
||||
::kube-state-regex]))
|
||||
::cluster-name
|
||||
::cluster-stage]))
|
||||
(s/def ::mon-auth (s/keys :req-un [::grafana-cloud-user
|
||||
::grafana-cloud-password]))
|
||||
|
||||
(def metric-regex {:node-regex
|
||||
(str "node_cpu_sec.+|node_load[0-9]+|node_memory_Buf.*|node_memory_Mem.*|"
|
||||
"node_memory_Cached.*|node_disk_[r,w,i].*|node_filesystem_[s,a].*|"
|
||||
"node_network_receive_bytes_total|node_network_transmit_bytes_total")
|
||||
:traefik-regex (str "traefik_entrypoint_.*_total|"
|
||||
"traefik_entrypoint_.*_seconds_count|"
|
||||
"traefik_router_.*_total|"
|
||||
"traefik_router_.*_seconds_count|"
|
||||
"traefik_service_.*_total|"
|
||||
"traefik_service_.*_seconds_count|"
|
||||
"traefik_tls_certs_not_after")
|
||||
:kube-state-regex (str "kube_pod_container_status_restarts_total|"
|
||||
"kube_pod_status_reason|kube_node_status_capacity|kube_node_status_allocatable|"
|
||||
"kube_cronjob_status_active|kube_job_status_failed")})
|
||||
|
||||
(def filter-regex-string
|
||||
(str/join "|" (vals metric-regex)))
|
||||
(def filter-regex-string int/filter-regex-string)
|
||||
|
||||
#?(:cljs
|
||||
(defmethod yaml/load-resource :monitoring [resource-name]
|
||||
(case resource-name
|
||||
"monitoring/namespace.yaml" (rc/inline "monitoring/namespace.yaml")
|
||||
|
||||
"monitoring/kube-state-metrics/cluster-role-binding.yaml" (rc/inline "monitoring/kube-state-metrics/cluster-role-binding.yaml")
|
||||
"monitoring/kube-state-metrics/cluster-role.yaml" (rc/inline "monitoring/kube-state-metrics/cluster-role.yaml")
|
||||
"monitoring/kube-state-metrics/deployment.yaml" (rc/inline "monitoring/kube-state-metrics/deployment.yaml")
|
||||
"monitoring/kube-state-metrics/service-account.yaml" (rc/inline "monitoring/kube-state-metrics/service-account.yaml")
|
||||
"monitoring/kube-state-metrics/service.yaml" (rc/inline "monitoring/kube-state-metrics/service.yaml")
|
||||
"monitoring/node-exporter/cluster-role-binding.yaml" (rc/inline "monitoring/node-exporter/cluster-role-binding.yaml")
|
||||
"monitoring/node-exporter/cluster-role.yaml" (rc/inline "monitoring/node-exporter/cluster-role.yaml")
|
||||
"monitoring/node-exporter/daemon-set.yaml" (rc/inline "monitoring/node-exporter/daemon-set.yaml")
|
||||
"monitoring/node-exporter/service-account.yaml" (rc/inline "monitoring/node-exporter/service-account.yaml")
|
||||
"monitoring/node-exporter/service.yaml" (rc/inline "monitoring/node-exporter/service.yaml")
|
||||
"monitoring/prometheus/cluster-role-binding.yaml" (rc/inline "monitoring/prometheus/cluster-role-binding.yaml")
|
||||
"monitoring/prometheus/cluster-role.yaml" (rc/inline "monitoring/prometheus/cluster-role.yaml")
|
||||
"monitoring/prometheus/config.yaml" (rc/inline "monitoring/prometheus/config.yaml")
|
||||
"monitoring/prometheus/deployment.yaml" (rc/inline "monitoring/prometheus/deployment.yaml")
|
||||
"monitoring/prometheus/prometheus.yaml" (rc/inline "monitoring/prometheus/prometheus.yaml")
|
||||
"monitoring/prometheus/service-account.yaml" (rc/inline "monitoring/prometheus/service-account.yaml")
|
||||
"monitoring/prometheus/service.yaml" (rc/inline "monitoring/prometheus/service.yaml")
|
||||
(throw (js/Error. "Undefined Resource!")))))
|
||||
|
||||
(defn-spec generate-stateful-set cp/map-or-seq?
|
||||
[config ::storage]
|
||||
(let [{:keys [pvc-storage-class-name]
|
||||
:or {pvc-storage-class-name :manual}} config]
|
||||
(->
|
||||
(yaml/load-as-edn "monitoring/stateful-set.yaml")
|
||||
(assoc-in [:spec :volumeClaimTemplates 0 :spec :storageClassName] (name pvc-storage-class-name)))))
|
||||
|
||||
(defn-spec generate-prometheus-config cp/map-or-seq?
|
||||
[config ::mon-cfg
|
||||
auth ::mon-auth]
|
||||
(let [{:keys [grafana-cloud-url cluster-name cluster-stage]} config
|
||||
{:keys [grafana-cloud-user grafana-cloud-password]} auth]
|
||||
(->
|
||||
(yaml/load-as-edn "monitoring/prometheus/prometheus.yaml")
|
||||
(assoc-in [:global :external_labels :cluster]
|
||||
cluster-name)
|
||||
(assoc-in [:global :external_labels :stage]
|
||||
cluster-stage)
|
||||
(assoc-in [:remote_write 0 :url]
|
||||
grafana-cloud-url)
|
||||
(assoc-in [:remote_write 0 :basic_auth :username]
|
||||
grafana-cloud-user)
|
||||
(assoc-in [:remote_write 0 :basic_auth :password]
|
||||
grafana-cloud-password)
|
||||
(cm/replace-all-matching-values-by-new-value "FILTER_REGEX" filter-regex-string))))
|
||||
|
||||
(defn-spec generate-config cp/map-or-seq?
|
||||
[config ::mon-cfg
|
||||
auth ::mon-auth]
|
||||
(->
|
||||
(yaml/load-as-edn "monitoring/prometheus/config.yaml")
|
||||
(assoc-in [:stringData :prometheus.yaml]
|
||||
(yaml/to-string
|
||||
(generate-prometheus-config config auth)))))
|
||||
|
||||
(defn-spec generate cp/map-or-seq?
|
||||
(defn-spec ^{:deprecated "6.4.1"} generate seq?
|
||||
"use generate-config and generate-auth instead"
|
||||
[config ::mon-cfg
|
||||
auth ::mon-auth]
|
||||
[(yaml/load-as-edn "monitoring/namespace.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus/cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus/cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus/service.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus/service-account.yaml")
|
||||
(generate-config config auth)
|
||||
(yaml/load-as-edn "monitoring/prometheus/deployment.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter/service-account.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter/cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter/cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter/daemon-set.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter/service.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics/cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics/cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics/deployment.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics/service-account.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics/service.yaml")])
|
||||
(yaml/load-as-edn "monitoring/prometheus-cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus-cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus-service.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus-service-account.yaml")
|
||||
(int/generate-config-secret config auth)
|
||||
(yaml/load-as-edn "monitoring/prometheus-deployment.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-service-account.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-daemon-set.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-service.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-deployment.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-service-account.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-service.yaml")])
|
||||
|
||||
(defn-spec generate-config seq?
|
||||
[]
|
||||
[(yaml/load-as-edn "monitoring/namespace.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus-cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus-cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus-service.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus-service-account.yaml")
|
||||
(yaml/load-as-edn "monitoring/prometheus-deployment.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-service-account.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-daemon-set.yaml")
|
||||
(yaml/load-as-edn "monitoring/node-exporter-service.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-cluster-role-binding.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-cluster-role.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-deployment.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-service-account.yaml")
|
||||
(yaml/load-as-edn "monitoring/kube-state-metrics-service.yaml")])
|
||||
|
||||
(defn-spec generate-auth seq?
|
||||
[config ::mon-cfg
|
||||
auth ::mon-auth]
|
||||
[(int/generate-config-secret config auth)])
|
||||
|
|
102
src/main/cljc/dda/c4k_common/monitoring/monitoring_internal.cljc
Normal file
102
src/main/cljc/dda/c4k_common/monitoring/monitoring_internal.cljc
Normal file
|
@ -0,0 +1,102 @@
|
|||
(ns dda.c4k-common.monitoring.monitoring-internal
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
#?(:cljs [shadow.resource :as rc])
|
||||
#?(:clj [orchestra.core :refer [defn-spec]]
|
||||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[dda.c4k-common.predicate :as cp]
|
||||
[dda.c4k-common.common :as cm]
|
||||
[clojure.string :as str]))
|
||||
|
||||
|
||||
#?(:cljs
|
||||
(defmethod yaml/load-resource :monitoring [resource-name]
|
||||
(case resource-name
|
||||
"monitoring/kube-state-metrics-cluster-role-binding.yaml" (rc/inline "monitoring/kube-state-metrics-cluster-role-binding.yaml")
|
||||
"monitoring/kube-state-metrics-cluster-role.yaml" (rc/inline "monitoring/kube-state-metrics-cluster-role.yaml")
|
||||
"monitoring/kube-state-metrics-deployment.yaml" (rc/inline "monitoring/kube-state-metrics-deployment.yaml")
|
||||
"monitoring/kube-state-metrics-service-account.yaml" (rc/inline "monitoring/kube-state-metrics-service-account.yaml")
|
||||
"monitoring/kube-state-metrics-service.yaml" (rc/inline "monitoring/kube-state-metrics-service.yaml")
|
||||
"monitoring/namespace.yaml" (rc/inline "monitoring/namespace.yaml")
|
||||
"monitoring/node-exporter-cluster-role-binding.yaml" (rc/inline "monitoring/node-exporter-cluster-role-binding.yaml")
|
||||
"monitoring/node-exporter-cluster-role.yaml" (rc/inline "monitoring/node-exporter-cluster-role.yaml")
|
||||
"monitoring/node-exporter-daemon-set.yaml" (rc/inline "monitoring/node-exporter-daemon-set.yaml")
|
||||
"monitoring/node-exporter-service-account.yaml" (rc/inline "monitoring/node-exporter-service-account.yaml")
|
||||
"monitoring/node-exporter-service.yaml" (rc/inline "monitoring/node-exporter-service.yaml")
|
||||
"monitoring/prometheus-cluster-role-binding.yaml" (rc/inline "monitoring/prometheus-cluster-role-binding.yaml")
|
||||
"monitoring/prometheus-cluster-role.yaml" (rc/inline "monitoring/prometheus-cluster-role.yaml")
|
||||
"monitoring/prometheus-config-secret.yaml" (rc/inline "monitoring/prometheus-config-secret.yaml")
|
||||
"monitoring/prometheus-deployment.yaml" (rc/inline "monitoring/prometheus-deployment.yaml")
|
||||
"monitoring/prometheus-prometheus.yaml" (rc/inline "monitoring/prometheus-prometheus.yaml")
|
||||
"monitoring/prometheus-service-account.yaml" (rc/inline "monitoring/prometheus-service-account.yaml")
|
||||
"monitoring/prometheus-service.yaml" (rc/inline "monitoring/prometheus-service.yaml")
|
||||
(throw (js/Error. (str "Undefined Resource: " resource-name))))))
|
||||
|
||||
|
||||
(s/def ::grafana-cloud-user cp/bash-env-string?)
|
||||
(s/def ::grafana-cloud-password cp/bash-env-string?)
|
||||
(s/def ::grafana-cloud-url string?)
|
||||
(s/def ::cluster-name string?)
|
||||
(s/def ::cluster-stage cp/stage?)
|
||||
(s/def ::mon-cfg (s/keys :req-un [::grafana-cloud-url
|
||||
::cluster-name
|
||||
::cluster-stage]))
|
||||
(s/def ::mon-auth (s/keys :req-un [::grafana-cloud-user
|
||||
::grafana-cloud-password]))
|
||||
|
||||
(def metric-regex {:node-regex
|
||||
(str "node_cpu_sec.+|node_load[0-9]+|node_memory_Buf.*|node_memory_Mem.*|"
|
||||
"node_memory_Cached.*|node_disk_[r,w,i].*|node_filesystem_[s,a].*|"
|
||||
"node_network_receive_bytes_total|node_network_transmit_bytes_total")
|
||||
:traefik-regex (str "traefik_entrypoint_.*_total|"
|
||||
"traefik_entrypoint_.*_seconds_count|"
|
||||
"traefik_router_.*_total|"
|
||||
"traefik_router_.*_seconds_count|"
|
||||
"traefik_service_.*_total|"
|
||||
"traefik_service_.*_seconds_count|"
|
||||
"traefik_tls_certs_not_after")
|
||||
:kube-state-regex (str "kube_pod_container_status_restarts_total|"
|
||||
"kube_pod_status_reason|kube_node_status_capacity|kube_node_status_allocatable|"
|
||||
"kube_cronjob_status_active|kube_job_status_failed")})
|
||||
|
||||
(def filter-regex-string
|
||||
(str/join "|" (vals metric-regex)))
|
||||
|
||||
(defn-spec generate-prometheus-config map?
|
||||
[config ::mon-cfg
|
||||
auth ::mon-auth]
|
||||
(let [{:keys [grafana-cloud-url cluster-name cluster-stage]} config
|
||||
{:keys [grafana-cloud-user grafana-cloud-password]} auth]
|
||||
(->
|
||||
(yaml/load-as-edn "monitoring/prometheus-prometheus.yaml")
|
||||
(assoc-in [:global :external_labels :cluster]
|
||||
cluster-name)
|
||||
(assoc-in [:global :external_labels :stage]
|
||||
cluster-stage)
|
||||
(assoc-in [:remote_write 0 :url]
|
||||
grafana-cloud-url)
|
||||
(assoc-in [:remote_write 0 :basic_auth :username]
|
||||
grafana-cloud-user)
|
||||
(assoc-in [:remote_write 0 :basic_auth :password]
|
||||
grafana-cloud-password)
|
||||
(cm/replace-all-matching "FILTER_REGEX" filter-regex-string))))
|
||||
|
||||
(defn-spec generate-config-secret map?
|
||||
[config ::mon-cfg
|
||||
auth ::mon-auth]
|
||||
(->
|
||||
(yaml/load-as-edn "monitoring/prometheus-config-secret.yaml")
|
||||
(assoc-in [:stringData :prometheus.yaml]
|
||||
(yaml/to-string
|
||||
(generate-prometheus-config config auth)))))
|
||||
|
||||
(defn-spec ^{:deprecated "6.4.1"} generate-config map?
|
||||
"Use generate-config-secret instead"
|
||||
[config ::mon-cfg
|
||||
auth ::mon-auth]
|
||||
(->
|
||||
(yaml/load-as-edn "monitoring/prometheus-config-secret.yaml")
|
||||
(assoc-in [:stringData :prometheus.yaml]
|
||||
(yaml/to-string
|
||||
(generate-prometheus-config config auth)))))
|
19
src/main/cljc/dda/c4k_common/namespace.cljc
Normal file
19
src/main/cljc/dda/c4k_common/namespace.cljc
Normal file
|
@ -0,0 +1,19 @@
|
|||
(ns dda.c4k-common.namespace
|
||||
(:require
|
||||
#?(:clj [orchestra.core :refer [defn-spec]]
|
||||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
[clojure.spec.alpha :as s]
|
||||
[dda.c4k-common.namespace.namespace-internal :as int]))
|
||||
|
||||
(s/def ::namespace ::int/namespace)
|
||||
|
||||
(def config? (s/keys :req-un []
|
||||
:opt-un [::namespace]))
|
||||
|
||||
(def default-config {:namespace "default"})
|
||||
|
||||
(defn-spec generate seq?
|
||||
[config config?]
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
[(int/generate-namespace final-config)]))
|
|
@ -0,0 +1,23 @@
|
|||
(ns dda.c4k-common.namespace.namespace-internal
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
#?(:clj [orchestra.core :refer [defn-spec]]
|
||||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
#?(:cljs [dda.c4k-common.macros :refer-macros [inline-resources]])))
|
||||
|
||||
#?(:cljs
|
||||
(defmethod yaml/load-resource :namespace [resource-name]
|
||||
(get (inline-resources "namespace") resource-name)))
|
||||
|
||||
(s/def ::namespace string?)
|
||||
|
||||
(def config? (s/keys :req-un [::namespace]
|
||||
:opt-un []))
|
||||
|
||||
(defn-spec generate-namespace map?
|
||||
[config config?]
|
||||
(let [{:keys [namespace]} config]
|
||||
(->
|
||||
(yaml/load-as-edn "namespace/namespace.yaml")
|
||||
(assoc-in [:metadata :name] namespace))))
|
|
@ -1,99 +1,111 @@
|
|||
(ns dda.c4k-common.postgres
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
#?(:cljs [shadow.resource :as rc])
|
||||
#?(:clj [orchestra.core :refer [defn-spec]]
|
||||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[dda.c4k-common.base64 :as b64]
|
||||
[dda.c4k-common.predicate :as cp]
|
||||
[dda.c4k-common.common :as cm]))
|
||||
[dda.c4k-common.namespace :as ns]
|
||||
[dda.c4k-common.postgres.postgres-internal :as int]))
|
||||
|
||||
(defn postgres-size?
|
||||
[input]
|
||||
(contains? #{:2gb :4gb :8gb :16gb} input))
|
||||
(def postgres-size? int/postgres-size?)
|
||||
|
||||
(defn postgres-image?
|
||||
[input]
|
||||
(contains? #{"postgres:13" "postgres:14"} input))
|
||||
(def postgres-image? int/postgres-image?)
|
||||
|
||||
(s/def ::postgres-db-user cp/bash-env-string?)
|
||||
(s/def ::postgres-db-password cp/bash-env-string?)
|
||||
(s/def ::postgres-data-volume-path string?)
|
||||
(s/def ::postgres-size postgres-size?)
|
||||
(s/def ::db-name cp/bash-env-string?)
|
||||
(s/def ::pvc-storage-class-name cp/pvc-storage-class-name?)
|
||||
(s/def ::pv-storage-size-gb pos?)
|
||||
(s/def ::postgres-db-user ::int/postgres-db-user)
|
||||
(s/def ::postgres-db-password ::int/postgres-db-password)
|
||||
(s/def ::postgres-data-volume-path ::int/postgres-data-volume-path)
|
||||
(s/def ::postgres-size ::int/postgres-size)
|
||||
(s/def ::db-name ::int/db-name)
|
||||
(s/def ::pvc-storage-class-name ::int/pvc-storage-class-name)
|
||||
(s/def ::pv-storage-size-gb ::int/pv-storage-size-gb)
|
||||
|
||||
(def pg-config?
|
||||
(s/keys :opt-un [::postgres-size ::db-name ::postgres-data-volume-path
|
||||
::pvc-storage-class-name ::pv-storage-size-gb]))
|
||||
::pvc-storage-class-name ::pv-storage-size-gb ::ns/namespace]))
|
||||
(def pg-auth?
|
||||
(s/keys :opt-un [::postgres-db-user ::postgres-db-password]))
|
||||
|
||||
(def postgres-function (s/keys :opt-un [::deserializer ::optional]))
|
||||
|
||||
#?(:cljs
|
||||
(defmethod yaml/load-resource :postgres [resource-name]
|
||||
(case resource-name
|
||||
"postgres/config-2gb.yaml" (rc/inline "postgres/config-2gb.yaml")
|
||||
"postgres/config-4gb.yaml" (rc/inline "postgres/config-4gb.yaml")
|
||||
"postgres/config-8gb.yaml" (rc/inline "postgres/config-8gb.yaml")
|
||||
"postgres/config-16gb.yaml" (rc/inline "postgres/config-16gb.yaml")
|
||||
"postgres/deployment.yaml" (rc/inline "postgres/deployment.yaml")
|
||||
"postgres/persistent-volume.yaml" (rc/inline "postgres/persistent-volume.yaml")
|
||||
"postgres/pvc.yaml" (rc/inline "postgres/pvc.yaml")
|
||||
"postgres/secret.yaml" (rc/inline "postgres/secret.yaml")
|
||||
"postgres/service.yaml" (rc/inline "postgres/service.yaml")
|
||||
(throw (js/Error. "Undefined Resource!")))))
|
||||
(def default-config (merge ns/default-config
|
||||
{:postgres-image "postgres:13"
|
||||
:postgres-size :2gb
|
||||
:db-name "postgres"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 10
|
||||
:pvc-storage-class-name "manual"}))
|
||||
|
||||
(defn-spec generate-config cp/map-or-seq?
|
||||
(defn-spec generate-configmap map?
|
||||
[& config (s/? pg-config?)]
|
||||
(let [{:keys [postgres-size db-name]
|
||||
:or {postgres-size :2gb
|
||||
db-name "postgres"}} (first config)]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource
|
||||
(str "postgres/config-" (name postgres-size) ".yaml")))
|
||||
(assoc-in [:data :postgres-db] db-name))))
|
||||
(let [final-config (merge default-config
|
||||
(first config))]
|
||||
(int/generate-configmap final-config)))
|
||||
|
||||
; TODO: why do we need a sequence of configs?
|
||||
(defn-spec generate-deployment cp/map-or-seq?
|
||||
|
||||
(defn-spec generate-deployment map?
|
||||
[& config (s/? pg-config?)]
|
||||
(let [{:keys [postgres-image]
|
||||
:or {postgres-image "postgres:13"}} (first config)]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/deployment.yaml"))
|
||||
(assoc-in [:spec :template :spec :containers 0 :image] postgres-image))))
|
||||
(let [final-config (merge default-config
|
||||
(first config))]
|
||||
(int/generate-deployment final-config)))
|
||||
|
||||
(defn-spec generate-persistent-volume cp/map-or-seq?
|
||||
|
||||
(defn-spec generate-persistent-volume map?
|
||||
[config pg-config?]
|
||||
(let [{:keys [postgres-data-volume-path pv-storage-size-gb]
|
||||
:or {postgres-data-volume-path "/var/postgres"
|
||||
pv-storage-size-gb 10}} config]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/persistent-volume.yaml"))
|
||||
(assoc-in [:spec :hostPath :path] postgres-data-volume-path)
|
||||
(assoc-in [:spec :capacity :storage] (str pv-storage-size-gb "Gi")))))
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
(int/generate-persistent-volume final-config)))
|
||||
|
||||
(defn-spec generate-pvc cp/map-or-seq?
|
||||
|
||||
(defn-spec generate-pvc map?
|
||||
[config pg-config?]
|
||||
(let [{:keys [pv-storage-size-gb pvc-storage-class-name]
|
||||
:or {pv-storage-size-gb 10
|
||||
pvc-storage-class-name "manual"}} config]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/pvc.yaml"))
|
||||
(assoc-in [:spec :resources :requests :storage] (str pv-storage-size-gb "Gi"))
|
||||
(assoc-in [:spec :storageClassName] (name pvc-storage-class-name)))))
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
(int/generate-pvc final-config)))
|
||||
|
||||
(defn-spec generate-secret cp/map-or-seq?
|
||||
[my-auth any?]
|
||||
(let [{:keys [postgres-db-user postgres-db-password]} my-auth]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/secret.yaml"))
|
||||
(cm/replace-key-value :postgres-user (b64/encode postgres-db-user))
|
||||
(cm/replace-key-value :postgres-password (b64/encode postgres-db-password)))))
|
||||
|
||||
(defn-spec generate-service cp/map-or-seq?
|
||||
[]
|
||||
(yaml/from-string (yaml/load-resource "postgres/service.yaml")))
|
||||
(defn-spec generate-secret map?
|
||||
([auth pg-auth?]
|
||||
(let [final-config default-config]
|
||||
(int/generate-secret final-config auth)))
|
||||
([config pg-config?
|
||||
auth pg-auth?]
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
(int/generate-secret final-config auth))))
|
||||
|
||||
|
||||
(defn-spec generate-service map?
|
||||
[config pg-config?]
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
(int/generate-service final-config)))
|
||||
|
||||
|
||||
(defn-spec ^{:deprecated "6.4.1"} generate seq?
|
||||
"use generate-config and generate-auth instead"
|
||||
[config pg-config?
|
||||
auth pg-auth?]
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
[(int/generate-secret final-config auth)
|
||||
(int/generate-persistent-volume final-config)
|
||||
(int/generate-configmap final-config)
|
||||
(int/generate-pvc final-config)
|
||||
(int/generate-deployment final-config)
|
||||
(int/generate-service final-config)]))
|
||||
|
||||
(defn-spec generate-config seq?
|
||||
[config pg-config?]
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
[(int/generate-persistent-volume final-config)
|
||||
(int/generate-configmap final-config)
|
||||
(int/generate-pvc final-config)
|
||||
(int/generate-deployment final-config)
|
||||
(int/generate-service final-config)]))
|
||||
|
||||
(defn-spec generate-auth seq?
|
||||
[config pg-config?
|
||||
auth pg-auth?]
|
||||
(let [final-config (merge default-config
|
||||
config)]
|
||||
[(int/generate-secret final-config auth)]))
|
||||
|
|
121
src/main/cljc/dda/c4k_common/postgres/postgres_internal.cljc
Normal file
121
src/main/cljc/dda/c4k_common/postgres/postgres_internal.cljc
Normal file
|
@ -0,0 +1,121 @@
|
|||
(ns dda.c4k-common.postgres.postgres-internal
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
#?(:cljs [shadow.resource :as rc])
|
||||
#?(:clj [orchestra.core :refer [defn-spec]]
|
||||
:cljs [orchestra.core :refer-macros [defn-spec]])
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[dda.c4k-common.base64 :as b64]
|
||||
[dda.c4k-common.predicate :as cp]
|
||||
[dda.c4k-common.common :as cm]
|
||||
[dda.c4k-common.namespace :as ns]))
|
||||
|
||||
|
||||
#?(:cljs
|
||||
(defmethod yaml/load-resource :postgres [resource-name]
|
||||
(case resource-name
|
||||
"postgres/config-2gb.yaml" (rc/inline "postgres/config-2gb.yaml")
|
||||
"postgres/config-4gb.yaml" (rc/inline "postgres/config-4gb.yaml")
|
||||
"postgres/config-8gb.yaml" (rc/inline "postgres/config-8gb.yaml")
|
||||
"postgres/config-16gb.yaml" (rc/inline "postgres/config-16gb.yaml")
|
||||
"postgres/deployment.yaml" (rc/inline "postgres/deployment.yaml")
|
||||
"postgres/persistent-volume.yaml" (rc/inline "postgres/persistent-volume.yaml")
|
||||
"postgres/pvc.yaml" (rc/inline "postgres/pvc.yaml")
|
||||
"postgres/secret.yaml" (rc/inline "postgres/secret.yaml")
|
||||
"postgres/service.yaml" (rc/inline "postgres/service.yaml")
|
||||
(throw (js/Error. (str "Undefined Resource: " resource-name))))))
|
||||
|
||||
|
||||
(defn postgres-size?
|
||||
[input]
|
||||
(contains? #{:2gb :4gb :8gb :16gb} input))
|
||||
|
||||
(defn postgres-image?
|
||||
[input]
|
||||
(contains? #{"postgres:13" "postgres:14" "postgres:15" "postgres:16"} input))
|
||||
(s/def ::postgres-db-user cp/bash-env-string?)
|
||||
(s/def ::postgres-db-password cp/bash-env-string?)
|
||||
(s/def ::postgres-data-volume-path string?)
|
||||
(s/def ::postgres-size postgres-size?)
|
||||
(s/def ::db-name cp/bash-env-string?)
|
||||
(s/def ::pvc-storage-class-name cp/pvc-storage-class-name?)
|
||||
|
||||
(s/def ::pv-storage-size-gb pos?)
|
||||
(def pg-config?
|
||||
(s/keys :req-un [::postgres-size ::db-name ::postgres-data-volume-path
|
||||
::pvc-storage-class-name ::pv-storage-size-gb ::ns/namespace]))
|
||||
|
||||
(def pg-auth?
|
||||
(s/keys :req-un [::postgres-db-user ::postgres-db-password]))
|
||||
|
||||
|
||||
(def postgres-function (s/keys :opt-un [::deserializer ::optional]))
|
||||
|
||||
|
||||
(defn-spec generate-configmap map?
|
||||
[config pg-config?]
|
||||
(let [{:keys [postgres-size db-name namespace]} config]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource
|
||||
(str "postgres/config-" (name postgres-size) ".yaml")))
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(assoc-in [:data :postgres-db] db-name))))
|
||||
|
||||
(defn-spec ^{:deprecated "6.4.1"} generate-config map?
|
||||
"use generate-configmap instead"
|
||||
[config pg-config?]
|
||||
(let [{:keys [postgres-size db-name namespace]} config]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource
|
||||
(str "postgres/config-" (name postgres-size) ".yaml")))
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(assoc-in [:data :postgres-db] db-name))))
|
||||
|
||||
|
||||
(defn-spec generate-deployment map?
|
||||
[config pg-config?]
|
||||
(let [{:keys [postgres-image namespace]} config]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/deployment.yaml"))
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(assoc-in [:spec :template :spec :containers 0 :image] postgres-image))))
|
||||
|
||||
|
||||
(defn-spec generate-persistent-volume map?
|
||||
[config pg-config?]
|
||||
(let [{:keys [postgres-data-volume-path pv-storage-size-gb namespace]} config]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/persistent-volume.yaml"))
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(assoc-in [:spec :hostPath :path] postgres-data-volume-path)
|
||||
(assoc-in [:spec :capacity :storage] (str pv-storage-size-gb "Gi")))))
|
||||
|
||||
|
||||
(defn-spec generate-pvc map?
|
||||
[config pg-config?]
|
||||
(let [{:keys [pv-storage-size-gb pvc-storage-class-name namespace]} config]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/pvc.yaml"))
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(assoc-in [:spec :resources :requests :storage] (str pv-storage-size-gb "Gi"))
|
||||
(assoc-in [:spec :storageClassName] (name pvc-storage-class-name)))))
|
||||
|
||||
|
||||
(defn-spec generate-secret map?
|
||||
[config pg-config?
|
||||
auth pg-auth?]
|
||||
(let [{:keys [namespace]} config
|
||||
{:keys [postgres-db-user postgres-db-password]} auth]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/secret.yaml"))
|
||||
(assoc-in [:metadata :namespace] namespace)
|
||||
(cm/replace-key-value :postgres-user (b64/encode postgres-db-user))
|
||||
(cm/replace-key-value :postgres-password (b64/encode postgres-db-password)))))
|
||||
|
||||
|
||||
(defn-spec generate-service map?
|
||||
[config pg-config?]
|
||||
(let [{:keys [namespace]} config]
|
||||
(->
|
||||
(yaml/from-string (yaml/load-resource "postgres/service.yaml"))
|
||||
(assoc-in [:metadata :namespace] namespace))))
|
|
@ -14,6 +14,16 @@
|
|||
(and (string? input)
|
||||
(some? (re-matches #"(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)" input))))
|
||||
|
||||
(defn ipv4-string?
|
||||
[input]
|
||||
(and (string? input)
|
||||
(some? (re-matches #"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$" input))))
|
||||
|
||||
(defn ipv6-string?
|
||||
[input]
|
||||
(and (string? input)
|
||||
(some? (re-matches #"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" input))))
|
||||
|
||||
(defn string-of-separated-by?
|
||||
[spec-function separator input]
|
||||
(every? true? (map spec-function (str/split input separator))))
|
||||
|
@ -33,7 +43,7 @@
|
|||
|
||||
(defn pvc-storage-class-name?
|
||||
[input]
|
||||
(contains? #{"manual" "local-path"} input))
|
||||
(contains? #{"manual" "local-path" "hcloud-volumes" "hcloud-volumes-encrypted"} input))
|
||||
|
||||
(defn port-number?
|
||||
[input]
|
||||
|
|
|
@ -21,20 +21,27 @@
|
|||
(s/def ::deserializer fn?)
|
||||
(s/def ::optional boolean?)
|
||||
(def dom-function-parameter (s/keys :opt-un [::deserializer ::optional]))
|
||||
(defn-spec get-content-value-from-element js-object?
|
||||
[name string?]
|
||||
(-> (get-element-by-id name)
|
||||
(.-value)))
|
||||
|
||||
(defn-spec deserialize-content js-object?
|
||||
[content string?
|
||||
deserializer ::deserializer
|
||||
optional ::optional]
|
||||
(cond
|
||||
(and optional (st/blank? content))
|
||||
nil
|
||||
:else
|
||||
(apply deserializer [content])))
|
||||
|
||||
(defn-spec get-content-from-element js-object?
|
||||
[name string?
|
||||
& {:keys [deserializer optional]
|
||||
:or {deserializer nil optional false}} dom-function-parameter]
|
||||
(let [content (-> (get-element-by-id name)
|
||||
(.-value))]
|
||||
(cond
|
||||
(and optional (some? deserializer))
|
||||
(when-not (st/blank? content)
|
||||
(apply deserializer [content]))
|
||||
(and (false? optional) (some? deserializer))
|
||||
(apply deserializer [content])
|
||||
:else
|
||||
content)))
|
||||
:or {deserializer identity optional false}} dom-function-parameter]
|
||||
(-> (get-content-value-from-element name)
|
||||
(deserialize-content deserializer optional)))
|
||||
|
||||
(defn-spec set-validation-result! js-object?
|
||||
[name string?
|
||||
|
@ -50,7 +57,7 @@
|
|||
[name string?
|
||||
spec js-object?
|
||||
& {:keys [deserializer optional]
|
||||
:or {deserializer nil optional false}} dom-function-parameter]
|
||||
:or {deserializer identity optional false}} dom-function-parameter]
|
||||
(let [content (get-content-from-element name :optional optional :deserializer deserializer)]
|
||||
(if (or (and optional (st/blank? content))
|
||||
(s/valid? spec content))
|
||||
|
|
2
src/main/cljs/dda/c4k_common/macros.cljs
Normal file
2
src/main/cljs/dda/c4k_common/macros.cljs
Normal file
|
@ -0,0 +1,2 @@
|
|||
(ns dda.c4k-common.macros
|
||||
(:require-macros [dda.c4k-common.macros]))
|
|
@ -3,7 +3,6 @@
|
|||
["js-yaml" :as yaml]
|
||||
[clojure.string :as st]
|
||||
[orchestra.core :refer-macros [defn-spec]]
|
||||
[shadow.resource :as rc]
|
||||
[dda.c4k-common.predicate :as cp]))
|
||||
|
||||
(defn string-or-keyword? [input]
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: c4k-common-https-ingress
|
||||
namespace: default
|
||||
labels:
|
||||
app.kubernetes.part-of: c4k-common-app
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web, websecure
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
|
||||
|
|
9
src/main/resources/ingress/middleware-ratelimit.yaml
Normal file
9
src/main/resources/ingress/middleware-ratelimit.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: ratelimit
|
||||
namespace: default
|
||||
spec:
|
||||
rateLimit:
|
||||
average: AVG
|
||||
burst: BRS
|
4
src/main/resources/namespace/namespace.yaml
Normal file
4
src/main/resources/namespace/namespace.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: default
|
21
src/test/clj/dda/c4k_common/macros_test.clj
Normal file
21
src/test/clj/dda/c4k_common/macros_test.clj
Normal file
|
@ -0,0 +1,21 @@
|
|||
(ns dda.c4k-common.macros-test
|
||||
(:require
|
||||
[clojure.test :refer [deftest is are testing run-tests]]
|
||||
[dda.c4k-common.macros :as cut :refer [inline-resources]]))
|
||||
|
||||
(deftest should-count-inline-resources
|
||||
(is (= 3 (count (inline-resources "dda/c4k_common/inline_resources_test")))))
|
||||
|
||||
(deftest should-inline-resources
|
||||
(let [resource-path (fn [name] (str "dda/c4k_common/inline_resources_test/" name))
|
||||
inlined-resources (inline-resources "dda/c4k_common/inline_resources_test")]
|
||||
(is (= "1" (get inlined-resources (resource-path "inline_resource_1.yaml"))))
|
||||
(is (= "2" (get inlined-resources (resource-path "inline_resource_2.yaml"))))
|
||||
(is (= "3" (get inlined-resources (resource-path "inline_resource_3.yaml"))))))
|
||||
|
||||
(deftest should-inline-jar-resources
|
||||
(let [jar-url (java.net.URL. "jar:file:./src/test/resources/dda/c4k_common/inline_jar_test/test.jar!/inline_resources_test/")
|
||||
inlined-resources (cut/inline-resource-jar jar-url)]
|
||||
(is (= "1" (get inlined-resources "inline_resources_test/inline_resource_1.yaml")))
|
||||
(is (= "2" (get inlined-resources "inline_resources_test/inline_resource_2.yaml")))
|
||||
(is (= "3" (get inlined-resources "inline_resources_test/inline_resource_3.yaml")))))
|
|
@ -1,8 +1,8 @@
|
|||
(ns dda.c4k-common.monitoring-regex-test
|
||||
(ns dda.c4k-common.monitoring.monitoring-regex-test
|
||||
(:require
|
||||
[clojure.test :refer [deftest is are testing run-tests]]
|
||||
[data-test :refer :all]
|
||||
[dda.c4k-common.monitoring :as cut]))
|
||||
[dda.c4k-common.monitoring.monitoring-internal :as cut]))
|
||||
|
||||
(defn filter-by-regex
|
||||
[regex-str collection]
|
22
src/test/cljc/dda/c4k_common/core_test.cljc
Normal file
22
src/test/cljc/dda/c4k_common/core_test.cljc
Normal file
|
@ -0,0 +1,22 @@
|
|||
(ns dda.c4k-common.core-test
|
||||
(:require
|
||||
#?(:cljs [shadow.resource :as rc])
|
||||
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
|
||||
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
|
||||
[clojure.spec.alpha :as s]
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[dda.c4k-common.core :as cut]))
|
||||
|
||||
#?(:cljs
|
||||
(defmethod yaml/load-resource :common-test [resource-name]
|
||||
(case resource-name
|
||||
"common-test/valid-auth.yaml" (rc/inline "common-test/valid-auth.yaml")
|
||||
"common-test/valid-config.yaml" (rc/inline "common-test/valid-config.yaml")
|
||||
(throw (js/Error. "Undefined Resource!")))))
|
||||
|
||||
(def conf (yaml/load-as-edn "common-test/valid-config.yaml"))
|
||||
(def auth (yaml/load-as-edn "common-test/valid-auth.yaml"))
|
||||
|
||||
(deftest validate-valid-resources
|
||||
(is (s/valid? cut/config? conf))
|
||||
(is (s/valid? cut/auth? auth)))
|
135
src/test/cljc/dda/c4k_common/ingress/ingress_internal_test.cljc
Normal file
135
src/test/cljc/dda/c4k_common/ingress/ingress_internal_test.cljc
Normal file
|
@ -0,0 +1,135 @@
|
|||
(ns dda.c4k-common.ingress.ingress-internal-test
|
||||
(:require
|
||||
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
|
||||
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
|
||||
[clojure.spec.test.alpha :as st]
|
||||
[dda.c4k-common.ingress.ingress-internal :as cut]))
|
||||
|
||||
(st/instrument `cut/generate-host-rule)
|
||||
(st/instrument `cut/generate-certificate)
|
||||
(st/instrument `cut/generate-rate-limit-middleware)
|
||||
(st/instrument `cut/generate-ingress)
|
||||
|
||||
|
||||
(deftest should-generate-rule
|
||||
(is (= {:host "test.com",
|
||||
:http
|
||||
{:paths
|
||||
[{:pathType "Prefix",
|
||||
:path "/",
|
||||
:backend
|
||||
{:service {:name "myservice", :port {:number 3000}}}}]}}
|
||||
(cut/generate-host-rule "myservice" 3000 "test.com"))))
|
||||
|
||||
|
||||
(deftest should-generate-certificate
|
||||
(is (= {:apiVersion "cert-manager.io/v1",
|
||||
:kind "Certificate",
|
||||
:metadata {:name "test-io-cert",
|
||||
:namespace "default",
|
||||
:labels {:app.kubernetes.part-of "c4k-common-app"}},
|
||||
:spec
|
||||
{:secretName "test-io-cert",
|
||||
:commonName "test.de",
|
||||
:duration "2160h",
|
||||
:renewBefore "720h",
|
||||
:dnsNames ["test.de" "test.org" "www.test.de" "www.test.org"],
|
||||
:issuerRef {:name "prod", :kind "ClusterIssuer"}}}
|
||||
(cut/generate-certificate {:fqdns ["test.de" "test.org" "www.test.de" "www.test.org"]
|
||||
:app-name "c4k-common-app"
|
||||
:cert-name "test-io-cert"
|
||||
:issuer "prod"
|
||||
:namespace "default"})))
|
||||
(is (= {:apiVersion "cert-manager.io/v1",
|
||||
:kind "Certificate",
|
||||
:metadata {:name "test-io-cert",
|
||||
:namespace "myapp",
|
||||
:labels {:app.kubernetes.part-of "c4k-common-app"}},
|
||||
:spec
|
||||
{:secretName "test-io-cert",
|
||||
:commonName "test.de",
|
||||
:duration "2160h",
|
||||
:renewBefore "720h",
|
||||
:dnsNames ["test.de" "test.org" "www.test.de" "www.test.org"],
|
||||
:issuerRef {:name "prod", :kind "ClusterIssuer"}}}
|
||||
(cut/generate-certificate {:fqdns ["test.de" "test.org" "www.test.de" "www.test.org"]
|
||||
:app-name "c4k-common-app"
|
||||
:cert-name "test-io-cert"
|
||||
:issuer "prod"
|
||||
:namespace "myapp"}))))
|
||||
|
||||
|
||||
(deftest should-generate-middleware-ratelimit
|
||||
(is (= {:apiVersion "traefik.containo.us/v1alpha1",
|
||||
:kind "Middleware",
|
||||
:metadata {:name "normal-ratelimit"
|
||||
:namespace "myapp",},
|
||||
:spec {:rateLimit {:average 10, :burst 5}}}
|
||||
(cut/generate-rate-limit-middleware {:rate-limit-name "normal"
|
||||
:namespace "myapp"
|
||||
:average-rate 10, :burst-rate 5}))))
|
||||
|
||||
|
||||
(deftest should-generate-ingress
|
||||
(is (= {:apiVersion "networking.k8s.io/v1",
|
||||
:kind "Ingress",
|
||||
:metadata
|
||||
{:namespace "myapp",
|
||||
:name "test-io-https-ingress",
|
||||
:labels {:app.kubernetes.part-of "c4k-common-app"},
|
||||
:annotations {:traefik.ingress.kubernetes.io/router.entrypoints
|
||||
"web, websecure"
|
||||
:traefik.ingress.kubernetes.io/router.middlewares
|
||||
"default-redirect-https@kubernetescrd"
|
||||
:metallb.universe.tf/address-pool "public"}}}
|
||||
(dissoc (cut/generate-ingress
|
||||
{:ingress-name "test-io-https-ingress"
|
||||
:app-name "c4k-common-app"
|
||||
:namespace "myapp"
|
||||
:service-name "test-io-service" :service-port 80
|
||||
:issuer "prod" :cert-name "noname"
|
||||
:fqdns ["test.de" "www.test.de" "test-it.de"
|
||||
"www.test-it.de"]}) :spec)))
|
||||
(is (= {:name "test-io-https-ingress",
|
||||
:namespace "default",
|
||||
:labels {:app.kubernetes.part-of "c4k-common-app"},
|
||||
:annotations {:traefik.ingress.kubernetes.io/router.entrypoints
|
||||
"web, websecure"
|
||||
:traefik.ingress.kubernetes.io/router.middlewares
|
||||
"default-redirect-https@kubernetescrd, default-normal-ratelimit@kubernetescrd",
|
||||
:metallb.universe.tf/address-pool "public"}}
|
||||
(:metadata (cut/generate-ingress
|
||||
{
|
||||
:ingress-name "test-io-https-ingress"
|
||||
:app-name "c4k-common-app"
|
||||
:namespace "default"
|
||||
:service-name "test-io-service" :service-port 80
|
||||
:rate-limit-name "normal"
|
||||
:issuer "prod" :cert-name "noname"
|
||||
:fqdns ["test.de"]}))))
|
||||
(is (= {:tls
|
||||
[{:hosts
|
||||
["test.de" "www.test.de" "test-it.de" "www.test-it.de"],
|
||||
:secretName "test-io-cert"}]
|
||||
:rules
|
||||
[{:host "test.de",
|
||||
:http
|
||||
{:paths [{:pathType "Prefix", :path "/", :backend {:service {:name "test-io-service", :port {:number 80}}}}]}}
|
||||
{:host "www.test.de",
|
||||
:http
|
||||
{:paths [{:pathType "Prefix", :path "/", :backend {:service {:name "test-io-service", :port {:number 80}}}}]}}
|
||||
{:host "test-it.de",
|
||||
:http
|
||||
{:paths [{:pathType "Prefix", :path "/", :backend {:service {:name "test-io-service", :port {:number 80}}}}]}}
|
||||
{:host "www.test-it.de",
|
||||
:http
|
||||
{:paths [{:pathType "Prefix", :path "/", :backend {:service {:name "test-io-service", :port {:number 80}}}}]}}]}
|
||||
(:spec (cut/generate-ingress {
|
||||
:ingress-name "test-io-https-ingress"
|
||||
:app-name "c4k-common-app"
|
||||
:namespace "default"
|
||||
:service-name "test-io-service" :service-port 80
|
||||
:issuer "prod" :cert-name "test-io-cert"
|
||||
:fqdns ["test.de" "www.test.de"
|
||||
"test-it.de"
|
||||
"www.test-it.de"]})))))
|
|
@ -5,67 +5,10 @@
|
|||
[clojure.spec.test.alpha :as st]
|
||||
[dda.c4k-common.ingress :as cut]))
|
||||
|
||||
(st/instrument `cut/generate-host-rule)
|
||||
(st/instrument `cut/generate-ingress)
|
||||
(st/instrument `cut/generate-certificate)
|
||||
(st/instrument `cut/generate-ingress-and-cert)
|
||||
|
||||
(deftest should-generate-rule
|
||||
(is (= {:host "test.com",
|
||||
:http
|
||||
{:paths
|
||||
[{:pathType "Prefix",
|
||||
:path "/",
|
||||
:backend
|
||||
{:service {:name "myservice", :port {:number 3000}}}}]}}
|
||||
(cut/generate-host-rule "myservice" 3000 "test.com"))))
|
||||
|
||||
(deftest should-generate-ingress
|
||||
(is (= {:apiVersion "networking.k8s.io/v1",
|
||||
:kind "Ingress",
|
||||
:metadata
|
||||
{:name "test-io-https-ingress",
|
||||
:namespace "default",
|
||||
:labels {:app.kubernetes.part-of "c4k-common-app"},
|
||||
:annotations {:traefik.ingress.kubernetes.io/router.entrypoints
|
||||
"web, websecure"
|
||||
:traefik.ingress.kubernetes.io/router.middlewares
|
||||
"default-redirect-https@kubernetescrd"
|
||||
:metallb.universe.tf/address-pool "public"}}}
|
||||
(dissoc (cut/generate-ingress
|
||||
{:issuer "prod"
|
||||
:service-name "test-io-service"
|
||||
:app-name "c4k-common-app"
|
||||
:service-port 80
|
||||
:ingress-name "test-io-https-ingress"
|
||||
:fqdns ["test.de" "www.test.de" "test-it.de"
|
||||
"www.test-it.de"]}) :spec)))
|
||||
(is (= {:tls
|
||||
[{:hosts
|
||||
["test.de" "www.test.de" "test-it.de" "www.test-it.de"],
|
||||
:secretName "test-io-cert"}]
|
||||
:rules
|
||||
[{:host "test.de",
|
||||
:http
|
||||
{:paths [{:pathType "Prefix", :path "/", :backend {:service {:name "test-io-service", :port {:number 80}}}}]}}
|
||||
{:host "www.test.de",
|
||||
:http
|
||||
{:paths [{:pathType "Prefix", :path "/", :backend {:service {:name "test-io-service", :port {:number 80}}}}]}}
|
||||
{:host "test-it.de",
|
||||
:http
|
||||
{:paths [{:pathType "Prefix", :path "/", :backend {:service {:name "test-io-service", :port {:number 80}}}}]}}
|
||||
{:host "www.test-it.de",
|
||||
:http
|
||||
{:paths [{:pathType "Prefix", :path "/", :backend {:service {:name "test-io-service", :port {:number 80}}}}]}}]}
|
||||
(:spec (cut/generate-ingress {:issuer "prod"
|
||||
:app-name "c4k-common-app"
|
||||
:service-name "test-io-service"
|
||||
:service-port 80
|
||||
:ingress-name "test-io-https-ingress"
|
||||
:cert-name "test-io-cert"
|
||||
:fqdns ["test.de" "www.test.de"
|
||||
"test-it.de"
|
||||
"www.test-it.de"]})))))
|
||||
(st/instrument `cut/generate-simple-ingress)
|
||||
|
||||
(deftest should-generate-certificate
|
||||
(is (= {:apiVersion "cert-manager.io/v1",
|
||||
|
@ -78,12 +21,29 @@
|
|||
:commonName "test.de",
|
||||
:duration "2160h",
|
||||
:renewBefore "720h",
|
||||
:dnsNames ["test.de" "test.org" "www.test.de" "www.test.org"],
|
||||
:issuerRef {:name "prod", :kind "ClusterIssuer"}}}
|
||||
(cut/generate-certificate {:fqdns ["test.de" "test.org" "www.test.de" "www.test.org"]
|
||||
:dnsNames ["test.de"],
|
||||
:issuerRef {:name "staging", :kind "ClusterIssuer"}}}
|
||||
(cut/generate-certificate {:fqdns ["test.de"]
|
||||
:app-name "c4k-common-app"
|
||||
:cert-name "test-io-cert"
|
||||
:issuer "prod"}))))
|
||||
:cert-name "test-io-cert"}))))
|
||||
|
||||
|
||||
(deftest should-generate-ingress
|
||||
(is (= {:name "test-io-https-ingress",
|
||||
:namespace "default",
|
||||
:labels {:app.kubernetes.part-of "c4k-common-app"},
|
||||
:annotations {:traefik.ingress.kubernetes.io/router.entrypoints
|
||||
"web, websecure"
|
||||
:traefik.ingress.kubernetes.io/router.middlewares
|
||||
"default-redirect-https@kubernetescrd",
|
||||
:metallb.universe.tf/address-pool "public"}}
|
||||
(:metadata (cut/generate-ingress
|
||||
{:ingress-name "test-io-https-ingress"
|
||||
:app-name "c4k-common-app"
|
||||
:service-name "test-io-service" :service-port 80
|
||||
:cert-name "myCert"
|
||||
:fqdns ["test.de"]})))))
|
||||
|
||||
|
||||
(deftest should-generate-ingress-and-cert
|
||||
(is (= [{:apiVersion "cert-manager.io/v1",
|
||||
|
@ -121,4 +81,47 @@
|
|||
:port {:number 80}}}}]}}]}}]
|
||||
(cut/generate-ingress-and-cert {:fqdns ["test.jit.si"]
|
||||
:service-name "web"
|
||||
:service-port 80}))))
|
||||
:service-port 80}))))
|
||||
|
||||
(deftest should-generate-simple-ingress
|
||||
(is (= [{:apiVersion "cert-manager.io/v1",
|
||||
:kind "Certificate",
|
||||
:metadata
|
||||
{:name "web",
|
||||
:labels {:app.kubernetes.part-of "web"},
|
||||
:namespace "default"},
|
||||
:spec
|
||||
{:secretName "web",
|
||||
:commonName "test.jit.si",
|
||||
:duration "2160h",
|
||||
:renewBefore "720h",
|
||||
:dnsNames ["test.jit.si"],
|
||||
:issuerRef {:name "staging", :kind "ClusterIssuer"}}}
|
||||
{:apiVersion "traefik.containo.us/v1alpha1",
|
||||
:kind "Middleware",
|
||||
:metadata {:name "web-ratelimit"
|
||||
:namespace "default"},
|
||||
:spec {:rateLimit {:average 10, :burst 20}}}
|
||||
{:apiVersion "networking.k8s.io/v1",
|
||||
:kind "Ingress",
|
||||
:metadata
|
||||
{:name "web",
|
||||
:namespace "default",
|
||||
:labels {:app.kubernetes.part-of "web"},
|
||||
:annotations
|
||||
{:traefik.ingress.kubernetes.io/router.entrypoints "web, websecure",
|
||||
:traefik.ingress.kubernetes.io/router.middlewares
|
||||
"default-redirect-https@kubernetescrd, default-web-ratelimit@kubernetescrd",
|
||||
:metallb.universe.tf/address-pool "public"}},
|
||||
:spec
|
||||
{:tls [{:hosts ["test.jit.si"], :secretName "web"}],
|
||||
:rules
|
||||
[{:host "test.jit.si",
|
||||
:http {:paths [{:path "/",
|
||||
:pathType "Prefix",
|
||||
:backend
|
||||
{:service {:name "web",
|
||||
:port {:number 80}}}}]}}]}}]
|
||||
(cut/generate-simple-ingress {:fqdns ["test.jit.si"]
|
||||
:service-name "web"
|
||||
:service-port 80}))))
|
|
@ -0,0 +1,59 @@
|
|||
(ns dda.c4k-common.monitoring.monitoring-internal-test
|
||||
(:require
|
||||
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
|
||||
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
|
||||
[clojure.string :as str]
|
||||
[clojure.spec.test.alpha :as st]
|
||||
[dda.c4k-common.monitoring.monitoring-internal :as cut]))
|
||||
|
||||
(st/instrument `cut/generate-stateful-set)
|
||||
(st/instrument `cut/generate-agent-config)
|
||||
(st/instrument `cut/generate-config-secret)
|
||||
|
||||
(def conf {:cluster-name "clustername"
|
||||
:cluster-stage "test"
|
||||
:grafana-cloud-url "https://some.url/with/path"})
|
||||
|
||||
(def auth {:grafana-cloud-user "user"
|
||||
:grafana-cloud-password "password"
|
||||
:hetzner-cloud-ro-token "ro-token"})
|
||||
|
||||
(def invalid-conf {:cluster-name "clustername"
|
||||
:cluster-stage "test"
|
||||
:grafana-clud-url "https://some.url/with/path"})
|
||||
|
||||
(def invalid-auth {:grafana-cloud-user "user"
|
||||
:grafana-clod-password "password"
|
||||
:hetzner-cloud-ro-token "ro-token"})
|
||||
|
||||
(deftest should-not-generate-config
|
||||
(is (thrown?
|
||||
#?(:clj Exception :cljs js/Error)
|
||||
(cut/generate-config-secret invalid-conf auth))))
|
||||
|
||||
(deftest should-not-generate-auth
|
||||
(is (thrown?
|
||||
#?(:clj Exception :cljs js/Error)
|
||||
(cut/generate-config-secret conf invalid-auth))))
|
||||
|
||||
|
||||
(deftest should-generate-prometheus-remote-write-auth
|
||||
(is (= {:username "user",
|
||||
:password "password"}
|
||||
(get-in
|
||||
(cut/generate-prometheus-config conf auth)
|
||||
[:remote_write 0 :basic_auth]))))
|
||||
|
||||
(deftest should-generate-prometheus-external-labels
|
||||
(is (= {:cluster "clustername",
|
||||
:stage "test"}
|
||||
(get-in
|
||||
(cut/generate-prometheus-config conf auth)
|
||||
[:global :external_labels]))))
|
||||
|
||||
(deftest should-generate-config
|
||||
(is (str/starts-with?
|
||||
(get-in
|
||||
(cut/generate-config-secret conf auth)
|
||||
[:stringData :prometheus.yaml])
|
||||
"global:\n scrape_interval:")))
|
|
@ -2,16 +2,12 @@
|
|||
(:require
|
||||
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
|
||||
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
|
||||
[clojure.string :as s]
|
||||
[clojure.spec.test.alpha :as st]
|
||||
[dda.c4k-common.monitoring :as cut]
|
||||
[dda.c4k-common.yaml :as yaml]
|
||||
[clojure.string :as str]))
|
||||
[dda.c4k-common.monitoring :as cut]))
|
||||
|
||||
(st/instrument `cut/generate)
|
||||
(st/instrument `cut/generate-stateful-set)
|
||||
(st/instrument `cut/generate-agent-config)
|
||||
(st/instrument `cut/generate-config)
|
||||
(st/instrument `cut/generate-auth)
|
||||
|
||||
(def conf {:cluster-name "clustername"
|
||||
:cluster-stage "test"
|
||||
|
@ -21,45 +17,11 @@
|
|||
:grafana-cloud-password "password"
|
||||
:hetzner-cloud-ro-token "ro-token"})
|
||||
|
||||
(def invalid-conf {:cluster-name "clustername"
|
||||
:cluster-stage "test"
|
||||
:grafana-clud-url "https://some.url/with/path"})
|
||||
|
||||
(def invalid-auth {:grafana-cloud-user "user"
|
||||
:grafana-clod-password "password"
|
||||
:hetzner-cloud-ro-token "ro-token"})
|
||||
|
||||
(deftest should-not-generate-config
|
||||
(is (thrown?
|
||||
#?(:clj Exception :cljs js/Error)
|
||||
(cut/generate-config invalid-conf auth))))
|
||||
|
||||
(deftest should-not-generate-auth
|
||||
(is (thrown?
|
||||
#?(:clj Exception :cljs js/Error)
|
||||
(cut/generate-config conf invalid-auth))))
|
||||
|
||||
(deftest should-generate
|
||||
(is (= 17
|
||||
(count (cut/generate conf auth)))))
|
||||
|
||||
(deftest should-generate-prometheus-remote-write-auth
|
||||
(is (= {:username "user",
|
||||
:password "password"}
|
||||
(get-in
|
||||
(cut/generate-prometheus-config conf auth)
|
||||
[:remote_write 0 :basic_auth]))))
|
||||
|
||||
(deftest should-generate-prometheus-external-labels
|
||||
(is (= {:cluster "clustername",
|
||||
:stage "test"}
|
||||
(get-in
|
||||
(cut/generate-prometheus-config conf auth)
|
||||
[:global :external_labels]))))
|
||||
|
||||
(deftest should-generate-config
|
||||
(is (s/starts-with?
|
||||
(get-in
|
||||
(cut/generate-config conf auth)
|
||||
[:stringData :prometheus.yaml])
|
||||
"global:\n scrape_interval:")))
|
||||
(count (cut/generate conf auth))))
|
||||
(is (= 16
|
||||
(count (cut/generate-config))))
|
||||
(is (= 1
|
||||
(count (cut/generate-auth conf auth)))))
|
||||
|
|
18
src/test/cljc/dda/c4k_common/namespace_test.cljc
Normal file
18
src/test/cljc/dda/c4k_common/namespace_test.cljc
Normal file
|
@ -0,0 +1,18 @@
|
|||
(ns dda.c4k-common.namespace-test
|
||||
(:require
|
||||
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
|
||||
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
|
||||
[clojure.spec.test.alpha :as st]
|
||||
[dda.c4k-common.namespace :as cut]))
|
||||
|
||||
(st/instrument `cut/generate)
|
||||
|
||||
(deftest should-generate-simple-ingress
|
||||
(is (= [{:apiVersion "v1"
|
||||
:kind "Namespace"
|
||||
:metadata {:name "default"}}]
|
||||
(cut/generate {})))
|
||||
(is (= [{:apiVersion "v1"
|
||||
:kind "Namespace"
|
||||
:metadata {:name "myapp"}}]
|
||||
(cut/generate {:namespace "myapp"}))))
|
|
@ -0,0 +1,180 @@
|
|||
(ns dda.c4k-common.postgres.postgres-internal-test
|
||||
(:require
|
||||
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
|
||||
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
|
||||
[clojure.spec.test.alpha :as st]
|
||||
[dda.c4k-common.postgres.postgres-internal :as cut]))
|
||||
|
||||
(st/instrument `cut/generate-configmap)
|
||||
(st/instrument `cut/generate-deployment)
|
||||
(st/instrument `cut/generate-persistent-volume)
|
||||
(st/instrument `cut/generate-pvc)
|
||||
(st/instrument `cut/generate-secret)
|
||||
(st/instrument `cut/generate-service)
|
||||
|
||||
(deftest should-generate-configmap
|
||||
(is (= {:name "postgres-config",
|
||||
:namespace "default"
|
||||
:labels {:app "postgres"}}
|
||||
(:metadata (cut/generate-configmap {:postgres-image "postgres:13"
|
||||
:postgres-size :2gb
|
||||
:db-name "postgres"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 10
|
||||
:pvc-storage-class-name "manual"
|
||||
:namespace "default"}))))
|
||||
(is (= {:name "postgres-config",
|
||||
:namespace "myapp"
|
||||
:labels {:app "postgres"}}
|
||||
(:metadata (cut/generate-configmap {:postgres-image "postgres:13"
|
||||
:postgres-size :2gb
|
||||
:db-name "postgres"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 10
|
||||
:pvc-storage-class-name "manual"
|
||||
:namespace "myapp"}))))
|
||||
(is (= {:postgres-db "postgres"
|
||||
:postgresql.conf
|
||||
"max_connections = 100\nwork_mem = 4MB\nshared_buffers = 512MB\n"}
|
||||
(:data (cut/generate-configmap {:postgres-image "postgres:13"
|
||||
:postgres-size :2gb
|
||||
:db-name "postgres"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 10
|
||||
:pvc-storage-class-name "manual"
|
||||
:namespace "default"}))))
|
||||
(is (= {:postgres-db "postgres"
|
||||
:postgresql.conf
|
||||
"max_connections = 700\nwork_mem = 3MB\nshared_buffers = 2048MB\n"}
|
||||
(:data (cut/generate-configmap {:postgres-image "postgres:13"
|
||||
:postgres-size :8gb
|
||||
:db-name "postgres"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 10
|
||||
:pvc-storage-class-name "manual"
|
||||
:namespace "default"}))))
|
||||
(is (= {:postgres-db "test"
|
||||
:postgresql.conf
|
||||
"max_connections = 100\nwork_mem = 4MB\nshared_buffers = 512MB\n"}
|
||||
(:data (cut/generate-configmap {:postgres-image "postgres:13"
|
||||
:postgres-size :2gb
|
||||
:db-name "test"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 10
|
||||
:pvc-storage-class-name "manual"
|
||||
:namespace "default"}))))
|
||||
)
|
||||
|
||||
(deftest should-generate-deployment
|
||||
(is (= [{:image "postgres:14"
|
||||
:name "postgresql"
|
||||
:env
|
||||
[{:name "POSTGRES_USER"
|
||||
:valueFrom
|
||||
{:secretKeyRef
|
||||
{:name "postgres-secret", :key "postgres-user"}}}
|
||||
{:name "POSTGRES_PASSWORD"
|
||||
:valueFrom
|
||||
{:secretKeyRef
|
||||
{:name "postgres-secret", :key "postgres-password"}}}
|
||||
{:name "POSTGRES_DB"
|
||||
:valueFrom
|
||||
{:configMapKeyRef
|
||||
{:name "postgres-config", :key "postgres-db"}}}]
|
||||
:ports [{:containerPort 5432, :name "postgresql"}]
|
||||
:volumeMounts
|
||||
[{:name "postgres-config-volume"
|
||||
:mountPath "/etc/postgresql/postgresql.conf"
|
||||
:subPath "postgresql.conf"
|
||||
:readOnly true}
|
||||
{:name "postgre-data-volume"
|
||||
:mountPath "/var/lib/postgresql/data"}]}]
|
||||
(get-in (cut/generate-deployment {:postgres-image "postgres:14"
|
||||
:postgres-size :2gb
|
||||
:db-name "test"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 10
|
||||
:pvc-storage-class-name "manual"
|
||||
:namespace "default"})
|
||||
[:spec :template :spec :containers])))
|
||||
(is (= {:name "postgresql",
|
||||
:namespace "myapp"}
|
||||
(:metadata (cut/generate-deployment {:postgres-image "postgres:14"
|
||||
:postgres-size :2gb
|
||||
:db-name "test"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 10
|
||||
:pvc-storage-class-name "manual"
|
||||
:namespace "myapp"})))))
|
||||
|
||||
|
||||
|
||||
(deftest should-generate-persistent-volume
|
||||
(is (= {:kind "PersistentVolume"
|
||||
:apiVersion "v1"
|
||||
:metadata
|
||||
{:name "postgres-pv-volume",
|
||||
:namespace "default"
|
||||
:labels {:type "local"}}
|
||||
:spec
|
||||
{:storageClassName "manual"
|
||||
:accessModes ["ReadWriteOnce"]
|
||||
:capacity {:storage "20Gi"}
|
||||
:hostPath {:path "xx"}}}
|
||||
(cut/generate-persistent-volume {:postgres-image "postgres:14"
|
||||
:postgres-size :2gb
|
||||
:db-name "test"
|
||||
:pvc-storage-class-name "manual"
|
||||
:postgres-data-volume-path "xx"
|
||||
:pv-storage-size-gb 20
|
||||
:namespace "default"}))))
|
||||
|
||||
|
||||
(deftest should-generate-persistent-volume-claim
|
||||
(is (= {:apiVersion "v1"
|
||||
:kind "PersistentVolumeClaim"
|
||||
:metadata
|
||||
{:name "postgres-claim",
|
||||
:namespace "default"
|
||||
:labels {:app "postgres"}}
|
||||
:spec
|
||||
{:storageClassName "local-path"
|
||||
:accessModes ["ReadWriteOnce"]
|
||||
:resources {:requests {:storage "20Gi"}}}}
|
||||
(cut/generate-pvc {:postgres-image "postgres:13"
|
||||
:postgres-size :2gb
|
||||
:db-name "postgres"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 20
|
||||
:pvc-storage-class-name "local-path"
|
||||
:namespace "default"}))))
|
||||
|
||||
|
||||
(deftest should-generate-secret
|
||||
(is (= {:apiVersion "v1"
|
||||
:kind "Secret"
|
||||
:metadata {:name "postgres-secret" :namespace "default"}
|
||||
:type "Opaque"
|
||||
:data
|
||||
{:postgres-user "eHgtdXM=", :postgres-password "eHgtcHc="}}
|
||||
(cut/generate-secret {:postgres-image "postgres:13"
|
||||
:postgres-size :2gb
|
||||
:db-name "postgres"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 20
|
||||
:pvc-storage-class-name "local-path"
|
||||
:namespace "default"}
|
||||
{:postgres-db-user "xx-us" :postgres-db-password "xx-pw"}))))
|
||||
|
||||
|
||||
(deftest should-generate-service
|
||||
(is (= {:name "postgresql-service" :namespace "default"}
|
||||
(:metadata (cut/generate-service
|
||||
{:postgres-image "postgres:13"
|
||||
:postgres-size :2gb
|
||||
:db-name "postgres"
|
||||
:postgres-data-volume-path "/var/postgres"
|
||||
:pv-storage-size-gb 20
|
||||
:pvc-storage-class-name "local-path"
|
||||
:namespace "default"})))))
|
||||
|
|
@ -3,132 +3,63 @@
|
|||
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
|
||||
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
|
||||
[clojure.spec.test.alpha :as st]
|
||||
[dda.c4k-common.test-helper :as ct]
|
||||
[dda.c4k-common.postgres :as cut]))
|
||||
|
||||
(st/instrument `cut/generate-config)
|
||||
(st/instrument `cut/generate-deployment)
|
||||
(st/instrument `cut/generate-configmap)
|
||||
(st/instrument `cut/generate-persistent-volume)
|
||||
(st/instrument `cut/generate-pvc)
|
||||
(st/instrument `cut/generate-secret)
|
||||
(st/instrument `cut/generate-service)
|
||||
(st/instrument `cut/generate)
|
||||
(st/instrument `cut/generate-config)
|
||||
(st/instrument `cut/generate-auth)
|
||||
|
||||
(deftest should-generate-config
|
||||
(deftest should-generate-configmap
|
||||
(is (= {:postgres-db "postgres"
|
||||
:postgresql.conf
|
||||
"max_connections = 100\nwork_mem = 4MB\nshared_buffers = 512MB\n"}
|
||||
(:data (cut/generate-config))))
|
||||
(is (= {:postgres-db "postgres"
|
||||
:postgresql.conf
|
||||
"max_connections = 700\nwork_mem = 3MB\nshared_buffers = 2048MB\n"}
|
||||
(:data (cut/generate-config {:postgres-size :8gb}))))
|
||||
(is (= {:postgres-db "test"
|
||||
:postgresql.conf
|
||||
"max_connections = 100\nwork_mem = 4MB\nshared_buffers = 512MB\n"}
|
||||
(:data (cut/generate-config {:db-name "test"}))))
|
||||
)
|
||||
|
||||
(deftest should-generate-config-diff
|
||||
(is (= {:postgres-db-c1 "postgres",
|
||||
:postgres-db-c2 "test",
|
||||
:postgresql.conf-c1 "max_connections = 100\nwork_mem = 4MB\nshared_buffers = 512MB\n",
|
||||
:postgresql.conf-c2 "max_connections = 700\nwork_mem = 3MB\nshared_buffers = 2048MB\n"}
|
||||
(ct/map-diff (cut/generate-config) (cut/generate-config {:db-name "test" :postgres-size :8gb})))))
|
||||
(:data (cut/generate-configmap)))))
|
||||
|
||||
(deftest should-generate-persistent-volume
|
||||
(is (= {:kind "PersistentVolume"
|
||||
:apiVersion "v1"
|
||||
:metadata
|
||||
{:name "postgres-pv-volume", :labels {:type "local"}}
|
||||
{:name "postgres-pv-volume", :namespace "default" :labels {:type "local"}}
|
||||
:spec
|
||||
{:storageClassName "manual"
|
||||
:accessModes ["ReadWriteOnce"]
|
||||
:capacity {:storage "10Gi"}
|
||||
:hostPath {:path "xx"}}}
|
||||
(cut/generate-persistent-volume {:postgres-data-volume-path "xx"})))
|
||||
(is (= {:kind "PersistentVolume"
|
||||
:apiVersion "v1"
|
||||
:metadata
|
||||
{:name "postgres-pv-volume", :labels {:type "local"}}
|
||||
:spec
|
||||
{:storageClassName "manual"
|
||||
:accessModes ["ReadWriteOnce"]
|
||||
:capacity {:storage "20Gi"}
|
||||
:hostPath {:path "xx"}}}
|
||||
(cut/generate-persistent-volume {:postgres-data-volume-path "xx"
|
||||
:pv-storage-size-gb 20}))))
|
||||
(cut/generate-persistent-volume {:postgres-data-volume-path "xx"}))))
|
||||
|
||||
(deftest should-generate-persistent-volume-diff
|
||||
(is (= {:storage-c1 "10Gi", :storage-c2 "20Gi",
|
||||
:path-c1 "/var/postgres", :path-c2 "xx"}
|
||||
(ct/map-diff (cut/generate-persistent-volume {})
|
||||
(cut/generate-persistent-volume {:postgres-data-volume-path "xx"
|
||||
:pv-storage-size-gb 20})))))
|
||||
|
||||
(deftest should-generate-persistent-volume-claim
|
||||
(is (= {:apiVersion "v1"
|
||||
:kind "PersistentVolumeClaim"
|
||||
:metadata
|
||||
{:name "postgres-claim", :labels {:app "postgres"}}
|
||||
{:name "postgres-claim", :namespace "default" :labels {:app "postgres"}}
|
||||
:spec
|
||||
{:storageClassName "manual"
|
||||
:accessModes ["ReadWriteOnce"]
|
||||
:resources {:requests {:storage "10Gi"}}}}
|
||||
(cut/generate-pvc {})))
|
||||
(is (= {:apiVersion "v1"
|
||||
:kind "PersistentVolumeClaim"
|
||||
:metadata
|
||||
{:name "postgres-claim", :labels {:app "postgres"}}
|
||||
:spec
|
||||
{:storageClassName "local-path"
|
||||
:accessModes ["ReadWriteOnce"]
|
||||
:resources {:requests {:storage "20Gi"}}}}
|
||||
(cut/generate-pvc {:pv-storage-size-gb 20
|
||||
:pvc-storage-class-name "local-path"}))))
|
||||
(cut/generate-pvc {}))))
|
||||
|
||||
(deftest should-generate-persistent-volume-claim-diff
|
||||
(is (= {:storageClassName-c1 "manual", :storageClassName-c2 "local-path",
|
||||
:storage-c1 "10Gi", :storage-c2 "20Gi"}
|
||||
(ct/map-diff (cut/generate-pvc {})
|
||||
(cut/generate-pvc {:pv-storage-size-gb 20
|
||||
:pvc-storage-class-name "local-path"})))))
|
||||
|
||||
(deftest should-generate-secret
|
||||
(is (= {:apiVersion "v1"
|
||||
:kind "Secret"
|
||||
:metadata {:name "postgres-secret"}
|
||||
:type "Opaque"
|
||||
:data
|
||||
{:postgres-user "eHgtdXM=", :postgres-password "eHgtcHc="}}
|
||||
(is (= {:apiVersion "v1",
|
||||
:kind "Secret",
|
||||
:metadata {:name "postgres-secret", :namespace "default"},
|
||||
:type "Opaque",
|
||||
:data {:postgres-user "eHgtdXM=", :postgres-password "eHgtcHc="}}
|
||||
(cut/generate-secret {:postgres-db-user "xx-us" :postgres-db-password "xx-pw"}))))
|
||||
|
||||
(deftest should-generate-deployment
|
||||
(is (= [{:image "postgres:14"
|
||||
:name "postgresql"
|
||||
:env
|
||||
[{:name "POSTGRES_USER"
|
||||
:valueFrom
|
||||
{:secretKeyRef
|
||||
{:name "postgres-secret", :key "postgres-user"}}}
|
||||
{:name "POSTGRES_PASSWORD"
|
||||
:valueFrom
|
||||
{:secretKeyRef
|
||||
{:name "postgres-secret", :key "postgres-password"}}}
|
||||
{:name "POSTGRES_DB"
|
||||
:valueFrom
|
||||
{:configMapKeyRef
|
||||
{:name "postgres-config", :key "postgres-db"}}}]
|
||||
:ports [{:containerPort 5432, :name "postgresql"}]
|
||||
:volumeMounts
|
||||
[{:name "postgres-config-volume"
|
||||
:mountPath "/etc/postgresql/postgresql.conf"
|
||||
:subPath "postgresql.conf"
|
||||
:readOnly true}
|
||||
{:name "postgre-data-volume"
|
||||
:mountPath "/var/lib/postgresql/data"}]}]
|
||||
(get-in (cut/generate-deployment {:postgres-image "postgres:14"})
|
||||
[:spec :template :spec :containers]))))
|
||||
|
||||
(deftest should-generate-deployment-diff
|
||||
(is (= {:image-c1 "postgres:13", :image-c2 "postgres:14"}
|
||||
(ct/map-diff (cut/generate-deployment) (cut/generate-deployment {:postgres-image "postgres:14"})))))
|
||||
(deftest should-generate
|
||||
(is (= 6
|
||||
(count (cut/generate {}
|
||||
{:postgres-db-user "user"
|
||||
:postgres-db-password "password"}))))
|
||||
(is (= 5
|
||||
(count (cut/generate-config {}))))
|
||||
(is (= 1
|
||||
(count (cut/generate-auth {}
|
||||
{:postgres-db-user "user"
|
||||
:postgres-db-password "password"})))))
|
|
@ -16,6 +16,18 @@
|
|||
(is (false? (cut/fqdn-string? "123.456.789")))
|
||||
(is (false? (cut/fqdn-string? "test&test.de"))))
|
||||
|
||||
(deftest test-ipv4-string?
|
||||
(is (true? (cut/ipv4-string? "127.0.0.1")))
|
||||
(is (true? (cut/ipv4-string? "192.168.192.168")))
|
||||
(is (true? (cut/ipv4-string? "1.2.3.4")))
|
||||
(is (false? (cut/ipv4-string? "1.a.2.b")))
|
||||
(is (false? (cut/ipv4-string? "f::f::f::f"))))
|
||||
|
||||
(deftest test-ipv6-string?
|
||||
(is (true? (cut/ipv6-string? "2a01:4f8:c012:cb41::1")))
|
||||
(is (false? (cut/ipv6-string? "1.a.2.b")))
|
||||
(is (false? (cut/ipv6-string? "f::f::f::f"))))
|
||||
|
||||
(deftest test-string-of-separated-by?
|
||||
(is (true? (cut/string-of-separated-by? cut/bash-env-string? #":" "abcd")))
|
||||
(is (true? (cut/string-of-separated-by? cut/bash-env-string? #":" "abcd:efgh")))
|
||||
|
|
34
src/test/cljs/dda/c4k_common/browser_test.cljs
Normal file
34
src/test/cljs/dda/c4k_common/browser_test.cljs
Normal file
|
@ -0,0 +1,34 @@
|
|||
(ns dda.c4k-common.browser-test
|
||||
(:require
|
||||
[cljs.test :refer-macros [deftest is are testing run-tests]]
|
||||
[clojure.spec.test.alpha :as st]
|
||||
[dda.c4k-common.browser :as cut]))
|
||||
|
||||
(st/instrument `cut/print-debug)
|
||||
(st/instrument `cut/get-element-by-id)
|
||||
(st/instrument `cut/get-content-from-element)
|
||||
(st/instrument `cut/deserialize-content)
|
||||
(st/instrument `cut/get-content-value-from-element)
|
||||
(st/instrument `cut/set-validation-result!)
|
||||
(st/instrument `cut/validate!)
|
||||
(st/instrument `cut/set-output!)
|
||||
(st/instrument `cut/set-form-validated!)
|
||||
(st/instrument `cut/create-js-obj-from-html)
|
||||
(st/instrument `cut/append-to-c4k-content)
|
||||
(st/instrument `cut/append-hickory)
|
||||
(st/instrument `cut/generate-feedback-tag)
|
||||
(st/instrument `cut/generate-label)
|
||||
(st/instrument `cut/generate-br)
|
||||
(st/instrument `cut/generate-input-field)
|
||||
(st/instrument `cut/generate-text-area)
|
||||
(st/instrument `cut/generate-button)
|
||||
(st/instrument `cut/generate-output)
|
||||
(st/instrument `cut/generate-needs-validation)
|
||||
(st/instrument `cut/generate-group)
|
||||
|
||||
(deftest should-deserialize-content
|
||||
(is (= (cut/deserialize-content " " identity true) nil))
|
||||
(is (= (cut/deserialize-content "test" keyword false) :test))
|
||||
(is (= (cut/deserialize-content "test" identity false) "test"))
|
||||
(is (= (cut/deserialize-content "test" identity true) "test")))
|
||||
|
14
src/test/cljs/dda/c4k_common/macros_test.cljs
Normal file
14
src/test/cljs/dda/c4k_common/macros_test.cljs
Normal file
|
@ -0,0 +1,14 @@
|
|||
(ns dda.c4k-common.macros-test
|
||||
(:require
|
||||
[cljs.test :refer-macros [deftest is]]
|
||||
[dda.c4k-common.macros :refer-macros [inline-resources]]))
|
||||
|
||||
(deftest should-count-inline-resources
|
||||
(is (= 4 (count (inline-resources "ingress")))))
|
||||
|
||||
(deftest should-inline-resources
|
||||
(let [resource-path (fn [name] (str "dda/c4k_common/inline_resources_test/" name))
|
||||
inlined-resources (inline-resources "dda/c4k_common/inline_resources_test")]
|
||||
(is (= "1" (get inlined-resources (resource-path "inline_resource_1.yaml"))))
|
||||
(is (= "2" (get inlined-resources (resource-path "inline_resource_2.yaml"))))
|
||||
(is (= "3" (get inlined-resources (resource-path "inline_resource_3.yaml"))))))
|
3
src/test/resources/common-test/valid-auth.yaml
Normal file
3
src/test/resources/common-test/valid-auth.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
mon-auth:
|
||||
grafana-cloud-user: "user"
|
||||
grafana-cloud-password: "password"
|
4
src/test/resources/common-test/valid-config.yaml
Normal file
4
src/test/resources/common-test/valid-config.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
mon-cfg:
|
||||
grafana-cloud-url: "url-for-your-prom-remote-write-endpoint"
|
||||
cluster-name: "forgejo"
|
||||
cluster-stage: "test"
|
BIN
src/test/resources/dda/c4k_common/inline_jar_test/test.jar
Normal file
BIN
src/test/resources/dda/c4k_common/inline_jar_test/test.jar
Normal file
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
1
|
|
@ -0,0 +1 @@
|
|||
2
|
|
@ -0,0 +1 @@
|
|||
3
|
Loading…
Reference in a new issue