Skip to content

Commit

Permalink
Merge pull request #9 from appuio/feat/local-env
Browse files Browse the repository at this point in the history
Add tooling to spin up local environments
  • Loading branch information
simu authored Dec 14, 2021
2 parents 84a450b + efe4112 commit 735c69b
Show file tree
Hide file tree
Showing 13 changed files with 2,307 additions and 4 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ _archive/
crd*.yaml

/control-api
/local-env/kind-kubeconfig-*
/local-env/realm.json
/local-env/.created
/local-env/.kind-setup_complete

# Go releaser
dist/
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ MAKEFLAGS += --no-builtin-variables
PROJECT_ROOT_DIR = .
include Makefile.vars.mk

localenv_make := $(MAKE) -C local-env

.PHONY: help
help: ## Show this help
@grep -E -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
Expand Down Expand Up @@ -56,6 +58,14 @@ build.docker: $(BIN_FILENAME) ## Build the docker image
clean: ## Cleans up the generated resources
rm -rf dist/ cover.out $(BIN_FILENAME) || true

.PHONY: local-env
local-env-setup: ## Setup local kind-based dev environment
$(localenv_make) setup

.PHONY: local-env-clean-setup
local-env-clean: ## Clean the local dev environment
$(localenv_make) clean-setup

###
### Assets
###
Expand Down
10 changes: 10 additions & 0 deletions Makefile.vars.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ KUSTOMIZE ?= go run sigs.k8s.io/kustomize/kustomize/v4

# Image URL to use all building/pushing image targets
GHCR_IMG ?= ghcr.io/appuio/control-api:$(IMG_TAG)

# Local dev environment setup
localenv_dir ?= $(CURDIR)/$(PROJECT_ROOT_DIR)/local-env
localenv_dir_created = $(localenv_dir)/.created

# Kind config
KIND_NODE_VERSION ?= v1.22.1
KIND ?= go run sigs.k8s.io/kind
KIND_KUBECONFIG ?= $(localenv_dir)/kind-kubeconfig-$(KIND_NODE_VERSION)
KIND_CLUSTER ?= control-api-$(KIND_NODE_VERSION)
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# control-api


### Generate Kubernetes code
## Generate Kubernetes code

