From b969d6c5ea315c08f3590fb066bc4901416ac5dd Mon Sep 17 00:00:00 2001 From: this-is-tobi Date: Fri, 16 Aug 2024 23:51:10 +0200 Subject: [PATCH 1/3] chore: :technologist: add development environement script --- .dockerignore | 1 + Dockerfile | 1 + README.md | 5 +- kind/README.md | 37 ++++ kind/configs/kind-config.yml | 21 +++ kind/configs/traefik-values.yml | 19 ++ kind/docker-compose.yml | 7 + kind/resources/external-dns-values.yml | 37 ++++ kind/resources/ingress.yml | 17 ++ kind/run.sh | 245 +++++++++++++++++++++++++ 10 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 kind/README.md create mode 100644 kind/configs/kind-config.yml create mode 100644 kind/configs/traefik-values.yml create mode 100644 kind/docker-compose.yml create mode 100644 kind/resources/external-dns-values.yml create mode 100644 kind/resources/ingress.yml create mode 100755 kind/run.sh diff --git a/.dockerignore b/.dockerignore index 039ef46..ca3b34f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,6 @@ .github/ ci/ +kind/ .release-please-manifest.json release-please-manifest.json README.md diff --git a/Dockerfile b/Dockerfile index d33f4ef..6bed458 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ # Dev stage FROM --platform=${BUILDPLATFORM:-linux/amd64} docker.io/golang:1.22.4 AS dev + RUN go install github.com/air-verse/air@latest WORKDIR /go/src/app COPY go.mod go.mod diff --git a/README.md b/README.md index 2aa253a..f708454 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,9 @@ domainFilters: ["subzone.d1.dev.example.com"] provider: name: webhook webhook: - image: ghcr.io/titigmr/external-dns-midaas-webhook - tag: latest + image: + repository: ghcr.io/titigmr/external-dns-midaas-webhook + tag: latest env: - name: PROVIDER_DNS_ZONE_SUFFIX value: "dev.example.com" diff --git a/kind/README.md b/kind/README.md new file mode 100644 index 0000000..eb48093 --- /dev/null +++ b/kind/README.md @@ -0,0 +1,37 @@ +# Run kubernetes locally with docker + +## Prerequisite + +Download & install on your local machine : +- [kind](https://github.com/kubernetes-sigs/kind) +- [kubectl](https://github.com/kubernetes/kubectl) +- [helm](https://github.com/helm/helm) + +Declare your images into the `./docker-compose.yml` file, it is used for parralel build and load images into Kind nodes. + +## Commands + +Put this directory in your git project, then start using the script : + +```sh +# Start kind cluster +sh ./run.sh -c create + +# Build and load docker-compose images into the cluster +sh ./run.sh -c build + +# Stop kind cluster +sh ./run.sh -c delete + +# Start kind cluster, build and load images and deploy app +sh ./run.sh -c dev +``` + +> [!TIP] +> See script helper by running `sh ./run.hs -h` + +## Cluster + +One single node is deployed but it can be customized in `./configs/kind-config.yml`. The cluster comes with [Traefik](https://doc.traefik.io/traefik/providers/kubernetes-ingress/) or [Nginx](https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx) ingress controller installed with port mapping on both ports `80` and `443`. + +The node is using `extraMounts` to provide a volume binding between host working directory and `/app` to give the ability to bind mount volumes into containers during development. diff --git a/kind/configs/kind-config.yml b/kind/configs/kind-config.yml new file mode 100644 index 0000000..6c27e25 --- /dev/null +++ b/kind/configs/kind-config.yml @@ -0,0 +1,21 @@ +--- +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: +- role: control-plane + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP + extraMounts: + - hostPath: ./ + containerPath: /app diff --git a/kind/configs/traefik-values.yml b/kind/configs/traefik-values.yml new file mode 100644 index 0000000..a028b87 --- /dev/null +++ b/kind/configs/traefik-values.yml @@ -0,0 +1,19 @@ +--- +providers: + kubernetesCRD: + namespaces: + - default + - ingress-traefik + kubernetesIngress: + namespaces: + - default + - ingress-traefik + +ports: + web: + nodePort: 80 + websecure: + nodePort: 443 + +service: + type: NodePort diff --git a/kind/docker-compose.yml b/kind/docker-compose.yml new file mode 100644 index 0000000..82eb3f3 --- /dev/null +++ b/kind/docker-compose.yml @@ -0,0 +1,7 @@ +services: + server: + image: external-dns-midaas-webhook:dev + build: + context: .. + dockerfile: ./Dockerfile + target: dev diff --git a/kind/resources/external-dns-values.yml b/kind/resources/external-dns-values.yml new file mode 100644 index 0000000..2475b02 --- /dev/null +++ b/kind/resources/external-dns-values.yml @@ -0,0 +1,37 @@ +# -- How DNS records are synchronized between sources and providers; available values are `sync` & `upsert-only`. +policy: sync +# -- Specify the registry for storing ownership and labels. +# Valid values are `txt`, `aws-sd`, `dynamodb` & `noop`. +# If `noop` midaas manage all records on zone +registry: txt +# can restrict zone +provider: + name: webhook + webhook: + image: + repository: external-dns-midaas-webhook + tag: dev + pullPolicy: Never + env: + - name: TSIG_ZONE_foo + value: bar + - name: API_LOG_LEVEL + value: DEBUG + - name: PROVIDER_SKIP_TLS_VERIFY + value: "true" + extraVolumeMounts: + - name: dev-workspace + mountPath: /go/src/app + env: + - name: PROVIDER_DNS_ZONE_SUFFIX + value: "dev.example.com" + - name: PROVIDER_WS_URL + value: https://midaas.example.com/midaas/ws" + - name: TSIG_ZONE_foo + value: bar +podSecurityContext: + runAsNonRoot: false +extraVolumes: +- name: dev-workspace + hostPath: + path: /app \ No newline at end of file diff --git a/kind/resources/ingress.yml b/kind/resources/ingress.yml new file mode 100644 index 0000000..0149d34 --- /dev/null +++ b/kind/resources/ingress.yml @@ -0,0 +1,17 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: test +spec: + rules: + - host: test.foo.dev.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: test + port: + number: 8080 \ No newline at end of file diff --git a/kind/run.sh b/kind/run.sh new file mode 100755 index 0000000..2ef7cc1 --- /dev/null +++ b/kind/run.sh @@ -0,0 +1,245 @@ +#!/bin/bash + +set -e +set -o pipefail + +# Colorize terminal +red='\e[0;31m' +no_color='\033[0m' + +# Get versions +DOCKER_VERSION="$(docker --version)" + +# Default +PROJECT_DIR="$(git rev-parse --show-toplevel)" +SCRIPT_PATH="$(cd -- "$(dirname "$0")" >/dev/null 2>&1; pwd -P)" +INGRESS_CONTROLLER="nginx" +COMPOSE_FILE="$SCRIPT_PATH/docker-compose.yml" + +# Declare script helper +TEXT_HELPER="\nThis script aims to manage a local kubernetes cluster using Kind also known as Kubernetes in Docker. +Following flags are available: + + -c Command tu run. Multiple commands can be provided as a comma separated list. + Available commands are : + create - Create kind cluster. + clean - Delete images in kind cluster (keep only infra resources and ingress controller). + delete - Delete kind cluster. + build - Build and load docker images from compose file into cluster nodes. + load - Load docker images from compose file into cluster nodes. + dev - Run application in development mode. + prod - Run application in production mode. + + -d Domains to add in /etc/hosts for local services resolution. + Comma separated list, this will require sudo. + + -f Path to the docker-compose file that will be used with Kind (default to '$COMPOSE_FILE'). + + -i Ingress controller to install (available values are 'nginx' or 'traefik', default to '$INGRESS_CONTROLLER'). + + -t Tag used to deploy application images. + If the 'CI' environment variable is set to 'true', it will use the + '$PROJECT_DIR/$HELM_DIR/values.yaml' images instead of local ones. + + -h Print script help.\n\n" + +print_help() { + printf "$TEXT_HELPER" +} + +# Parse options +while getopts hc:d:f:i:t: flag; do + case "${flag}" in + c) + COMMAND=${OPTARG};; + d) + DOMAINS=${OPTARG};; + f) + COMPOSE_FILE=${OPTARG};; + i) + INGRESS_CONTROLLER=${OPTARG};; + t) + TAG=${OPTARG};; + h | *) + print_help + exit 0;; + esac +done + + +# Functions +install_kind() { + printf "\n\n${red}[kind wrapper].${no_color} Install kind...\n\n" + if [ "$(uname)" = "Linux" ]; then + OS="linux" + elif [ "$(uname)" = "Darwin" ]; then + OS="darwin" + else + printf "\n\nNo installation available for your system, plese refer to the installation guide\n\n" + exit 0 + fi + + if [ "$(uname -m)" = "x86_64" ]; then + ARCH="amd64" + elif [ "$(uname -m)" = "arm64" ] || [ "$(uname -m)" = "aarch64" ]; then + ARCH="arm64" + fi + + KIND_VERSION="0.23.0" + curl -Lo ./kind "https://kind.sigs.k8s.io/dl/v${VERSION}/kind-${OS}-${ARCH}" + chmod +x ./kind + mv ./kind /usr/local/bin/kind + + printf "\n\n$(kind --version) installed\n\n" +} + +create () { + if [ -z "$(kind get clusters | grep 'kind')" ]; then + printf "\n\n${red}[kind wrapper].${no_color} Create Kind cluster\n\n" + kind create cluster --config $SCRIPT_PATH/configs/kind-config.yml + if [ "$INGRESS_CONTROLLER" = "nginx" ]; then + printf "\n\n${red}[kind wrapper].${no_color} Install Nginx ingress controller\n\n" + kubectl --context kind-kind apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml + sleep 20 + kubectl --context kind-kind wait --namespace ingress-nginx \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/component=controller \ + --timeout=90s + elif [ "$INGRESS_CONTROLLER" = "traefik" ]; then + printf "\n\n${red}[kind wrapper].${no_color} Install traefik ingress controller\n\n" + helm --kube-context kind-kind repo add traefik https://traefik.github.io/charts && helm repo update + helm --kube-context kind-kind upgrade \ + --install \ + --wait \ + --namespace ingress-traefik \ + --create-namespace \ + --values $SCRIPTPATH/configs/traefik-values.yml \ + traefik traefik/traefik + fi + fi +} + +build () { + printf "\n\n${red}[kind wrapper].${no_color} Build images into cluster node\n\n" + cd $(dirname "$COMPOSE_FILE") && docker buildx bake --file $(basename "$COMPOSE_FILE") --load && cd - +} + +load () { + printf "\n\n${red}[kind wrapper].${no_color} Load images into cluster node\n\n" + kind load docker-image $(cat "$COMPOSE_FILE" | docker run -i --rm mikefarah/yq -o t '.services | map(select(.build) | .image)') +} + +clean () { + printf "\n\n${red}[kind wrapper].${no_color} Clean Kind cluster\n\n" + kubectl delete ingress test + helm uninstall external-dns +} + +delete () { + printf "\n\n${red}[kind wrapper].${no_color} Delete Kind cluster\n\n" + kind delete cluster +} + +deploy () { + printf "\n\n${red}[kind wrapper].${no_color} Deploy application in development mode\n\n" + helm repo add external-dns https://kubernetes-sigs.github.io/external-dns + helm upgrade --install external-dns external-dns/external-dns -f "$SCRIPT_PATH/resources/external-dns-values.yml" + kubectl apply -f "$SCRIPT_PATH/resources/ingress.yml" +} + + +# Script condition +if [ -z "$(kind --version)" ]; then + while true; do + read -p "\nYou need kind to run this script. Do you wish to install kind?\n" yn + case $yn in + [Yy]*) + install_kind;; + [Nn]*) + exit 1;; + *) + echo "\nPlease answer yes or no.\n";; + esac + done +fi + +if [[ "$COMMAND" =~ "build" ]] || [[ "$COMMAND" =~ "load" ]] && [ ! -f "$(readlink -f $COMPOSE_FILE)" ]; then + echo "\nDocker compose file $COMPOSE_FILE does not exist.\n" + print_help + exit 1 +fi + + +# Add local services to /etc/hosts +if [ ! -z "$DOMAINS" ]; then + printf "\n\n${red}[kind wrapper].${no_color} Add services local domains to /etc/hosts\n\n" + FORMATED_DOMAINS="$(echo "$DOMAINS" | sed 's/,/\ /g')" + if [ "$(grep -c "$FORMATED_DOMAINS" /etc/hosts)" -ge 1 ]; then + printf "\n\n${red}[kind wrapper].${no_color} Services local domains already added to /etc/hosts\n\n" + else + sudo sh -c "echo $'\n\n# Kind\n127.0.0.1 $FORMATED_DOMAINS' >> /etc/hosts" + printf "\n\n${red}[kind wrapper].${no_color} Services local domains successfully added to /etc/hosts\n\n" + fi +fi + + +# Deploy cluster with trefik ingress controller +if [[ "$COMMAND" =~ "create" ]]; then + create & + JOB_ID_CREATE="$!" +fi + + +# Build and load images into cluster nodes +if [[ "$COMMAND" =~ "build" ]]; then + build & + JOB_ID_BUILD="$!" + [ ! -z $JOB_ID_CREATE ] && wait $JOB_ID_CREATE + wait $JOB_ID_BUILD + load & + JOB_ID_LOAD="$!" +fi + + +# Load images into cluster nodes +if [[ "$COMMAND" =~ "load" ]]; then + [ ! -z $JOB_ID_CREATE ] && wait $JOB_ID_CREATE + load & + JOB_ID_LOAD="$!" +fi + + +# Clean cluster application resources +if [ "$COMMAND" = "clean" ]; then + clean +fi + + +# Delete cluster +if [ "$COMMAND" = "delete" ]; then + delete +fi + + +# Deploy application in dev or test mode +if [[ "$COMMAND" =~ "deploy" ]]; then + [ ! -z $JOB_ID_CREATE ] && wait $JOB_ID_CREATE + [ ! -z $JOB_ID_BUILD ] && wait $JOB_ID_BUILD + [ ! -z $JOB_ID_LOAD ] && wait $JOB_ID_LOAD + deploy +fi + + +# Deploy application in dev or test mode +if [[ "$COMMAND" =~ "dev" ]]; then + create & + JOB_ID_CREATE="$!" + build & + JOB_ID_BUILD="$!" + wait $JOB_ID_CREATE + wait $JOB_ID_BUILD + load & + JOB_ID_LOAD="$!" + wait $JOB_ID_LOAD + deploy +fi From 74180c5e116500496841bec826bd42f5b37b84c4 Mon Sep 17 00:00:00 2001 From: titigmr Date: Sat, 17 Aug 2024 05:07:04 +0200 Subject: [PATCH 2/3] chore: add first version midaas mock --- .gitignore | 1 + contribute/Makefile | 31 +++++++ contribute/midaas-ws/Dockerfile | 7 ++ contribute/midaas-ws/main.py | 114 ++++++++++++++++++++++++++ contribute/midaas-ws/requirements.txt | 3 + midaas/midaas.go | 6 +- 6 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 contribute/Makefile create mode 100644 contribute/midaas-ws/Dockerfile create mode 100644 contribute/midaas-ws/main.py create mode 100644 contribute/midaas-ws/requirements.txt diff --git a/.gitignore b/.gitignore index bce9047..d5ba8c1 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ go.work go.work.sum tmp/ +__pycache__ \ No newline at end of file diff --git a/contribute/Makefile b/contribute/Makefile new file mode 100644 index 0000000..3ed6a9f --- /dev/null +++ b/contribute/Makefile @@ -0,0 +1,31 @@ +MIDAAS_IMAGE := api-midaas:v1 +WEBHOOK_IMAGE := webhook:v1 +ENV_MIDAAS_KEYNAME := test +ENV_MIDAAS_KEYVALUE := test +ENV_MIDAAS_ZONES := dev.local +CACHE_CONFIG := /tmp/cluster-name.txt +CLUSTER_NAME := go + +build-midaas: + docker build -t ${MIDAAS_IMAGE} midaas-ws/ + +push-midaas: + kind load docker-image ${MIDAAS_IMAGE} --name ${CLUSTER_NAME} + +delete-midaas: + @kubectl delete pod midaas --ignore-not-found + @kubectl delete svc midaas --ignore-not-found + +deploy-midaas: delete-midaas + @kubectl run --image ${MIDAAS_IMAGE} --expose=true --port 8080 \ + --env "MIDAAS_KEYNAME=${ENV_MIDAAS_KEYNAME}" \ + --env "MIDAAS_KEYVALUE=${ENV_MIDAAS_KEYVALUE}" \ + --env "MIDAAS_ZONES=${ENV_MIDAAS_ZONES}" midaas + @echo "\n\n-------------------" + @echo "Kubernetes midaas service is listening on port 8080" + @echo "Use 'TSIG_${ENV_MIDAAS_KEYNAME}=${ENV_MIDAAS_KEYVALUE}' on environment variable in webhook" + @echo "For manual actions, OpenAPI schema is available at path /docs" + + +start-midaas: build-midaas push-midaas deploy-midaas + diff --git a/contribute/midaas-ws/Dockerfile b/contribute/midaas-ws/Dockerfile new file mode 100644 index 0000000..a55e887 --- /dev/null +++ b/contribute/midaas-ws/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.9 +WORKDIR /app +COPY ./requirements.txt . +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt +COPY main.py . +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080", "--reload"] + diff --git a/contribute/midaas-ws/main.py b/contribute/midaas-ws/main.py new file mode 100644 index 0000000..c6acf89 --- /dev/null +++ b/contribute/midaas-ws/main.py @@ -0,0 +1,114 @@ +import os +import pathlib +import json +from fastapi import FastAPI, Response, Request, Body +from pydantic import BaseModel + + +KEYNAME = os.environ.get("MIDAAS_KEYNAME", "test") +KEYVALUE = os.environ.get("MIDAAS_KEYVALUE", "test") +ZONES = os.environ.get("MIDAAS_ZONE", "dev.local,test.local") +ALL_ZONES = ZONES.split(",") + +INFO = f""" +Informations about current midaas instance: +- keyname: {KEYNAME} - (MIDAAS_KEYNAME env var) +- keyvalue: {KEYVALUE} - (MIDAAS_KEYVALUE env var) + +Availables zones are comma separated: {ZONES} (MIDAAS_ZONE env var) +""" +app = FastAPI() + + +class TTLDelete(BaseModel): + keyname: str + keyvalue: str + + +class TTLCreate(BaseModel): + ttl: int + keyname: str + keyvalue: str + + +def check_TSIG(keyname, keyvalue): + return keyname == KEYNAME and KEYVALUE == keyvalue + + +def create_zone(file): + print(f"Creating zone on {file}") + with open(file, "w+") as f: + json.dump({}, f) + + +def create_zone_if_not_exist(file): + p = pathlib.Path(file) + if not p.exists(): + create_zone(file) + else: + # check if zone is not empty + with open(file, "r") as f: + content = f.read() + if not content: + create_zone(file) + + +def read_zone(file): + with open(file, "r") as f: + data = json.loads(f.read()) + return data + + +@app.get("/ws/{domaine}") +async def list_domain(request: Request, domaine): + records = {} + for zone in ALL_ZONES: + if zone in domaine.strip().lower(): + file = f"/tmp/{zone}" + create_zone_if_not_exist(file) + records = read_zone(file) + return records + return records + + +@app.get("/healthz") +async def health(): + return {"status": "OK"} + + +@app.put("/ws/{domaine}/{type}/{valeur}") +def create(response: Response, request: Request, domaine: str, type: str, valeur: str, TTL: TTLCreate) -> dict: + if not check_TSIG(keyname=TTL.keyname, keyvalue=TTL.keyvalue): + return {"status": "ERROR", "message": "wrong credentials"} + + for zone in ALL_ZONES: + if zone in domaine: + file = f"/tmp/{zone}" + create_zone_if_not_exist(file=file) + data = read_zone(file=file) + with open(file, "w+") as f: + updated_data = data | {domaine: {"type": type, + "valeur": valeur, + "ttl": TTL.ttl}} + json.dump(updated_data, f) + return {"status": "OK"} + return {"status": "ERROR", "message": "zone not available"} + + +@app.delete("/ws/{domaine}/{type}/{valeur}") +def delete(response: Response, request: Request, domaine: str, type: str, valeur: str, TTL: TTLDelete) -> dict: + if not check_TSIG(keyname=TTL.keyname, keyvalue=TTL.keyvalue): + return {"status": "ERROR", "message": "wrong credentials"} + + for zone in ALL_ZONES: + if zone in domaine: + file = f"/tmp/{zone}" + create_zone_if_not_exist(file=file) + data = read_zone(file=file) + print(domaine, data) + with open(file, "w") as f: + if domaine in data: + data.pop(domaine) + json.dump(data, f) + return {"status": "OK"} + return {"status": "ERROR", "message": "no domain"} diff --git a/contribute/midaas-ws/requirements.txt b/contribute/midaas-ws/requirements.txt new file mode 100644 index 0000000..42612ea --- /dev/null +++ b/contribute/midaas-ws/requirements.txt @@ -0,0 +1,3 @@ +fastapi +requests +uvicorn diff --git a/midaas/midaas.go b/midaas/midaas.go index 523ce1c..479df2a 100644 --- a/midaas/midaas.go +++ b/midaas/midaas.go @@ -229,9 +229,9 @@ func RequestUrl(method string, url string, body Body) error { bodyResponse, err := io.ReadAll(response.Body) defer response.Body.Close() - if err != nil { - return err - } + if err != nil { + return err + } if response.StatusCode > 200 { return fmt.Errorf(string(bodyResponse)) } From b2ba88ab7115808141e7bce3c4c8b46d667213aa Mon Sep 17 00:00:00 2001 From: titigmr Date: Sat, 17 Aug 2024 21:15:05 +0200 Subject: [PATCH 3/3] chore: :zap: add stack for dev locally --- Makefile | 117 +++++++++ README.md | 63 ++++- contribute/Makefile | 31 --- contribute/README.md | 38 +++ .../kind/kind-config.yaml | 4 +- .../kind/traefik-values.yaml | 10 +- contribute/midaas-ws/main.py | 45 ++-- .../ressources/external-dns-values.yaml | 35 +-- .../ressources/ingress.yaml | 4 +- kind/README.md | 37 --- kind/docker-compose.yml | 7 - kind/run.sh | 245 ------------------ 12 files changed, 271 insertions(+), 365 deletions(-) create mode 100644 Makefile delete mode 100644 contribute/Makefile create mode 100644 contribute/README.md rename kind/configs/kind-config.yml => contribute/kind/kind-config.yaml (91%) rename kind/configs/traefik-values.yml => contribute/kind/traefik-values.yaml (57%) rename kind/resources/external-dns-values.yml => contribute/ressources/external-dns-values.yaml (57%) rename kind/resources/ingress.yml => contribute/ressources/ingress.yaml (80%) delete mode 100644 kind/README.md delete mode 100644 kind/docker-compose.yml delete mode 100755 kind/run.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dde1fa2 --- /dev/null +++ b/Makefile @@ -0,0 +1,117 @@ +SHELL := /bin/bash +DOCKER := $(shell type -p docker) +KIND := $(shell type -p kind) +HELM := $(shell type -p helm) +KUBECTL := $(shell type -p helm) + +WEBHOOK_IMAGE := external-dns-midaas-webhook:dev +WEBHOOK_FOLDER := ./ + +MIDAAS_IMAGE := api-midaas:dev +MIDAAS_FOLDER := ./contribute/midaas-ws/ + +export MIDAAS_WS_URL ?= http://midaas.default:8080/ws/ +export MIDAAS_DEV_SUFFIX ?= dev.local +export MIDAAS_ENV_KEYNAME ?= d1 +export MIDAAS_ENV_KEYVALUE ?= test +export MIDAAS_ENV_ZONES ?= $(MIDAAS_ENV_KEYNAME).$(MIDAAS_DEV_SUFFIX) + +KIND_CLUSTER_NAME ?= midaas +KIND_INGRESS_CONTROLLER = NGINX + +# Commons targets + +all: deploy-MIDAAS deploy-WEBHOOK create-test-ingress + +clean: delete-cluster delete-image-MIDAAS delete-image-WEBHOOK + +create-test-ingress: create-cluster check-prerequisites-kubectl + @kubectl apply -f ./contribute/ressources/ingress.yaml + +logs-%: + @kubectl logs -f deployments/external-dns -c $* + +midaas-get-zone: + @kubectl exec midaas cat /tmp/$(MIDAAS_ENV_KEYNAME).$(MIDAAS_DEV_SUFFIX) | jq + +# Check prerequisites + +check-prerequisites-docker: +ifeq ("$(wildcard ${DOCKER})","") + @echo "docker not found" ; exit 1 +endif +check-prerequisites-kind: +ifeq ("$(wildcard ${KIND})","") + @echo "'kind' not found" ; exit 1 +endif +check-prerequisites-kubectl: +ifeq ("$(wildcard ${KUBECTL})","") + @echo "'kubectl' not found" ; exit 1 +endif +check-prerequisites-helm: +ifeq ("$(wildcard ${HELM})","") + @echo "'helm' not found" ; exit 1 +endif + +# Kind targets + +create-cluster: check-prerequisites-kind +ifeq ($(shell kind get clusters |grep $(KIND_CLUSTER_NAME)), $(KIND_CLUSTER_NAME)) + @echo "Kind cluster '$(KIND_CLUSTER_NAME)' already exists, skipping" +else + @kind create cluster --name $(KIND_CLUSTER_NAME) --config ./contribute/kind/kind-config.yaml + @kubectl config use-context kind-$(KIND_CLUSTER_NAME) +endif + +delete-cluster: check-prerequisites-kind +ifeq ($(shell kind get clusters |grep $(KIND_CLUSTER_NAME)), $(KIND_CLUSTER_NAME)) + @kind delete cluster --name $(KIND_CLUSTER_NAME) +endif + +start-ingress-controller: create-cluster +ifeq ($(KIND_INGRESS_CONTROLLER), NGINX) + @if [ ! -s /tmp/external-dns-nginx.yaml ]; then curl -Ls https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml > /tmp/external-dns-nginx.yaml; fi + @kubectl apply -f /tmp/external-dns-nginx.yaml + @kubectl wait --namespace ingress-nginx \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/component=controller \ + --timeout=90s +else ifeq ($(KIND_INGRESS_CONTROLLER), TRAEFIK) + @echo traefik +endif + +# Docker build and push targets +build-%: check-prerequisites-docker + @if [ $* = WEBHOOK ]; then docker build --target dev ${OPTIONS} -t ${$*_IMAGE} ${$*_FOLDER}; else docker build ${OPTIONS} -t ${$*_IMAGE} ${$*_FOLDER}; fi + +push-%: check-prerequisites-kind + kind load docker-image ${$*_IMAGE} --name $(KIND_CLUSTER_NAME) + +delete-image-%: check-prerequisites-docker + docker rmi ${$*_IMAGE} + +# Midaas deployment targets +delete-MIDAAS: create-cluster check-prerequisites-kubectl + @kubectl delete pod midaas --ignore-not-found + @kubectl delete svc midaas --ignore-not-found + +deploy-MIDAAS: create-cluster check-prerequisites-kubectl build-MIDAAS push-MIDAAS delete-MIDAAS + @kubectl run --image $(MIDAAS_IMAGE) --expose=true --port 8080 \ + --env "MIDAAS_KEYNAME=$(MIDAAS_ENV_KEYNAME)" \ + --env "MIDAAS_KEYVALUE=$(MIDAAS_ENV_KEYVALUE)" \ + --env "MIDAAS_ZONES=$(MIDAAS_ENV_ZONES)" midaas + @echo "Kubernetes midaas service is listening on port 8080" + + +# Webhook deployment targets + +delete-WEBHOOK: create-cluster check-prerequisites-helm + @if [ "external-dns" == "$(shell helm ls -f external-dns -o json |jq -r .[].name)" ]; then helm delete external-dns; else echo "No external-dns release is currently running"; fi + +deploy-WEBHOOK: start-ingress-controller check-prerequisites-helm build-WEBHOOK push-WEBHOOK delete-WEBHOOK + @echo "Adding repository" + @helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/ + @envsubst < ./contribute/ressources/external-dns-values.yaml > /tmp/external-dns-values.yaml + @helm upgrade --force --install external-dns external-dns/external-dns -f /tmp/external-dns-values.yaml + @echo "external DNS is running with webhook in sidecar" + diff --git a/README.md b/README.md index f708454..fbbe582 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/ 1. Create the helm values file `external-dns-midaas-values.yaml`: ```yaml +sources: + - ingress # -- How DNS records are synchronized between sources and providers; available values are `sync` & `upsert-only`. policy: sync # -- Specify the registry for storing ownership and labels. @@ -24,7 +26,7 @@ policy: sync # If `noop` midaas manage all records on zone registry: txt # can restrict zone -domainFilters: ["subzone.d1.dev.example.com"] +domainFilters: [] provider: name: webhook webhook: @@ -43,7 +45,7 @@ provider: 2. Create helm deployment: ```sh -helm install external-dns external-dns -f external-dns-midaas-values.yaml +helm install external-dns external-dns/external-dns -f external-dns-midaas-values.yaml ``` ## Parameters references @@ -65,7 +67,62 @@ For example, `TSIG_ZONE_d1` with `PROVIDER_DNS_ZONE_SUFFIX` with `dev.example.co ## Local development -🚧 Work in progress. +### Prerequisite + +Download and install on your local machine: +- `make` in Debian/Ubuntu distrib with +```bash + sudo apt install build-essential +``` +- [docker](https://docs.docker.com/engine/install/) +- [kubectl](https://github.com/kubernetes/kubectl) +- [kind](https://github.com/kubernetes-sigs/kind) +- [helm](https://github.com/helm/helm) + +### Usage + + +You can create a development stack locally with this command: + +```sh +make +``` + +This target do the following target successively: +- `create-cluster` : create a `kind` cluster locally with an ingress controller configured +- `deploy-MIDAAS` : build, push and deploy `midaas` [webservice mock](./contribute/midaas-ws/) in the cluster +- `deploy-WEBHOOK` : build, push and deploy `external-dns` with the midaas webhook in development mode. You can modify the code with hot reload. + +For example, for restarting the webhook: + +```bash +make deploy-WEBHOOK +``` + +Don't forget create an ingress for trigger `external-dns`, an [example](./contribute/ressources/ingress.yaml) can be created with: + +```bash +make create-test-ingress +``` + +You can read the containers logs with: + +```bash +make logs-webhook +``` + +or + +```bash +make logs-external-dns +``` + +To clean all the components + +```sh +make clean +``` + ## Contributions diff --git a/contribute/Makefile b/contribute/Makefile deleted file mode 100644 index 3ed6a9f..0000000 --- a/contribute/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -MIDAAS_IMAGE := api-midaas:v1 -WEBHOOK_IMAGE := webhook:v1 -ENV_MIDAAS_KEYNAME := test -ENV_MIDAAS_KEYVALUE := test -ENV_MIDAAS_ZONES := dev.local -CACHE_CONFIG := /tmp/cluster-name.txt -CLUSTER_NAME := go - -build-midaas: - docker build -t ${MIDAAS_IMAGE} midaas-ws/ - -push-midaas: - kind load docker-image ${MIDAAS_IMAGE} --name ${CLUSTER_NAME} - -delete-midaas: - @kubectl delete pod midaas --ignore-not-found - @kubectl delete svc midaas --ignore-not-found - -deploy-midaas: delete-midaas - @kubectl run --image ${MIDAAS_IMAGE} --expose=true --port 8080 \ - --env "MIDAAS_KEYNAME=${ENV_MIDAAS_KEYNAME}" \ - --env "MIDAAS_KEYVALUE=${ENV_MIDAAS_KEYVALUE}" \ - --env "MIDAAS_ZONES=${ENV_MIDAAS_ZONES}" midaas - @echo "\n\n-------------------" - @echo "Kubernetes midaas service is listening on port 8080" - @echo "Use 'TSIG_${ENV_MIDAAS_KEYNAME}=${ENV_MIDAAS_KEYVALUE}' on environment variable in webhook" - @echo "For manual actions, OpenAPI schema is available at path /docs" - - -start-midaas: build-midaas push-midaas deploy-midaas - diff --git a/contribute/README.md b/contribute/README.md new file mode 100644 index 0000000..81f894b --- /dev/null +++ b/contribute/README.md @@ -0,0 +1,38 @@ +# Run all stack locally with docker + +## Kind cluster + +One single node is deployed but it can be customized in `./kind/kind-config.yml`. The cluster comes with [Traefik](https://doc.traefik.io/traefik/providers/kubernetes-ingress/) or [Nginx](https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx) ingress controller installed with port mapping on both ports `8080` and `8443`. + +The node is using `extraMounts` to provide a volume binding between host working directory and `/app` to give the ability to bind mount volumes into containers during development. + + +## Midaas Webservice + +A wrapper of midaas is available for development on folder `./midaas-ws`. Note that tool not really do dns records. It only writes fake domain on container filesystem. + +This webservice is written in python with `Fastapi` framework. The webservice listen on 3 endpoints: +- `GET` - `/ws/{domaine}` : retrieve all domains for a specific zone +- `PUT` - `/ws/{domaine}/{type}/{valeur}` : add or modify a DNS record +You must add this body in the request: +```json +{"ttl": 0, "keyname": "string", "keyvalue": "string"} +``` +- `DELETE` - `/ws/{domaine}/{type}/{valeur}` : add or modify a DNS +You must add this body in the request: +```json +{"keyname": "string", "keyvalue": "string"} +``` + +The midaas webservice can be configured with the following environment variables: + +| Name | Description | Default | +| --------------- | ---------------------- | --------- | +| MIDAAS_KEYNAME | TSIG Keyname | test | +| MIDAAS_KEYVALUE | TSIG Keyvalue | test | +| MIDAAS_ZONE | Zone managed by MiDaas | dev.local | + + +## External-DNS Locally + +:construction: \ No newline at end of file diff --git a/kind/configs/kind-config.yml b/contribute/kind/kind-config.yaml similarity index 91% rename from kind/configs/kind-config.yml rename to contribute/kind/kind-config.yaml index 6c27e25..62fff9a 100644 --- a/kind/configs/kind-config.yml +++ b/contribute/kind/kind-config.yaml @@ -11,10 +11,10 @@ nodes: node-labels: "ingress-ready=true" extraPortMappings: - containerPort: 80 - hostPort: 80 + hostPort: 8080 protocol: TCP - containerPort: 443 - hostPort: 443 + hostPort: 8443 protocol: TCP extraMounts: - hostPath: ./ diff --git a/kind/configs/traefik-values.yml b/contribute/kind/traefik-values.yaml similarity index 57% rename from kind/configs/traefik-values.yml rename to contribute/kind/traefik-values.yaml index a028b87..6fae1b7 100644 --- a/kind/configs/traefik-values.yml +++ b/contribute/kind/traefik-values.yaml @@ -1,9 +1,7 @@ --- providers: kubernetesCRD: - namespaces: - - default - - ingress-traefik + enabled: false kubernetesIngress: namespaces: - default @@ -11,9 +9,9 @@ providers: ports: web: - nodePort: 80 + hostPort: 80 websecure: - nodePort: 443 + hostPort: 443 service: - type: NodePort + type: ClusterIP \ No newline at end of file diff --git a/contribute/midaas-ws/main.py b/contribute/midaas-ws/main.py index c6acf89..3e3734a 100644 --- a/contribute/midaas-ws/main.py +++ b/contribute/midaas-ws/main.py @@ -3,20 +3,15 @@ import json from fastapi import FastAPI, Response, Request, Body from pydantic import BaseModel +import logging +logger = logging.getLogger('uvicorn.error') -KEYNAME = os.environ.get("MIDAAS_KEYNAME", "test") +KEYNAME = "ddns-key." + os.environ.get("MIDAAS_KEYNAME", "d1") KEYVALUE = os.environ.get("MIDAAS_KEYVALUE", "test") -ZONES = os.environ.get("MIDAAS_ZONE", "dev.local,test.local") +ZONES = os.environ.get("MIDAAS_ZONES", "d1.dev.local") ALL_ZONES = ZONES.split(",") -INFO = f""" -Informations about current midaas instance: -- keyname: {KEYNAME} - (MIDAAS_KEYNAME env var) -- keyvalue: {KEYVALUE} - (MIDAAS_KEYVALUE env var) - -Availables zones are comma separated: {ZONES} (MIDAAS_ZONE env var) -""" app = FastAPI() @@ -32,11 +27,16 @@ class TTLCreate(BaseModel): def check_TSIG(keyname, keyvalue): - return keyname == KEYNAME and KEYVALUE == keyvalue + if not keyname == KEYNAME and KEYVALUE == keyvalue: + logger.info(f"Keyname or Keyvalue not match") + logger.info(f"Keyname: {keyname} with {KEYNAME}") + logger.info(f"Keyvalue: {keyname} with {KEYNAME}") + return False + return True def create_zone(file): - print(f"Creating zone on {file}") + logger.info(f"Creating zone on {file}") with open(file, "w+") as f: json.dump({}, f) @@ -61,12 +61,14 @@ def read_zone(file): @app.get("/ws/{domaine}") async def list_domain(request: Request, domaine): + logger.info(f"GET on url: {request.url}") records = {} for zone in ALL_ZONES: if zone in domaine.strip().lower(): file = f"/tmp/{zone}" create_zone_if_not_exist(file) records = read_zone(file) + logger.info(f"Zone content : {records}") return records return records @@ -78,6 +80,7 @@ async def health(): @app.put("/ws/{domaine}/{type}/{valeur}") def create(response: Response, request: Request, domaine: str, type: str, valeur: str, TTL: TTLCreate) -> dict: + logger.info(f"PUT on url: {request.url}") if not check_TSIG(keyname=TTL.keyname, keyvalue=TTL.keyvalue): return {"status": "ERROR", "message": "wrong credentials"} @@ -87,16 +90,21 @@ def create(response: Response, request: Request, domaine: str, type: str, valeur create_zone_if_not_exist(file=file) data = read_zone(file=file) with open(file, "w+") as f: - updated_data = data | {domaine: {"type": type, - "valeur": valeur, - "ttl": TTL.ttl}} + if type == "CNAME": + valeur += "." + key = f"{domaine}./{type}/{valeur}" + updated_data = data | {key: {"type": type, + "valeur": valeur, + "ttl": TTL.ttl}} json.dump(updated_data, f) + logger.info(f"Zone content : {updated_data}") return {"status": "OK"} return {"status": "ERROR", "message": "zone not available"} @app.delete("/ws/{domaine}/{type}/{valeur}") def delete(response: Response, request: Request, domaine: str, type: str, valeur: str, TTL: TTLDelete) -> dict: + logger.info(f"DELETE on url: {request.url}") if not check_TSIG(keyname=TTL.keyname, keyvalue=TTL.keyvalue): return {"status": "ERROR", "message": "wrong credentials"} @@ -105,10 +113,13 @@ def delete(response: Response, request: Request, domaine: str, type: str, valeur file = f"/tmp/{zone}" create_zone_if_not_exist(file=file) data = read_zone(file=file) - print(domaine, data) with open(file, "w") as f: - if domaine in data: - data.pop(domaine) + if type == "CNAME": + valeur += "." + key = f"{domaine}./{type}/{valeur}" + if key in data: + data.pop(key) json.dump(data, f) + logger.info(f"Zone content : {data}") return {"status": "OK"} return {"status": "ERROR", "message": "no domain"} diff --git a/kind/resources/external-dns-values.yml b/contribute/ressources/external-dns-values.yaml similarity index 57% rename from kind/resources/external-dns-values.yml rename to contribute/ressources/external-dns-values.yaml index 2475b02..287bd60 100644 --- a/kind/resources/external-dns-values.yml +++ b/contribute/ressources/external-dns-values.yaml @@ -3,35 +3,40 @@ policy: sync # -- Specify the registry for storing ownership and labels. # Valid values are `txt`, `aws-sd`, `dynamodb` & `noop`. # If `noop` midaas manage all records on zone -registry: txt +registry: noop # can restrict zone -provider: +provider: name: webhook webhook: image: repository: external-dns-midaas-webhook tag: dev - pullPolicy: Never + pullPolicy: Always env: - - name: TSIG_ZONE_foo - value: bar + - name: GOFLAGS + value: -buildvcs=false + - name: TSIG_ZONE_${MIDAAS_ENV_KEYNAME} + value: ${MIDAAS_ENV_KEYVALUE} - name: API_LOG_LEVEL value: DEBUG - - name: PROVIDER_SKIP_TLS_VERIFY - value: "true" + - name: PROVIDER_DNS_ZONE_SUFFIX + value: ${MIDAAS_DEV_SUFFIX} + - name: PROVIDER_WS_URL + value: ${MIDAAS_WS_URL} + - name: https_proxy + value: ${https_proxy} + - name: no_proxy + value: .default,{no_proxy} extraVolumeMounts: - name: dev-workspace mountPath: /go/src/app - env: - - name: PROVIDER_DNS_ZONE_SUFFIX - value: "dev.example.com" - - name: PROVIDER_WS_URL - value: https://midaas.example.com/midaas/ws" - - name: TSIG_ZONE_foo - value: bar podSecurityContext: runAsNonRoot: false extraVolumes: - name: dev-workspace hostPath: - path: /app \ No newline at end of file + path: /app + +sources: + - ingress +logLevel: debug \ No newline at end of file diff --git a/kind/resources/ingress.yml b/contribute/ressources/ingress.yaml similarity index 80% rename from kind/resources/ingress.yml rename to contribute/ressources/ingress.yaml index 0149d34..5637007 100644 --- a/kind/resources/ingress.yml +++ b/contribute/ressources/ingress.yaml @@ -5,13 +5,13 @@ metadata: name: test spec: rules: - - host: test.foo.dev.example.com + - host: test.d1.dev.local http: paths: - path: / pathType: Prefix backend: service: - name: test + name: midaas port: number: 8080 \ No newline at end of file diff --git a/kind/README.md b/kind/README.md deleted file mode 100644 index eb48093..0000000 --- a/kind/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Run kubernetes locally with docker - -## Prerequisite - -Download & install on your local machine : -- [kind](https://github.com/kubernetes-sigs/kind) -- [kubectl](https://github.com/kubernetes/kubectl) -- [helm](https://github.com/helm/helm) - -Declare your images into the `./docker-compose.yml` file, it is used for parralel build and load images into Kind nodes. - -## Commands - -Put this directory in your git project, then start using the script : - -```sh -# Start kind cluster -sh ./run.sh -c create - -# Build and load docker-compose images into the cluster -sh ./run.sh -c build - -# Stop kind cluster -sh ./run.sh -c delete - -# Start kind cluster, build and load images and deploy app -sh ./run.sh -c dev -``` - -> [!TIP] -> See script helper by running `sh ./run.hs -h` - -## Cluster - -One single node is deployed but it can be customized in `./configs/kind-config.yml`. The cluster comes with [Traefik](https://doc.traefik.io/traefik/providers/kubernetes-ingress/) or [Nginx](https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx) ingress controller installed with port mapping on both ports `80` and `443`. - -The node is using `extraMounts` to provide a volume binding between host working directory and `/app` to give the ability to bind mount volumes into containers during development. diff --git a/kind/docker-compose.yml b/kind/docker-compose.yml deleted file mode 100644 index 82eb3f3..0000000 --- a/kind/docker-compose.yml +++ /dev/null @@ -1,7 +0,0 @@ -services: - server: - image: external-dns-midaas-webhook:dev - build: - context: .. - dockerfile: ./Dockerfile - target: dev diff --git a/kind/run.sh b/kind/run.sh deleted file mode 100755 index 2ef7cc1..0000000 --- a/kind/run.sh +++ /dev/null @@ -1,245 +0,0 @@ -#!/bin/bash - -set -e -set -o pipefail - -# Colorize terminal -red='\e[0;31m' -no_color='\033[0m' - -# Get versions -DOCKER_VERSION="$(docker --version)" - -# Default -PROJECT_DIR="$(git rev-parse --show-toplevel)" -SCRIPT_PATH="$(cd -- "$(dirname "$0")" >/dev/null 2>&1; pwd -P)" -INGRESS_CONTROLLER="nginx" -COMPOSE_FILE="$SCRIPT_PATH/docker-compose.yml" - -# Declare script helper -TEXT_HELPER="\nThis script aims to manage a local kubernetes cluster using Kind also known as Kubernetes in Docker. -Following flags are available: - - -c Command tu run. Multiple commands can be provided as a comma separated list. - Available commands are : - create - Create kind cluster. - clean - Delete images in kind cluster (keep only infra resources and ingress controller). - delete - Delete kind cluster. - build - Build and load docker images from compose file into cluster nodes. - load - Load docker images from compose file into cluster nodes. - dev - Run application in development mode. - prod - Run application in production mode. - - -d Domains to add in /etc/hosts for local services resolution. - Comma separated list, this will require sudo. - - -f Path to the docker-compose file that will be used with Kind (default to '$COMPOSE_FILE'). - - -i Ingress controller to install (available values are 'nginx' or 'traefik', default to '$INGRESS_CONTROLLER'). - - -t Tag used to deploy application images. - If the 'CI' environment variable is set to 'true', it will use the - '$PROJECT_DIR/$HELM_DIR/values.yaml' images instead of local ones. - - -h Print script help.\n\n" - -print_help() { - printf "$TEXT_HELPER" -} - -# Parse options -while getopts hc:d:f:i:t: flag; do - case "${flag}" in - c) - COMMAND=${OPTARG};; - d) - DOMAINS=${OPTARG};; - f) - COMPOSE_FILE=${OPTARG};; - i) - INGRESS_CONTROLLER=${OPTARG};; - t) - TAG=${OPTARG};; - h | *) - print_help - exit 0;; - esac -done - - -# Functions -install_kind() { - printf "\n\n${red}[kind wrapper].${no_color} Install kind...\n\n" - if [ "$(uname)" = "Linux" ]; then - OS="linux" - elif [ "$(uname)" = "Darwin" ]; then - OS="darwin" - else - printf "\n\nNo installation available for your system, plese refer to the installation guide\n\n" - exit 0 - fi - - if [ "$(uname -m)" = "x86_64" ]; then - ARCH="amd64" - elif [ "$(uname -m)" = "arm64" ] || [ "$(uname -m)" = "aarch64" ]; then - ARCH="arm64" - fi - - KIND_VERSION="0.23.0" - curl -Lo ./kind "https://kind.sigs.k8s.io/dl/v${VERSION}/kind-${OS}-${ARCH}" - chmod +x ./kind - mv ./kind /usr/local/bin/kind - - printf "\n\n$(kind --version) installed\n\n" -} - -create () { - if [ -z "$(kind get clusters | grep 'kind')" ]; then - printf "\n\n${red}[kind wrapper].${no_color} Create Kind cluster\n\n" - kind create cluster --config $SCRIPT_PATH/configs/kind-config.yml - if [ "$INGRESS_CONTROLLER" = "nginx" ]; then - printf "\n\n${red}[kind wrapper].${no_color} Install Nginx ingress controller\n\n" - kubectl --context kind-kind apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml - sleep 20 - kubectl --context kind-kind wait --namespace ingress-nginx \ - --for=condition=ready pod \ - --selector=app.kubernetes.io/component=controller \ - --timeout=90s - elif [ "$INGRESS_CONTROLLER" = "traefik" ]; then - printf "\n\n${red}[kind wrapper].${no_color} Install traefik ingress controller\n\n" - helm --kube-context kind-kind repo add traefik https://traefik.github.io/charts && helm repo update - helm --kube-context kind-kind upgrade \ - --install \ - --wait \ - --namespace ingress-traefik \ - --create-namespace \ - --values $SCRIPTPATH/configs/traefik-values.yml \ - traefik traefik/traefik - fi - fi -} - -build () { - printf "\n\n${red}[kind wrapper].${no_color} Build images into cluster node\n\n" - cd $(dirname "$COMPOSE_FILE") && docker buildx bake --file $(basename "$COMPOSE_FILE") --load && cd - -} - -load () { - printf "\n\n${red}[kind wrapper].${no_color} Load images into cluster node\n\n" - kind load docker-image $(cat "$COMPOSE_FILE" | docker run -i --rm mikefarah/yq -o t '.services | map(select(.build) | .image)') -} - -clean () { - printf "\n\n${red}[kind wrapper].${no_color} Clean Kind cluster\n\n" - kubectl delete ingress test - helm uninstall external-dns -} - -delete () { - printf "\n\n${red}[kind wrapper].${no_color} Delete Kind cluster\n\n" - kind delete cluster -} - -deploy () { - printf "\n\n${red}[kind wrapper].${no_color} Deploy application in development mode\n\n" - helm repo add external-dns https://kubernetes-sigs.github.io/external-dns - helm upgrade --install external-dns external-dns/external-dns -f "$SCRIPT_PATH/resources/external-dns-values.yml" - kubectl apply -f "$SCRIPT_PATH/resources/ingress.yml" -} - - -# Script condition -if [ -z "$(kind --version)" ]; then - while true; do - read -p "\nYou need kind to run this script. Do you wish to install kind?\n" yn - case $yn in - [Yy]*) - install_kind;; - [Nn]*) - exit 1;; - *) - echo "\nPlease answer yes or no.\n";; - esac - done -fi - -if [[ "$COMMAND" =~ "build" ]] || [[ "$COMMAND" =~ "load" ]] && [ ! -f "$(readlink -f $COMPOSE_FILE)" ]; then - echo "\nDocker compose file $COMPOSE_FILE does not exist.\n" - print_help - exit 1 -fi - - -# Add local services to /etc/hosts -if [ ! -z "$DOMAINS" ]; then - printf "\n\n${red}[kind wrapper].${no_color} Add services local domains to /etc/hosts\n\n" - FORMATED_DOMAINS="$(echo "$DOMAINS" | sed 's/,/\ /g')" - if [ "$(grep -c "$FORMATED_DOMAINS" /etc/hosts)" -ge 1 ]; then - printf "\n\n${red}[kind wrapper].${no_color} Services local domains already added to /etc/hosts\n\n" - else - sudo sh -c "echo $'\n\n# Kind\n127.0.0.1 $FORMATED_DOMAINS' >> /etc/hosts" - printf "\n\n${red}[kind wrapper].${no_color} Services local domains successfully added to /etc/hosts\n\n" - fi -fi - - -# Deploy cluster with trefik ingress controller -if [[ "$COMMAND" =~ "create" ]]; then - create & - JOB_ID_CREATE="$!" -fi - - -# Build and load images into cluster nodes -if [[ "$COMMAND" =~ "build" ]]; then - build & - JOB_ID_BUILD="$!" - [ ! -z $JOB_ID_CREATE ] && wait $JOB_ID_CREATE - wait $JOB_ID_BUILD - load & - JOB_ID_LOAD="$!" -fi - - -# Load images into cluster nodes -if [[ "$COMMAND" =~ "load" ]]; then - [ ! -z $JOB_ID_CREATE ] && wait $JOB_ID_CREATE - load & - JOB_ID_LOAD="$!" -fi - - -# Clean cluster application resources -if [ "$COMMAND" = "clean" ]; then - clean -fi - - -# Delete cluster -if [ "$COMMAND" = "delete" ]; then - delete -fi - - -# Deploy application in dev or test mode -if [[ "$COMMAND" =~ "deploy" ]]; then - [ ! -z $JOB_ID_CREATE ] && wait $JOB_ID_CREATE - [ ! -z $JOB_ID_BUILD ] && wait $JOB_ID_BUILD - [ ! -z $JOB_ID_LOAD ] && wait $JOB_ID_LOAD - deploy -fi - - -# Deploy application in dev or test mode -if [[ "$COMMAND" =~ "dev" ]]; then - create & - JOB_ID_CREATE="$!" - build & - JOB_ID_BUILD="$!" - wait $JOB_ID_CREATE - wait $JOB_ID_BUILD - load & - JOB_ID_LOAD="$!" - wait $JOB_ID_LOAD - deploy -fi