diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..cb80fb4 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,70 @@ +# OpenCenter Examples + +This directory provides a reference flow to stand up: + +1. Cluster infrastructure (`examples/iac/dev-cluster`) +2. Core service content using Flux (`examples/applications/overlays/dev-cluster`) + +It is intended as a **starter template**. Copy and adapt for your own environment. + +## Directory Layout + +- `iac/dev-cluster/` + - Terraform configuration for OpenStack infrastructure and cluster bootstrap modules. +- `applications/overlays/dev-cluster/` + - Example Flux/Kustomize service deployment content (cert-manager, gateway-api, headlamp, metallb, etc.). + +## Prerequisites + +- OpenStack credentials and project access +- Terraform +- `kubectl` +- Flux CLI (for bootstrap/ops) +- Access to a Git repo for your cluster config + +Note: `examples/iac/dev-cluster/Makefile` contains helper targets to install common tooling versions. + +## 1) Deploy Infrastructure + +Use the Terraform example in `examples/iac/dev-cluster` as a reference template for cluster infrastructure creation. + +Typical flow: + +```bash +terraform init +terraform plan +terraform apply +``` + +Before applying, replace placeholders in `provider.tf` and `main.tf` for your environment. + +## 2) Deploy Service Content (Flux/Kustomize) + +Use `examples/applications/overlays/dev-cluster` as a template for your cluster repo. + +Recommended flow: + +1. Copy `examples/applications/overlays/dev-cluster` into your cluster Git repo. +2. Bootstrap Flux in your cluster (if not already bootstrapped). +3. Point Flux `Kustomization` to your copied overlay path. + +## Important Before Applying + +Validate and adjust these in the example manifests: + +1. Source paths currently use `./applications/overlays/dev/...` in some Flux files; if you keep `dev-cluster`, update paths accordingly. +2. GitRepository URLs/branches under `services/sources/` should match your repo strategy. +3. Domain names, hostnames, and IP ranges in service overrides must be environment-specific. +4. Encrypted secrets (if added) require your SOPS/Age setup. + +## Suggested Validation + +Run local checks before committing: + +```bash +kustomize build examples/applications/overlays/dev-cluster/services/sources +kustomize build examples/applications/overlays/dev-cluster/services/fluxcd +kustomize build examples/applications/overlays/dev-cluster +``` + +Then let Flux reconcile from your cluster repo path. diff --git a/examples/applications/overlays/dev-cluster/kustomization.yaml b/examples/applications/overlays/dev-cluster/kustomization.yaml new file mode 100644 index 0000000..4090610 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/kustomization.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ./flux-system + - ./services/fluxcd diff --git a/examples/applications/overlays/dev-cluster/services/calico/helm-values/override_values.yaml b/examples/applications/overlays/dev-cluster/services/calico/helm-values/override_values.yaml new file mode 100644 index 0000000..faa559f --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/calico/helm-values/override_values.yaml @@ -0,0 +1,19 @@ +--- +installation: + enabled: true + kubernetesProvider: "" + calicoNetwork: + windowsDataplane: "Disabled" + nodeAddressAutodetectionV4: + interface: "enp3s0" + ipPools: + - cidr: "10.42.0.0/16" + encapsulation: "VXLAN" + natOutgoing: Enabled + serviceCIDRs: + - "10.43.0.0/16" + +# Optionally configure the host and port used to access the Kubernetes API server. +kubernetesServiceEndpoint: + host: "10.2.188.10" + port: "443" diff --git a/examples/applications/overlays/dev-cluster/services/cert-manager/kustomization.yaml b/examples/applications/overlays/dev-cluster/services/cert-manager/kustomization.yaml new file mode 100644 index 0000000..1cf5cdf --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/cert-manager/kustomization.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: cert-manager +resources: + - "./rackspace-selfsigned-issuer.yaml" + - "./rackspace-selfsigned-ca.yaml" + - "./rackspace-ca-issuer.yaml" diff --git a/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-ca-issuer.yaml b/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-ca-issuer.yaml new file mode 100644 index 0000000..99f2d82 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-ca-issuer.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: rackspace-ca +spec: + ca: + secretName: rackspace-root-secret diff --git a/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-selfsigned-ca.yaml b/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-selfsigned-ca.yaml new file mode 100644 index 0000000..12baee0 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-selfsigned-ca.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: rackspace-selfsigned-ca +spec: + isCA: true + commonName: rmpk.dev + secretName: rackspace-root-secret + duration: 87600h0m0s + renewBefore: 360h0m0s + privateKey: + algorithm: ECDSA + size: 256 + issuerRef: + name: rackspace-selfsigned-issuer + kind: Issuer + group: cert-manager.io diff --git a/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-selfsigned-issuer.yaml b/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-selfsigned-issuer.yaml new file mode 100644 index 0000000..c159c3b --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/cert-manager/rackspace-selfsigned-issuer.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: rackspace-selfsigned-issuer +spec: + selfSigned: {} diff --git a/examples/applications/overlays/dev-cluster/services/fluxcd/cert-manager.yaml b/examples/applications/overlays/dev-cluster/services/fluxcd/cert-manager.yaml new file mode 100644 index 0000000..6cdeab1 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/fluxcd/cert-manager.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: cert-manager-base + namespace: flux-system +spec: + dependsOn: + - name: sources + namespace: flux-system + interval: 15m + retryInterval: 1m + timeout: 10m + sourceRef: + kind: GitRepository + name: opencenter-cert-manager + namespace: flux-system + path: applications/base/services/cert-manager + targetNamespace: cert-manager + prune: true + wait: true + healthChecks: + - apiVersion: helm.toolkit.fluxcd.io/v2 + kind: HelmRelease + name: cert-manager + namespace: cert-manager + commonMetadata: + labels: + app.kubernetes.io/part-of: cert-manager + app.kubernetes.io/managed-by: flux + opencenter/managed-by: opencenter diff --git a/examples/applications/overlays/dev-cluster/services/fluxcd/gateway-api.yaml b/examples/applications/overlays/dev-cluster/services/fluxcd/gateway-api.yaml new file mode 100644 index 0000000..b1a0484 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/fluxcd/gateway-api.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: envoy-gateway-api-base + namespace: flux-system +spec: + dependsOn: + - name: sources + namespace: flux-system + interval: 15m + retryInterval: 1m + timeout: 10m + sourceRef: + kind: GitRepository + name: opencenter-gateway-api + namespace: flux-system + path: applications/base/services/gateway-api + targetNamespace: envoy-gateway-system + prune: true + wait: true + healthChecks: + - apiVersion: helm.toolkit.fluxcd.io/v2 + kind: HelmRelease + name: envoy-gateway-api + namespace: envoy-gateway-system + commonMetadata: + labels: + app.kubernetes.io/part-of: envoy-gateway + app.kubernetes.io/managed-by: flux + opencenter/managed-by: opencenter + patches: + - target: + kind: Namespace + name: envoy-gateway-system + patch: | + apiVersion: v1 + kind: Namespace + metadata: + name: envoy-gateway-system + labels: + istio-injection: enabled + pod-security.kubernetes.io/enforce: privileged + pod-security.kubernetes.io/enforce-version: latest + pod-security.kubernetes.io/warn: baseline + pod-security.kubernetes.io/warn-version: latest + pod-security.kubernetes.io/audit: baseline + pod-security.kubernetes.io/audit-version: latest diff --git a/examples/applications/overlays/dev-cluster/services/fluxcd/headlamp.yaml b/examples/applications/overlays/dev-cluster/services/fluxcd/headlamp.yaml new file mode 100644 index 0000000..0a34550 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/fluxcd/headlamp.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: headlamp-base + namespace: flux-system +spec: + dependsOn: + - name: sources + namespace: flux-system + interval: 15m + retryInterval: 1m + timeout: 5m + sourceRef: + kind: GitRepository + name: opencenter-headlamp + namespace: flux-system + path: applications/base/services/headlamp + targetNamespace: headlamp + prune: true + healthChecks: + - apiVersion: helm.toolkit.fluxcd.io/v2 + kind: HelmRelease + name: headlamp + namespace: headlamp + commonMetadata: + labels: + app.kubernetes.io/part-of: headlamp + app.kubernetes.io/managed-by: flux + opencenter/managed-by: opencenter diff --git a/examples/applications/overlays/dev-cluster/services/fluxcd/kustomization.yaml b/examples/applications/overlays/dev-cluster/services/fluxcd/kustomization.yaml new file mode 100644 index 0000000..80e4153 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/fluxcd/kustomization.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ./sources.yaml + - ./gateway-api.yaml + - ./headlamp.yaml + - ./cert-manager.yaml + - ./metallb.yaml diff --git a/examples/applications/overlays/dev-cluster/services/fluxcd/metallb.yaml b/examples/applications/overlays/dev-cluster/services/fluxcd/metallb.yaml new file mode 100644 index 0000000..4ade706 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/fluxcd/metallb.yaml @@ -0,0 +1,57 @@ +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: metallb-base + namespace: flux-system +spec: + dependsOn: + - name: sources + namespace: flux-system + interval: 15m + retryInterval: 1m + timeout: 10m + sourceRef: + kind: GitRepository + name: opencenter-metallb + namespace: flux-system + path: applications/base/services/metallb + targetNamespace: metallb-system + prune: true + wait: true + healthChecks: + - apiVersion: helm.toolkit.fluxcd.io/v2 + kind: HelmRelease + name: metallb + namespace: metallb-system + commonMetadata: + labels: + app.kubernetes.io/part-of: metallb + app.kubernetes.io/managed-by: flux + opencenter/managed-by: opencenter +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: metallb-override + namespace: flux-system +spec: + dependsOn: + - name: sources + namespace: flux-system + interval: 15m + retryInterval: 1m + timeout: 10m + sourceRef: + kind: GitRepository + name: flux-system + namespace: flux-system + path: ./applications/overlays/dev/services/metallb + targetNamespace: metallb-system + prune: true + wait: true + commonMetadata: + labels: + app.kubernetes.io/part-of: metallb + app.kubernetes.io/managed-by: flux + opencenter/managed-by: opencenter diff --git a/examples/applications/overlays/dev-cluster/services/fluxcd/sources.yaml b/examples/applications/overlays/dev-cluster/services/fluxcd/sources.yaml new file mode 100644 index 0000000..1002bf3 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/fluxcd/sources.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: sources + namespace: flux-system +spec: + interval: 15m + path: ./applications/overlays/dev/services/sources + prune: true + sourceRef: + kind: GitRepository + name: flux-system + namespace: flux-system + wait: true diff --git a/examples/applications/overlays/dev-cluster/services/metallb/helm-values/override-values.yaml b/examples/applications/overlays/dev-cluster/services/metallb/helm-values/override-values.yaml new file mode 100644 index 0000000..91da2a7 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/metallb/helm-values/override-values.yaml @@ -0,0 +1,2 @@ +--- +... diff --git a/examples/applications/overlays/dev-cluster/services/metallb/ipaddresspool.yaml b/examples/applications/overlays/dev-cluster/services/metallb/ipaddresspool.yaml new file mode 100644 index 0000000..4ed6b6c --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/metallb/ipaddresspool.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: default-pool + namespace: metallb-system +spec: + addresses: + - diff --git a/examples/applications/overlays/dev-cluster/services/metallb/kustomization.yaml b/examples/applications/overlays/dev-cluster/services/metallb/kustomization.yaml new file mode 100644 index 0000000..edafcd9 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/metallb/kustomization.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: metallb-system +resources: + - "./ipaddresspool.yaml" + - "./l2advertisement.yaml" +secretGenerator: + - name: metallb-values-override + type: Opaque + files: [override.yaml=helm-values/override-values.yaml] + options: + disableNameSuffixHash: true diff --git a/examples/applications/overlays/dev-cluster/services/metallb/l2advertisement.yaml b/examples/applications/overlays/dev-cluster/services/metallb/l2advertisement.yaml new file mode 100644 index 0000000..0466840 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/metallb/l2advertisement.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: default + namespace: metallb-system +spec: + ipAddressPools: + - default-pool diff --git a/examples/applications/overlays/dev-cluster/services/sources/kustomization.yaml b/examples/applications/overlays/dev-cluster/services/sources/kustomization.yaml new file mode 100644 index 0000000..fdbb0c2 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/sources/kustomization.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: flux-system +resources: + - ./opencenter-gateway-api.yaml + - ./opencenter-cert-manager.yaml + - ./opencenter-headlamp.yaml + - ./opencenter-metallb.yaml diff --git a/examples/applications/overlays/dev-cluster/services/sources/opencenter-cert-manager.yaml b/examples/applications/overlays/dev-cluster/services/sources/opencenter-cert-manager.yaml new file mode 100644 index 0000000..c661260 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/sources/opencenter-cert-manager.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: GitRepository +metadata: + name: opencenter-cert-manager + namespace: flux-system +spec: + interval: 10m + url: https://github.com/rackerlabs/openCenter-gitops-base.git + ref: + branch: main diff --git a/examples/applications/overlays/dev-cluster/services/sources/opencenter-gateway-api.yaml b/examples/applications/overlays/dev-cluster/services/sources/opencenter-gateway-api.yaml new file mode 100644 index 0000000..3718e44 --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/sources/opencenter-gateway-api.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: GitRepository +metadata: + name: opencenter-gateway-api + namespace: flux-system +spec: + interval: 10m + url: https://github.com/rackerlabs/openCenter-gitops-base.git + ref: + branch: main diff --git a/examples/applications/overlays/dev-cluster/services/sources/opencenter-headlamp.yaml b/examples/applications/overlays/dev-cluster/services/sources/opencenter-headlamp.yaml new file mode 100644 index 0000000..25d61be --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/sources/opencenter-headlamp.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: GitRepository +metadata: + name: opencenter-headlamp + namespace: flux-system +spec: + interval: 10m + url: https://github.com/rackerlabs/openCenter-gitops-base.git + ref: + branch: main diff --git a/examples/applications/overlays/dev-cluster/services/sources/opencenter-metallb.yaml b/examples/applications/overlays/dev-cluster/services/sources/opencenter-metallb.yaml new file mode 100644 index 0000000..d30acce --- /dev/null +++ b/examples/applications/overlays/dev-cluster/services/sources/opencenter-metallb.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: GitRepository +metadata: + name: opencenter-metallb + namespace: flux-system +spec: + interval: 10m + url: https://github.com/rackerlabs/openCenter-gitops-base.git + ref: + branch: main diff --git a/examples/iac/dev-cluster/Makefile b/examples/iac/dev-cluster/Makefile new file mode 100644 index 0000000..10a1649 --- /dev/null +++ b/examples/iac/dev-cluster/Makefile @@ -0,0 +1,286 @@ +.PHONY: clean lint rke terraform kubectl helm velero sops age kustomize flux gitops kubelogin egctl +.PHONY: secrets-encrypt secrets-decrypt secrets-list secrets-check secrets-status + +BIN := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))/.bin +TERRAFORM_VERSION := 1.12.2 +KUBECTL_VERSION := 1.28.0 +HELM_VERSION := 3.13.0 +VELERO_VERSION := 1.12.1 +SOPS_VERSION := 3.8.1 +KUSTOMIZE_VERSION := 5.2.1 +FLUX_VERSION := 2.2.2 +GITOPS_VERSION := 0.38.0 +EGCTL_VERSION := 1.5.4 +HELM_VERSION := 3.13.0 + +export PATH := $(BIN):$(PATH) +export TF_CLI_CONFIG_FILE=config.tfrc + +export ANSIBLE_INVENTORY = /Users/migu4903/Documents/RPC/MPK/tf-gene/etc/genestack/sjc-lab/inventory/inventory.yaml + +# SOPS Configuration +SOPS_SCRIPT := ./sops_manager.sh +SOPS_CONFIG := .sops.yaml +SOPS_AGE_KEY_FILE ?= $(HOME)/.age/key.txt +export SOPS_AGE_KEY_FILE + +UNAME_S := $(shell uname -s) +UNAME_M := $(shell uname -m) +ifeq ($(UNAME_S),Linux) + OS = linux + GITOPS_OS = Linux + ifeq ($(UNAME_M),x86_64) + ARCH = amd64 + endif + ifeq ($(UNAME_M),aarch64) + ARCH = arm64 + endif +endif +ifeq ($(UNAME_S),Darwin) + OS = darwin + GITOPS_OS = Darwin + ifeq ($(UNAME_M),x86_64) + ARCH = amd64 + endif + ifeq ($(UNAME_M),arm64) + ARCH = arm64 + endif +endif + +clean: + rm cluster.rkestate kube_config_cluster.yml terraform.tfstate* + +rke: + +terraform: + @if ! terraform --version | head -n 1 | grep $(TERRAFORM_VERSION); then \ + mkdir -p $(BIN); \ + curl -L https://releases.hashicorp.com/terraform/$(TERRAFORM_VERSION)/terraform_$(TERRAFORM_VERSION)_$(OS)_$(ARCH).zip > $(BIN)/terraform.zip; \ + unzip $(BIN)/terraform.zip -d $(BIN); \ + rm $(BIN)/terraform.zip; \ + fi; + +kubectl: + @if ! kubectl version --client --output=yaml 2>/dev/null | grep -q "gitVersion: v$(KUBECTL_VERSION)"; then \ + mkdir -p $(BIN); \ + curl -L "https://dl.k8s.io/release/v$(KUBECTL_VERSION)/bin/$(OS)/$(ARCH)/kubectl" -o $(BIN)/kubectl; \ + chmod +x $(BIN)/kubectl; \ + fi; + +helm: + @if ! helm version --template="{{.Version}}" 2>/dev/null | grep -q "v$(HELM_VERSION)"; then \ + mkdir -p $(BIN); \ + curl -L "https://get.helm.sh/helm-v$(HELM_VERSION)-$(OS)-$(ARCH).tar.gz" | tar xz -C $(BIN) --strip-components=1 $(OS)-$(ARCH)/helm; \ + fi; + +velero: + @if ! velero version --client-only 2>/dev/null | grep -q "v$(VELERO_VERSION)"; then \ + mkdir -p $(BIN); \ + curl -L "https://github.com/vmware-tanzu/velero/releases/download/v$(VELERO_VERSION)/velero-v$(VELERO_VERSION)-$(OS)-$(ARCH).tar.gz" | tar xz -C $(BIN) --strip-components=1 velero-v$(VELERO_VERSION)-$(OS)-$(ARCH)/velero; \ + fi; + +sops: + @if ! sops --version 2>/dev/null | grep -q "$(SOPS_VERSION)"; then \ + mkdir -p $(BIN); \ + curl -L "https://github.com/getsops/sops/releases/download/v$(SOPS_VERSION)/sops-v$(SOPS_VERSION).$(OS).$(ARCH)" -o $(BIN)/sops; \ + chmod +x $(BIN)/sops; \ + fi; + +age: + @if ! age --version 2>/dev/null | grep -q "v"; then \ + mkdir -p $(BIN); \ + curl -L "https://dl.filippo.io/age/latest?for=$(OS)/$(ARCH)" | tar xz -C $(BIN) --strip-components=1 age/age age/age-keygen; \ + fi; + +kustomize: + @if ! kustomize version 2>/dev/null | grep -q "v$(KUSTOMIZE_VERSION)"; then \ + mkdir -p $(BIN); \ + curl -L "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv$(KUSTOMIZE_VERSION)/kustomize_v$(KUSTOMIZE_VERSION)_$(OS)_$(ARCH).tar.gz" | tar xz -C $(BIN) kustomize; \ + fi; + +flux: + @if ! flux --version 2>/dev/null | grep -q "$(FLUX_VERSION)"; then \ + mkdir -p $(BIN); \ + curl -L "https://github.com/fluxcd/flux2/releases/download/v$(FLUX_VERSION)/flux_$(FLUX_VERSION)_$(OS)_$(ARCH).tar.gz" | tar xz -C $(BIN) flux; \ + fi; + +gitops: + @if ! gitops version 2>/dev/null | grep -q "$(GITOPS_VERSION)"; then \ + mkdir -p $(BIN); \ + curl -L "https://github.com/weaveworks/weave-gitops/releases/download/v$(GITOPS_VERSION)/gitops-$(GITOPS_OS)-$(UNAME_M).tar.gz" | tar xz -C $(BIN) gitops; \ + fi; + +kubelogin: + @if ! $(BIN)/kubectl-oidc_login --version 2>/dev/null; then \ + mkdir -p $(BIN); \ + curl -L "https://github.com/int128/kubelogin/releases/latest/download/kubelogin_$(OS)_$(ARCH).zip" -o $(BIN)/kubelogin.zip; \ + unzip -o $(BIN)/kubelogin.zip -d $(BIN); \ + mv $(BIN)/kubelogin $(BIN)/kubectl-oidc_login; \ + chmod +x $(BIN)/kubectl-oidc_login; \ + rm $(BIN)/kubelogin.zip; \ + fi; + +egctl: + @if ! egctl version 2>/dev/null | grep -q "v$(EGCTL_VERSION)"; then \ + mkdir -p $(BIN); \ + curl -L "https://github.com/envoyproxy/gateway/releases/download/v$(EGCTL_VERSION)/egctl_v$(EGCTL_VERSION)_$(OS)_$(ARCH).tar.gz" | tar xz -C $(BIN) bin/$(OS)/$(ARCH)/egctl --strip-components=3; \ + fi; + +############################################################################### +# SOPS Secrets Management Targets +############################################################################### + +# Check if SOPS script exists +.check-sops-script: + @if [ ! -f "$(SOPS_SCRIPT)" ]; then \ + echo "Error: $(SOPS_SCRIPT) not found!"; \ + echo "Please ensure sops_manager.sh is in the current directory."; \ + exit 1; \ + fi + @chmod +x $(SOPS_SCRIPT) + +# Check if Age key file exists +.check-age-key: + @if [ -z "$(SOPS_AGE_KEY_FILE)" ]; then \ + echo "Error: SOPS_AGE_KEY_FILE not set!"; \ + echo "Set it with: export SOPS_AGE_KEY_FILE=~/.age/key.txt"; \ + exit 1; \ + fi + @if [ ! -f "$(SOPS_AGE_KEY_FILE)" ]; then \ + echo "Error: Age key file not found at $(SOPS_AGE_KEY_FILE)"; \ + echo "Generate one with: make age-keygen"; \ + exit 1; \ + fi + +# Generate Age key pair +age-keygen: age + @if [ -f "$(SOPS_AGE_KEY_FILE)" ]; then \ + echo "Age key already exists at $(SOPS_AGE_KEY_FILE)"; \ + echo "Backup your existing key before generating a new one!"; \ + exit 1; \ + fi + @mkdir -p $$(dirname $(SOPS_AGE_KEY_FILE)) + @age-keygen -o $(SOPS_AGE_KEY_FILE) + @echo "" + @echo "✓ Age key generated at: $(SOPS_AGE_KEY_FILE)" + @echo "" + @echo "IMPORTANT: Save your public key from above and add it to $(SOPS_CONFIG)" + @echo "Example .sops.yaml entry:" + @echo " creation_rules:" + @echo " - path_regex: '^.*\.ya?ml$$'" + @echo " age: age1..." + +# List secrets that will be processed +secrets-list: .check-sops-script sops + @echo "Listing secrets to be encrypted/decrypted..." + @$(SOPS_SCRIPT) encrypt --list + +# Show status of secrets (encrypted or not) +secrets-status: secrets-list + +# Check secrets configuration +secrets-check: .check-sops-script .check-age-key sops age + @echo "✓ SOPS script found: $(SOPS_SCRIPT)" + @echo "✓ SOPS installed: $$(sops --version)" + @echo "✓ Age installed: $$(age --version)" + @echo "✓ Age key file: $(SOPS_AGE_KEY_FILE)" + @if [ -f "$(SOPS_CONFIG)" ]; then \ + echo "✓ SOPS config found: $(SOPS_CONFIG)"; \ + else \ + echo "⚠ Warning: $(SOPS_CONFIG) not found"; \ + echo " SOPS will use default behavior or environment variables"; \ + fi + @echo "" + @echo "Configuration OK!" + +# Encrypt secrets with backups (safe) +secrets-encrypt: .check-sops-script .check-age-key sops + @if [ ! -f "$(SOPS_CONFIG)" ]; then \ + echo "Warning: $(SOPS_CONFIG) not found!"; \ + echo "SOPS will use default configuration."; \ + echo ""; \ + fi + @echo "Encrypting secrets..." + @$(SOPS_SCRIPT) encrypt -v + +# Encrypt secrets without backups (fast) +secrets-encrypt-fast: .check-sops-script .check-age-key sops + @if [ ! -f "$(SOPS_CONFIG)" ]; then \ + echo "Warning: $(SOPS_CONFIG) not found!"; \ + echo "SOPS will use default configuration."; \ + echo ""; \ + fi + @echo "Encrypting secrets (no backups)..." + @$(SOPS_SCRIPT) encrypt -v --no-backup + +# Decrypt secrets with backups (safe) +secrets-decrypt: .check-sops-script .check-age-key sops + @echo "Decrypting secrets..." + @$(SOPS_SCRIPT) decrypt -v + +# Decrypt secrets without backups (fast) +secrets-decrypt-fast: .check-sops-script .check-age-key sops + @echo "Decrypting secrets (no backups)..." + @$(SOPS_SCRIPT) decrypt -v --no-backup + +# Dry run - show what would be encrypted +secrets-encrypt-dry: .check-sops-script sops + @echo "Dry run - showing what would be encrypted..." + @$(SOPS_SCRIPT) encrypt --dry-run + +# Dry run - show what would be decrypted +secrets-decrypt-dry: .check-sops-script sops + @echo "Dry run - showing what would be decrypted..." + @$(SOPS_SCRIPT) decrypt --dry-run + +# Clean backup files created by SOPS operations +secrets-clean-backups: + @echo "Cleaning SOPS backup files..." + @find . -type f \( -name "*.backup.*" -o -name "*.encrypted.*" \) -print + @echo "" + @read -p "Delete these files? [y/N] " confirm; \ + if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \ + find . -type f \( -name "*.backup.*" -o -name "*.encrypted.*" \) -delete; \ + echo "✓ Backup files cleaned"; \ + else \ + echo "Cancelled"; \ + fi + +# Help target for secrets management +secrets-help: + @echo "SOPS Secrets Management Commands:" + @echo "" + @echo "Setup:" + @echo " make age-keygen - Generate Age encryption key pair" + @echo " make secrets-check - Verify SOPS configuration" + @echo "" + @echo "Information:" + @echo " make secrets-list - List files that will be processed" + @echo " make secrets-status - Show status of secrets (alias for secrets-list)" + @echo "" + @echo "Encryption:" + @echo " make secrets-encrypt - Encrypt secrets (with backups)" + @echo " make secrets-encrypt-fast - Encrypt secrets (no backups, faster)" + @echo " make secrets-encrypt-dry - Dry run encryption" + @echo "" + @echo "Decryption:" + @echo " make secrets-decrypt - Decrypt secrets (with backups)" + @echo " make secrets-decrypt-fast - Decrypt secrets (no backups, faster)" + @echo " make secrets-decrypt-dry - Dry run decryption" + @echo "" + @echo "Maintenance:" + @echo " make secrets-clean-backups - Remove backup files" + @echo "" + @echo "Configuration:" + @echo " SOPS_AGE_KEY_FILE - Path to Age private key (default: ~/.age/key.txt)" + @echo " $(SOPS_CONFIG) - SOPS configuration file (defines encryption rules)" + @echo "" + @echo "Examples:" + @echo " # Setup" + @echo " make age-keygen" + @echo " export SOPS_AGE_KEY_FILE=~/.age/key.txt" + @echo " # Add public key to $(SOPS_CONFIG)" + @echo "" + @echo " # Usage" + @echo " make secrets-encrypt" + @echo " make secrets-decrypt" \ No newline at end of file diff --git a/examples/iac/dev-cluster/main.tf b/examples/iac/dev-cluster/main.tf new file mode 100644 index 0000000..4819b37 --- /dev/null +++ b/examples/iac/dev-cluster/main.tf @@ -0,0 +1,235 @@ +locals { + # this will be the user's name and the DNS zone prefix + cluster_name = "" + # Prefix to add to Openstack resource names + naming_prefix = "${local.cluster_name}-" + openstack_auth_url = "https://keystone.api.sjc3.rackspacecloud.com/v3/" + openstack_insecure = false + openstack_region = "SJC3" + availability_zone = "az1" + openstack_user_name = var.openstack_user_name == "" ? local.cluster_name : var.openstack_user_name + openstack_user_password = var.openstack_user_password + openstack_admin_password = var.openstack_admin_password + openstack_project_domain_name = "rackspace_cloud_domain" + openstack_user_domain_name = "rackspace_cloud_domain" + openstack_tenant_name = "" + floatingip_pool = "PUBLICNET" + router_external_network_id = "723f8fa2-dbf7-4cec-8d5f-017e62c12f79" + # VLAN settings + vlan_id = "" + mtu = "" + network_provider = "physnet1" + #CIDR that the openstack VMs will use for K8s nodes + subnet_nodes = "10.2.188.0/22" + subnet_nodes_oct = join(".", slice(split(".", split("/", local.subnet_nodes)[0]), 0, 3)) + #Leave some IPs free for the VRRP IP and the MetalLB Range + allocation_pool_start = "${local.subnet_nodes_oct}.50" + allocation_pool_end = "${local.subnet_nodes_oct}.254" + # vrrp_ip Must be an IP from subnet_nodes and will be used as the internal Kubernetes API VIP. + vrrp_ip = "${local.subnet_nodes_oct}.10" + #CIDR that will be used by kubernetes pods. Not an openstack network. + subnet_pods = "10.42.0.0/16" + #CIDR that will be used for kubernetes services. Not an openstack network. + subnet_services = "10.43.0.0/16" + # use_octavia set to false to create a floating IP associated with the vrrp_ip port. true will create an octavia LB with a floating IP + use_octavia = false + loadbalancer_provider = "amphora" + # vrrp_enabled cannot be set to true if use_octavia is true + vrrp_enabled = true + # Creates a DNS record using the LB floating IP and dns_zone_name + use_designate = false + # dns_zone_name is the dns zone to create if use_designate is true + dns_zone_name = "${local.cluster_name}.demo.mk8s.net" + # DNS servers to configure on the nodes + dns_nameservers = ["8.8.8.8", "8.8.4.4"] + image_id = "56277265-8f0c-40dc-87e2-944b7d320dae" + image_id_windows = "899af84f-d98f-4255-bf98-ceba5e3a8257" + k8s_api_port = 443 + worker_count = 5 + worker_count_windows = 0 + # Enter 1 or 3 masters. + master_count = 3 + ssh_user = "ubuntu" + # these are the ssh public keys that will be able to connect to the cluster's bastion node + ssh_authorized_keys = [""] + node_worker = "wn" + node_master = "cp" + node_worker_windows = "win" + ub_version = "24" + #FLEX Flavor Settings ========================== + flavor_bastion = "gp.0.2.2" + flavor_master = "gp.0.4.8" + flavor_worker = "gp.0.4.8" + # flavor_worker_windows = "gp.0.8.16" + + worker_node_bfv_volume_size = 100 + worker_node_bfv_destination_type = "volume" + worker_node_bfv_source_type = "image" + worker_node_bfv_volume_type = "HA-Standard" + + # ==================================== + #ca_certificates add CA certificates to server's trusts. Good for trusting internal private Certificate Authorities. + ca_certificates = "" + openstack_ca = "" + + # ==================================== + #Kubespray Settings + kubespray_version = "v2.28.1" + kubernetes_version = "1.32.5" + network_plugin = "calico" + deploy_cluster = true + #kub-vip settings + kube_vip_enabled = true + #Hardening + k8s_hardening_enabled = true + kube_pod_security_exemptions_namespaces = ["trivy-temp"] + kubelet_rotate_server_certificates = true + os_hardening_enabled = true + + #OIDC Settings + kube_oidc_auth_enabled = true + kube_oidc_url = "https://auth.dev.sjc3.rmpk.dev/realms/opencenter" + kube_oidc_client_id = "opencenter" + # Optional settings fo OIDC + kube_oidc_ca_file = "/etc/ssl/certs/ca-certificates.crt" + kube_oidc_username_claim = "preferred_username" + kube_oidc_username_prefix = "oidc:" + kube_oidc_groups_claim = "groups" + kube_oidc_groups_prefix = "oidc:" + + #Calico Settings + cni_iface = "enp3s0" + #Interface detection method for Calico nodeAddressAutodetectionV4. Can be "first-found", "interface", "cidr" + #https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io%2fv1.NodeAddressAutodetection + calico_interface_autodetect = "interface" + calico_interface_autodetect_cidr = "" + calico_encapsulation_type = "VXLAN" + calico_nat_outgoing = true + + # ## Windows settings + # windows_user = "Administrator" + # windows_admin_password = "" + # worker_node_bfv_size_windows = 0 + # worker_node_bfv_type_windows = "local" +} + +module "openstack-nova" { + # source = "../../../install/iac/infra/openstack-nova" + source = "github.com/rackerlabs/openCenter-gitops-base.git//iac/cloud/openstack/openstack-nova?ref=main" + availability_zone = local.availability_zone + ca_certificates = local.ca_certificates + use_octavia = local.use_octavia + use_designate = local.use_designate + dns_nameservers = local.dns_nameservers + dns_zone_name = local.dns_zone_name + flavor_bastion = local.flavor_bastion + openstack_auth_url = local.openstack_auth_url + openstack_ca = local.openstack_ca + openstack_insecure = local.openstack_insecure + openstack_region = local.openstack_region + openstack_tenant_name = local.openstack_tenant_name + openstack_user_name = local.openstack_user_name + openstack_password = local.openstack_user_password + openstack_project_domain_name = local.openstack_project_domain_name + openstack_user_domain_name = local.openstack_user_domain_name + naming_prefix = local.naming_prefix + ssh_user = local.ssh_user + floatingip_pool = local.floatingip_pool + image_id = local.image_id + image_id_windows = local.image_id_windows + router_external_network_id = local.router_external_network_id + network_id = "" + vlan_id = local.vlan_id + vrrp_enabled = local.vrrp_enabled + vrrp_ip = local.vrrp_ip + ssh_authorized_keys = local.ssh_authorized_keys + subnet_nodes = local.subnet_nodes + subnet_services = local.subnet_services + subnet_pods = local.subnet_pods + allocation_pool_start = local.allocation_pool_start + allocation_pool_end = local.allocation_pool_end + k8s_api_port = local.k8s_api_port + size_master = { + count = local.master_count + flavor = local.flavor_master + } + size_worker = { + count = local.worker_count + flavor = local.flavor_worker + } + # size_worker_windows = { + # count = local.worker_count_windows + # flavor = local.flavor_worker_windows + # } + node_master = local.node_master + node_worker = local.node_worker + node_worker_windows = local.node_worker_windows + ub_version = local.ub_version + # windows_admin_password = local.windows_admin_password + # worker_node_bfv_size_windows = local.worker_node_bfv_size_windows + # worker_node_bfv_type_windows = local.worker_node_bfv_type_windows + # master_node_bfv_volume_size = 39 + # master_node_bfv_destination_type = "local" + + worker_node_bfv_volume_size = local.worker_node_bfv_volume_size + worker_node_bfv_destination_type = local.worker_node_bfv_destination_type + worker_node_bfv_source_type = local.worker_node_bfv_source_type + worker_node_bfv_volume_type = local.worker_node_bfv_volume_type +} + +module "kubespray-cluster" { + source = "github.com/rackerlabs/openCenter-gitops-base.git//iac/provider/kubespray?ref=main" + # source = "../../../install/iac/kubespray" + address_bastion = module.openstack-nova.bastion_floating_ip + cluster_name = local.cluster_name + cni_iface = local.cni_iface + deploy_cluster = local.deploy_cluster + dns_zone_name = local.dns_zone_name + master_nodes = module.openstack-nova.master_nodes + network_plugin = local.network_plugin + k8s_hardening_enabled = local.k8s_hardening_enabled + os_hardening_enabled = local.os_hardening_enabled + ssh_user = local.ssh_user + subnet_nodes = local.subnet_nodes + subnet_pods = local.subnet_pods + subnet_services = local.subnet_services + kubernetes_version = local.kubernetes_version + kubespray_version = local.kubespray_version + kube_vip_enabled = local.kube_vip_enabled + kube_pod_security_exemptions_namespaces = local.kube_pod_security_exemptions_namespaces + kubelet_rotate_server_certificates = local.kubelet_rotate_server_certificates + worker_nodes = module.openstack-nova.worker_nodes + k8s_api_ip = module.openstack-nova.k8s_api_ip + k8s_api_port = local.k8s_api_port + vrrp_ip = local.vrrp_ip + vrrp_enabled = local.vrrp_enabled + windows_nodes = module.openstack-nova.windows_nodes + use_octavia = local.use_octavia + kube_oidc_auth_enabled = local.kube_oidc_auth_enabled + kube_oidc_url = local.kube_oidc_url + kube_oidc_client_id = local.kube_oidc_client_id + kube_oidc_ca_file = local.kube_oidc_ca_file + kube_oidc_username_claim = local.kube_oidc_username_claim + kube_oidc_username_prefix = local.kube_oidc_username_prefix + kube_oidc_groups_claim = local.kube_oidc_groups_claim + kube_oidc_groups_prefix = local.kube_oidc_groups_prefix +} + + +module "calico" { + source = "github.com/rackerlabs/openCenter-gitops-base.git//iac/cni/calico?ref=main" + + calico_interface_autodetect = local.calico_interface_autodetect + calico_encapsulation_type = local.calico_encapsulation_type + calico_nat_outgoing = local.calico_nat_outgoing + calico_interface_autodetect_cidr = local.calico_interface_autodetect_cidr == "" ? local.subnet_nodes : local.calico_interface_autodetect_cidr + cni_iface = local.cni_iface + cluster_name = local.cluster_name + deploy_cluster = local.deploy_cluster + k8s_internal_ip = module.openstack-nova.k8s_internal_ip + k8s_api_port = local.k8s_api_port + subnet_nodes = local.subnet_nodes + subnet_pods = local.subnet_pods + subnet_services = local.subnet_services + windows_dataplane = length(module.openstack-nova.windows_nodes) > 0 ? "HSN" : "Disabled" +} diff --git a/examples/iac/dev-cluster/provider.tf b/examples/iac/dev-cluster/provider.tf new file mode 100644 index 0000000..bfc919b --- /dev/null +++ b/examples/iac/dev-cluster/provider.tf @@ -0,0 +1,18 @@ + + +## Local backend +terraform { + backend "local" { + path = "./.tfstate" + } +} +## S3 Backend +# terraform { +# backend "s3" { +# bucket = "" +# key = "/tfstate/terraform.tfstate" +# region = "" +# use_lockfile = true +# encrypt = true +# } +# } \ No newline at end of file