If you make changes to the CRD structs you'll need to run code generation.
This can be done with make:
Expand All @@ -17,14 +17,28 @@ This can be done with make:
make generate
```

### Building
## Building

See `make help` for a list of build targets.

* `make build`: Build binary for linux/amd64
* `make build -e GOOS=darwin -e GOARCH=arm64`: Build binary for macos/arm64
* `make build.docker`: Build Docker image for local environment

### Install CRDs
## Install CRDs

CRDs can be either installed on the cluster by running `kubectl apply -k config/crd/apiextensions.k8s.io/v1`.
CRDs can be installed on the cluster by running `kubectl apply -k config/crd/apiextensions.k8s.io/v1`.

## Local development environment

You can setup a [kind]-based local environment with

```bash
make local-env-setup
```

See the [local-env/README.md](./local-env/README.md) for more details on the local environment setup.

Please be aware that the productive deployment of the control-api may run on a different Kubernetes distribution than [kind].

[kind]: https://kind.sigs.k8s.io/
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ require (
k8s.io/klog/v2 v2.9.0 // indirect
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c // indirect
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect
sigs.k8s.io/kind v0.11.1 // indirect
sigs.k8s.io/kustomize/api v0.10.1 // indirect
sigs.k8s.io/kustomize/cmd/config v0.10.2 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.0 // indirect
Expand Down
29 changes: 29 additions & 0 deletions go.sum

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions local-env/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Set Shell to bash, otherwise some targets fail with dash/zsh etc.
SHELL := /bin/bash

# Disable built-in rules
MAKEFLAGS += --no-builtin-rules
MAKEFLAGS += --no-builtin-variables
.SUFFIXES:
.SECONDARY:

PROJECT_ROOT_DIR = ..
include ../Makefile.vars.mk
include kind.mk

uname_s := $(shell uname -s)
ifeq ($(uname_s),Linux)
xargs := xargs --no-run-if-empty
else
xargs := xargs
endif

.DEFAULT_GOAL := help

.PHONY: setup
setup: export KUBECONFIG = $(KIND_KUBECONFIG)
setup: $(localenv_dir_created) kind-setup ## Setup the local environment

.PHONY: clean-setup
clean-setup: export KUBECONFIG = $(KIND_KUBECONFIG)
clean-setup: kind-clean ## Clean the local environment (e.g. to rerun the setup)
rm $(localenv_dir_created) || true

.PHONY: help
help: ## Show this help
@grep -E -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

###
### Artifacts
###

# a marker file must be created, because the date of the
# directory may update when content in it is created/updated,
# which would cause a rebuild / re-initialization of dependants
$(localenv_dir_created):
@touch $(localenv_dir_created)
26 changes: 26 additions & 0 deletions local-env/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Local development environment

We provide a script and some templates to setup a local test environment based on [kind](https://kind.sigs.k8s.io/).
The templates can be found in directory `templates/`.

## Prerequisites

* `bash`
* `sed`
* `kind`
* `kubectl`
* `kubelogin` as `kubectl-oidc_login`

The setup script will provide links to the install guides for `kubectl` and `kubelogin` if no appropriate command is found.

## Installation

The `setup-kind.sh` script will guide you through the setup.
There are some steps that you have to perform manually on a Keycloak instance, which the script prompts you for.
The script defaults to VSHN's APPUiO Dev Keycloak instance, but you can provide an URL pointing to a different instance during the install process.

Since the setup script requires a few arguments, we provide a make target to run the script:

```
make setup
```
30 changes: 30 additions & 0 deletions local-env/kind.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
kind_marker := $(localenv_dir)/.kind-setup_complete

curl_args ?= --location --fail --silent --show-error

.DEFAULT_TARGET: kind-setup

.PHONY: kind-setup
kind-setup: export KUBECONFIG = $(KIND_KUBECONFIG)
kind-setup: $(kind_marker) $(localenv_dir_created) ## Creates the kind cluster

.PHONY: kind-clean
kind-clean: export KUBECONFIG = $(KIND_KUBECONFIG)
kind-clean: ## Remove the kind Cluster
@$(KIND) delete cluster --name $(KIND_CLUSTER) || true
@rm $(kind_marker) $(KIND_KUBECONFIG) || true

###
### Artifacts
###

$(KIND_KUBECONFIG): export KUBECONFIG = $(KIND_KUBECONFIG)
$(KIND_KUBECONFIG):
$(localenv_dir)/setup-kind.sh "$(KIND)" "$(KIND_CLUSTER)" "$(KIND_NODE_VERSION)" "$(KIND_KUBECONFIG)"
@kubectl version
@kubectl cluster-info

$(kind_marker): export KUBECONFIG = $(KIND_KUBECONFIG)
$(kind_marker): $(KIND_KUBECONFIG)
@kubectl config use-context kind-$(KIND_CLUSTER)
@touch $(kind_marker)
93 changes: 93 additions & 0 deletions local-env/setup-kind.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/bin/bash
# vim:sts=2:ts=2:et:sw=2:tw=0

set -euo pipefail

readonly script_dir=$(dirname "$0")
readonly kind_cmd="${1:-kind}"
readonly kind_cluster="${2:-control-api-localenv}"
readonly kind_node_version="${3:-v1.22.1}"
readonly kind_kubeconfig="${4:-"${script_dir}/control-api.kubeconfig"}"

export KUBECONFIG="${kind_kubeconfig}"

keycloak_url=https://id.dev.appuio.cloud

step() {
echo
echo -e "$1"
read -n 1 -s -r -p "Press any key to continue"
echo
}

check_command() {
if ! command -v "${1}" >/dev/null 2>&1; then
step "Install ${2}. Follow the instructions at ${3}"
fi
}

check_command "kubectl" "kubectl" "https://kubernetes.io/docs/tasks/tools/#kubectl"
check_command "kubectl-oidc_login" "kubectl oidc-login plugin" "https://github.com/int128/kubelogin#setup"

echo
read -r -p "Provide the URL of the Keycloak to connect the local environment to (default=${keycloak_url}): " user_url
if [ x"${user_url}" != x"" ]; then
keycloak_url="${user_url}"
fi

echo
identifier=
while [ x"$identifier" == x"" ]; do
read -r -p "Provide an identifier for your local-dev Keycloak realm: " identifier
done

realm_name="local-dev-${identifier}"
sed -e "s/REPLACEME/${realm_name}/g" "${script_dir}/templates/realm.json.tpl" > "${script_dir}/realm.json"

step "Navigate to ${keycloak_url} and create a new realm by importing the '$(realpath "${script_dir}/realm.json")' file."

step "Create a user in the new realm, grant it realm role 'admin'.\nMake sure the user has an email configured and 'Email Verified' is set to 'On'."

echo ""
echo -e "\033[1m================================================================================"
echo "Note: After the cluster is created, a browser window will open where you have to sign in to Keycloak with the user you've created in the previous step."
echo -e "================================================================================\033[0m"
echo ""

sed -e "s#ISSUER_KEYCLOAK#${keycloak_url}#; s/REALM/${realm_name}/g" "${script_dir}/templates/kind-oidc.yaml.tpl" > "${script_dir}/.kind-oidc.yaml"
${kind_cmd} create cluster \
--name "${kind_cluster}" \
--image "kindest/node:${kind_node_version}" \
--config="${script_dir}/.kind-oidc.yaml"
rm "${script_dir}/.kind-oidc.yaml"
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-cluster-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: Group
name: admin
EOF
kubectl oidc-login setup \
--oidc-issuer-url="${keycloak_url}/auth/realms/${realm_name}" \
--oidc-client-id=local-dev >/dev/null 2>&1
kubectl config set-credentials oidc-user \
--exec-api-version=client.authentication.k8s.io/v1beta1 \
--exec-command=kubectl \
--exec-arg=oidc-login \
--exec-arg=get-token \
--exec-arg=--oidc-issuer-url="${keycloak_url}/auth/realms/${realm_name}" \
--exec-arg=--oidc-client-id=local-dev \
--exec-arg=--oidc-extra-scope="email offline_access profile openid"
kubectl config set-context --current --user=oidc-user
kubectl apply -k "${script_dir}/../config/crd/apiextensions.k8s.io/v1"

echo =======
echo "Setup finished. To interact with the local dev cluster, set the KUBECONFIG environment variable as follows:"
echo "\"export \$KUBECONFIG=$(realpath "${kind_kubeconfig}")\""
echo =======
3 changes: 3 additions & 0 deletions local-env/templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Templates for setting up local environment

This directory contains templates that are used by the `setup-kind.sh` script.
13 changes: 13 additions & 0 deletions local-env/templates/kind-oidc.yaml.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
oidc-issuer-url: ISSUER_KEYCLOAK/auth/realms/REALM
oidc-client-id: local-dev
oidc-username-claim: email
oidc-groups-claim: groups
Loading

0 comments on commit 735c69b

Please sign in to comment.