From 998f77fc6f4fd68c953d04fd9ff8bd56ab27cecb Mon Sep 17 00:00:00 2001 From: this-is-tobi Date: Fri, 16 Aug 2024 23:51:10 +0200 Subject: [PATCH] chore: :technologist: add development environement script --- .dockerignore | 1 + Dockerfile | 1 + README.md | 5 +- kind/README.md | 30 +++ 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, 381 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..9f8f0eb --- /dev/null +++ b/kind/README.md @@ -0,0 +1,30 @@ +# 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) + +## Commands + +```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 +``` + +## 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/) 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