From e9948f1c2030749b506eff02f4c0f14d6027506f Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Thu, 15 May 2025 08:30:23 +0200 Subject: [PATCH 01/11] Fixed issues --- .gitignore | 1 + 00-bootstrap-vm-cs.sh | 89 ++++++++++++++--- 01-create-kind-cluster.sh | 12 +++ 01-deploy-capi.sh => 02-deploy-capi.sh | 0 02-deploy-cso.sh => 03-deploy-cso.sh | 2 +- 03-cloud-secret.sh => 04-cloud-secret.sh | 2 +- 04-deploy-cstack.sh => 05-deploy-cstack.sh | 0 ...clusterclass.sh => 06-wait-clusterclass.sh | 0 06-create-cluster.sh => 07-create-cluster.sh | 2 +- 07-wait-cluster.sh => 08-wait-cluster.sh | 2 +- 16-delete-cluster.sh | 2 +- CLAUDE.md | 96 +++++++++++++++++++ ciab.cert.sample | 0 clouds.yaml.sample | 14 +++ cluster-settings-template.env | 2 +- cluster-settings.env.sample | 34 +++++++ 16 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 .gitignore create mode 100755 01-create-kind-cluster.sh rename 01-deploy-capi.sh => 02-deploy-capi.sh (100%) rename 02-deploy-cso.sh => 03-deploy-cso.sh (91%) rename 03-cloud-secret.sh => 04-cloud-secret.sh (97%) rename 04-deploy-cstack.sh => 05-deploy-cstack.sh (100%) rename 05-wait-clusterclass.sh => 06-wait-clusterclass.sh (100%) rename 06-create-cluster.sh => 07-create-cluster.sh (96%) rename 07-wait-cluster.sh => 08-wait-cluster.sh (88%) create mode 100644 CLAUDE.md create mode 100644 ciab.cert.sample create mode 100644 clouds.yaml.sample create mode 100644 cluster-settings.env.sample diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66d62f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +**/.claude/settings.local.json diff --git a/00-bootstrap-vm-cs.sh b/00-bootstrap-vm-cs.sh index f0495ee..5df2719 100755 --- a/00-bootstrap-vm-cs.sh +++ b/00-bootstrap-vm-cs.sh @@ -27,29 +27,94 @@ test_sha256() # Usage install_via_download_bin URL sha256 [newname] install_via_download_bin() { - cd ~/Download - curl -LO "$1" || return + cd ~/Download || { echo "ERROR: Failed to cd into ~/Download"; return 1; } + echo "Downloading $1..." + curl -LO "$1" || { echo "ERROR: Failed to download $1"; return 1; } FNM="${1##*/}" - if ! test_sha256 "$FNM" "$2"; then echo "Checksum mismatch for ${FNM}" 1>&2; return 1; fi - chmod +x "$FNM" - sudo mv "$FNM" /usr/local/bin/"$3" + if ! test_sha256 "$FNM" "$2"; then echo "ERROR: Checksum mismatch for ${FNM}" 1>&2; return 1; fi + chmod +x "$FNM" || { echo "ERROR: Failed to set executable permissions on $FNM"; return 1; } + + # Determine target filename + local TARGET="/usr/local/bin/${FNM}" + if [ -n "$3" ] && [ "$3" != "." ]; then + TARGET="/usr/local/bin/$3" + fi + + echo "Moving $FNM to $TARGET" + sudo mv "$FNM" "$TARGET" || { echo "ERROR: Failed to move $FNM to $TARGET"; return 1; } + echo "Successfully installed $TARGET" } # Usage install_via_download_bin URL sha256 extrpath [newname] install_via_download_tgz() { - cd ~/Download - curl -LO "$1" || return + cd ~/Download || { echo "ERROR: Failed to cd into ~/Download"; return 1; } + echo "Downloading $1..." + curl -LO "$1" || { echo "ERROR: Failed to download $1"; return 1; } FNM="${1##*/}" - if ! test_sha256 "$FNM" "$2"; then echo "Checksum mismatch for ${FNM}" 1>&2; return 1; fi - tar xvzf "$FNM" - sudo mv "$3" /usr/local/bin/"$4" + if ! test_sha256 "$FNM" "$2"; then echo "ERROR: Checksum mismatch for ${FNM}" 1>&2; return 1; fi + + echo "Extracting $FNM..." + tar xzf "$FNM" || { echo "ERROR: Failed to extract $FNM"; return 1; } + + # Determine target filename + local TARGET="/usr/local/bin/${3##*/}" + if [ -n "$4" ] && [ "$4" != "." ]; then + TARGET="/usr/local/bin/$4" + fi + + echo "Moving $3 to $TARGET" + sudo mv "$3" "$TARGET" || { + echo "ERROR: Failed to move $3 to $TARGET"; + echo "Looking for file to move..."; + find . -name "${3##*/}" -type f; + return 1; + } + echo "Successfully installed $TARGET" } # Debian 12 (Bookworm) mkdir -p ~/Download +# Ensure Download directory exists and is accessible +if [ ! -d ~/Download ]; then + echo "Failed to create ~/Download directory, creating it again" + mkdir -p ~/Download + if [ ! -d ~/Download ]; then + echo "ERROR: Could not create ~/Download directory" + exit 1 + fi +fi +# Make sure we have write permissions +touch ~/Download/.write_test && rm ~/Download/.write_test INSTCMD="apt-get install -y --no-install-recommends --no-install-suggests" -DEB12_PKGS=(docker.io golang jq yq git gh python3-openstackclient) + +# Detect Ubuntu version +if [ -f /etc/os-release ]; then + . /etc/os-release + OS_NAME=$ID + OS_VERSION=$VERSION_ID +else + OS_NAME="unknown" + OS_VERSION="unknown" +fi + +# Set packages based on OS version +if [ "$OS_NAME" = "ubuntu" ] && [ "$OS_VERSION" = "22.04" ]; then + echo "Detected Ubuntu 22.04" + # For Ubuntu 22.04, use Docker's official repository instead of docker.io + echo "Setting up Docker repository..." + sudo apt-get install -y ca-certificates curl gnupg + sudo install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg + sudo chmod a+r /etc/apt/keyrings/docker.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + sudo apt-get update + DEB12_PKGS=(docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin golang jq git gh python3-openstackclient) +else + echo "Using default package list for Debian 12 or other distributions" + DEB12_PKGS=(docker.io golang jq git gh python3-openstackclient) +fi + DEB12_TGZS=("https://get.helm.sh/helm-v3.17.1-${OS}-${ARCH}.tar.gz") DEB12_TCHK=("3b66f3cd28409f29832b1b35b43d9922959a32d795003149707fea84cbcd4469") DEB12_TOLD=("${OS}-${ARCH}/helm") @@ -67,9 +132,11 @@ DEB12_BNEW=("kind" "." "clusterctl") sudo apt-get update install_via_pkgmgr "${DEB12_PKGS[@]}" || exit 1 for i in $(seq 0 $((${#DEB12_TGZS[*]}-1))); do + echo "Processing tarball ${DEB12_TGZS[$i]}..." install_via_download_tgz "${DEB12_TGZS[$i]}" "${DEB12_TCHK[$i]}" "${DEB12_TOLD[$i]}" "${DEB12_TNEW[$i]}" || exit 2 done for i in $(seq 0 $((${#DEB12_BINS[*]}-1))); do + echo "Processing binary ${DEB12_BINS[$i]}..." install_via_download_bin "${DEB12_BINS[$i]}" "${DEB12_BCHK[$i]}" "${DEB12_BNEW[$i]}" || exit 3 done diff --git a/01-create-kind-cluster.sh b/01-create-kind-cluster.sh new file mode 100755 index 0000000..a24fdff --- /dev/null +++ b/01-create-kind-cluster.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Create a local kind (Kubernetes in Docker) cluster +set -e + +echo "Creating a new local kind cluster..." +kind create cluster + +# Verify the cluster is running +echo "Verifying cluster status..." +kubectl cluster-info + +echo "Kind cluster successfully created and running." \ No newline at end of file diff --git a/01-deploy-capi.sh b/02-deploy-capi.sh similarity index 100% rename from 01-deploy-capi.sh rename to 02-deploy-capi.sh diff --git a/02-deploy-cso.sh b/03-deploy-cso.sh similarity index 91% rename from 02-deploy-cso.sh rename to 03-deploy-cso.sh index eb2dc8d..a0c8695 100755 --- a/02-deploy-cso.sh +++ b/03-deploy-cso.sh @@ -24,5 +24,5 @@ controllerManager: EOF # Install Cluster Stack Operator (CSO) with above values helm upgrade -i cso -n cso-system \ - --create-namespace -- --values ~/tmp/cso-rbac.yaml \ + --create-namespace --values ~/tmp/cso-rbac.yaml \ oci://registry.scs.community/cluster-stacks/cso diff --git a/03-cloud-secret.sh b/04-cloud-secret.sh similarity index 97% rename from 03-cloud-secret.sh rename to 04-cloud-secret.sh index 0044bed..6dbd317 100755 --- a/03-cloud-secret.sh +++ b/04-cloud-secret.sh @@ -21,7 +21,7 @@ kubectl create namespace "$CS_NAMESPACE" # - We will detect a cacert in there and pass it to the helper chart if ! test -r "$CLOUDS_YAML"; then echo "clouds.yaml $CLOUDS_YAML not readable"; exit 2; fi CA=$(grep -A11 "^ $OS_CLOUD:" $CLOUDS_YAML | grep 'cacert:' | sed 's/^ *cacert: //') -OS_CACERT=${OS_CACERT:-CA} +OS_CACERT=${OS_CACERT:-$CA} if test -n "$OS_CACERT"; then SETCACERT="--set cacert=$(cat $OS_CACERT)" else diff --git a/04-deploy-cstack.sh b/05-deploy-cstack.sh similarity index 100% rename from 04-deploy-cstack.sh rename to 05-deploy-cstack.sh diff --git a/05-wait-clusterclass.sh b/06-wait-clusterclass.sh similarity index 100% rename from 05-wait-clusterclass.sh rename to 06-wait-clusterclass.sh diff --git a/06-create-cluster.sh b/07-create-cluster.sh similarity index 96% rename from 06-create-cluster.sh rename to 07-create-cluster.sh index 0c33b1f..e5d8976 100755 --- a/06-create-cluster.sh +++ b/07-create-cluster.sh @@ -15,7 +15,7 @@ if test -z "$CS_MAINVER"; then echo "Configure CS_MAINVER"; exit 2; fi if test -z "$CS_VERSION"; then echo "Configure CS_VERSION"; exit 3; fi if test -z "$CL_PATCHVER"; then echo "Configure CL_PATCHVER"; exit 4; fi if test -z "$CL_NAME"; then echo "Configure CL_NAME"; exit 5; fi -if test -z "$CL_PODDIDR"; then echo "Configure CL_PODCIDR"; exit 6; fi +if test -z "$CL_PODCIDR"; then echo "Configure CL_PODCIDR"; exit 6; fi if test -z "$CL_SVCCIDR"; then echo "Configure CL_SVCCIDR"; exit 7; fi if test -z "$CL_CTRLNODES"; then echo "Configure CL_CTRLNODES"; exit 8; fi if test -z "$CL_WRKRNODES"; then echo "Configure CL_WRKRNODES"; exit 9; fi diff --git a/07-wait-cluster.sh b/08-wait-cluster.sh similarity index 88% rename from 07-wait-cluster.sh rename to 08-wait-cluster.sh index a66e0e5..93763b7 100755 --- a/07-wait-cluster.sh +++ b/08-wait-cluster.sh @@ -16,4 +16,4 @@ echo "The cluster should exist now" echo "WARN: Waiting not yet implemented" set -x clusterctl describe cluster -n "$CS_NAMESPACE" $CL_NAME -echo clusterctl get kubeconfug -n "$CS_NAMESPACE" $CL_NAME > "~/.kube/$CS_NAMESPACE.$CL_NAME.yaml" +echo clusterctl get kubeconfig -n "$CS_NAMESPACE" $CL_NAME > "~/.kube/$CS_NAMESPACE.$CL_NAME.yaml" diff --git a/16-delete-cluster.sh b/16-delete-cluster.sh index ad1e44b..8ea3033 100755 --- a/16-delete-cluster.sh +++ b/16-delete-cluster.sh @@ -16,7 +16,7 @@ if test -z "$CS_MAINVER"; then echo "Configure CS_MAINVER"; exit 2; fi if test -z "$CS_VERSION"; then echo "Configure CS_VERSION"; exit 3; fi if test -z "$CL_PATCHVER"; then echo "Configure CL_PATCHVER"; exit 4; fi if test -z "$CL_NAME"; then echo "Configure CL_NAME"; exit 5; fi -if test -z "$CL_PODDIDR"; then echo "Configure CL_PODCIDR"; exit 6; fi +if test -z "$CL_PODCIDR"; then echo "Configure CL_PODCIDR"; exit 6; fi if test -z "$CL_SVCCIDR"; then echo "Configure CL_SVCCIDR"; exit 7; fi if test -z "$CL_CTRLNODES"; then echo "Configure CL_CTRLNODES"; exit 8; fi if test -z "$CL_WRKRNODES"; then echo "Configure CL_WRKRNODES"; exit 9; fi diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..81da6e1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,96 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository Purpose + +This repository contains scripts to automate the creation of Kubernetes clusters using Cluster API and OpenStack. The scripts are organized into a sequence of numbered steps that correspond to different phases of the cluster lifecycle management process. + +## Environment Setup + +Before running the scripts: + +1. Create a copy of `cluster-settings-template.env` and fill in the required parameters +2. Source this file: `source your-settings.env` or pass it as a parameter to the individual scripts + +## Script Execution Flow + +The scripts are designed to be run in numerical order, based on the frequency and context of operations: + +### Management Host Setup (Run once per host) + +```bash +# Install required tools (Debian-based systems) +./00-bootstrap-vm-cs.sh + +# Create a local kind cluster +./01-create-kind-cluster.sh + +# Install Cluster API components +./02-deploy-capi.sh + +# Install Cluster Stack Operator +./03-deploy-cso.sh +``` + +### Cloud Access Configuration (Run once per OpenStack project) + +```bash +# Create namespace and cloud secrets +./04-cloud-secret.sh your-settings.env +``` + +### Kubernetes Version Setup (Run once per Kubernetes version) + +```bash +# Deploy the cluster stack for a specific K8s version +./05-deploy-cstack.sh your-settings.env + +# Wait for cluster class to be ready +./06-wait-clusterclass.sh your-settings.env +``` + +### Cluster Lifecycle Management (Run per cluster) + +```bash +# Create a new cluster +./07-create-cluster.sh your-settings.env + +# Wait for cluster to be ready +./08-wait-cluster.sh your-settings.env + +# Delete a cluster when no longer needed +./16-delete-cluster.sh your-settings.env +``` + +## Configuration Parameters + +Key parameters in the cluster settings file include: + +- `CS_NAMESPACE`: Namespace for cluster resources +- `CLOUDS_YAML`: Path to OpenStack credentials file +- `OS_CLOUD`: Name of the cloud in the clouds.yaml file +- `CS_MAINVER`: Kubernetes major.minor version (e.g., 1.32) +- `CS_VERSION`: Cluster stack template version +- `CL_PATCHVER`: Full Kubernetes version (e.g., 1.32.3) +- `CL_NAME`: Cluster name +- `CL_PODCIDR`: Pod CIDR range +- `CL_SVCCIDR`: Service CIDR range +- `CL_CTRLNODES`: Number of control plane nodes +- `CL_WRKRNODES`: Number of worker nodes + +## Implementation Notes + +- The scripts follow a modular design to accommodate different frequencies of operations +- They handle the installation of required tools, CAPI components, and the cluster stack operator +- Cloud credentials are managed through Kubernetes secrets +- Cluster deployment is templated based on the settings file +- Some scripts (e.g., waiting for resources) are placeholders with "not yet implemented" functionality + +## Troubleshooting + +If a script fails: +1. Check the error message for missing configuration parameters +2. Verify that previous steps completed successfully +3. Ensure your OpenStack credentials in `clouds.yaml` are correct +4. Verify the Kubernetes resources with `kubectl get` commands as shown in the scripts \ No newline at end of file diff --git a/ciab.cert.sample b/ciab.cert.sample new file mode 100644 index 0000000..e69de29 diff --git a/clouds.yaml.sample b/clouds.yaml.sample new file mode 100644 index 0000000..9e85f9b --- /dev/null +++ b/clouds.yaml.sample @@ -0,0 +1,14 @@ +clouds: + openstack: + auth: + auth_url: "https://api.in-a-box.cloud:5000/v3" + username: "test" + password: "test" + project_name: "test" + project_domain_name: "Default" + user_domain_name: "Default" + region_name: "RegionOne" + interface: "public" + identity_api_version: 3 + verify: false # Skip TLS verification for self-signed certs + cacert: "/path/to/ca-bundle.crt" \ No newline at end of file diff --git a/cluster-settings-template.env b/cluster-settings-template.env index ea099d5..835e97c 100644 --- a/cluster-settings-template.env +++ b/cluster-settings-template.env @@ -30,5 +30,5 @@ CL_CTRLNODES=1 # Number of (initial) worker nodes CL_WRKRNODES=1 ### Autofill magic, don't touch -CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}) +CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} diff --git a/cluster-settings.env.sample b/cluster-settings.env.sample new file mode 100644 index 0000000..c218c58 --- /dev/null +++ b/cluster-settings.env.sample @@ -0,0 +1,34 @@ +# Cluster settings +# This is a file to be sourced from bash +# Don't do this with untrusted files! + +### Per namespace: secrets +# The namespace to keep your CS objects in for a set of clusters +CS_NAMESPACE=cluster +# Location of the clouds.yaml +CLOUDS_YAML=~/.config/openstack/clouds.yaml +# Name of the cloud in there -- currently it must be called openstack +OS_CLOUD=${OS_CLOUD:-openstack} + +### Per cluster stack settings +# Kubernetes Maj.Min, e.g. 1.30 (without leading v) +CS_MAINVER=1.30 +# CS Template version that matches, e.g. v1 or v0-sha.XXXXXXX +CS_VERSION=v1 + +### Now the per workload cluster settings +# Full K8s Version Maj.Min.Patch, e.g. 1.30.3 (this is per cluster) +CL_PATCHVER=1.30.3 +# Cluster name +CL_NAME=my-cluster +# Pod CIDR (e.g. 172.16.0.0/16) +CL_PODCIDR=172.16.0.0/18 +# Service CIDR (e.g. 10.96.0.0/12) +CL_SVCCIDR=10.96.0.0/14 +# Number of (initial) control plane nodes +CL_CTRLNODES=1 +# Number of (initial) worker nodes +CL_WRKRNODES=1 + +### Autofill magic, don't touch +CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} \ No newline at end of file From 9d5a4d0cc99a2f720f2ac5184842f8d40be4d190 Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Thu, 15 May 2025 09:32:02 +0200 Subject: [PATCH 02/11] Fixed issues --- ...p-vm-cs.sh => 00-bootstrap-vm-cs-ubuntu.sh | 0 08-wait-cluster.sh | 49 +++++++++++++++++-- 2 files changed, 44 insertions(+), 5 deletions(-) rename 00-bootstrap-vm-cs.sh => 00-bootstrap-vm-cs-ubuntu.sh (100%) diff --git a/00-bootstrap-vm-cs.sh b/00-bootstrap-vm-cs-ubuntu.sh similarity index 100% rename from 00-bootstrap-vm-cs.sh rename to 00-bootstrap-vm-cs-ubuntu.sh diff --git a/08-wait-cluster.sh b/08-wait-cluster.sh index 93763b7..c5bb472 100755 --- a/08-wait-cluster.sh +++ b/08-wait-cluster.sh @@ -11,9 +11,48 @@ else fi # Read settings -- make sure you can trust it source "$SET" -# ToDo: Wait for cluster -echo "The cluster should exist now" -echo "WARN: Waiting not yet implemented" +# Wait for cluster to be ready +echo "Waiting for cluster $CL_NAME to be ready..." set -x -clusterctl describe cluster -n "$CS_NAMESPACE" $CL_NAME -echo clusterctl get kubeconfig -n "$CS_NAMESPACE" $CL_NAME > "~/.kube/$CS_NAMESPACE.$CL_NAME.yaml" + +TIMEOUT=3600 # 60 minutes timeout +START_TIME=$(date +%s) +READY=false + +while [ "$READY" = "false" ]; do + # Check if timeout has been reached + CURRENT_TIME=$(date +%s) + ELAPSED_TIME=$((CURRENT_TIME - START_TIME)) + if [ $ELAPSED_TIME -gt $TIMEOUT ]; then + echo "Timeout waiting for cluster to be ready after $((TIMEOUT/60)) minutes" + exit 1 + fi + + # Check cluster status using kubectl instead of clusterctl json output + if kubectl get cluster -n "$CS_NAMESPACE" $CL_NAME -o jsonpath='{.status.phase}' 2>/dev/null; then + CLUSTER_STATUS=$(kubectl get cluster -n "$CS_NAMESPACE" $CL_NAME -o jsonpath='{.status.phase}' 2>/dev/null) + echo "Current cluster status: $CLUSTER_STATUS" + else + CLUSTER_STATUS="Unknown" + echo "Current cluster status: Unknown (cluster resource may not be fully created yet)" + fi + + # Check if the cluster is ready based on status + if [ "$CLUSTER_STATUS" = "Provisioned" ]; then + READY=true + echo "Cluster is ready!" + else + # Show current cluster state + clusterctl describe cluster -n "$CS_NAMESPACE" $CL_NAME + echo "Waiting for cluster to be ready... (elapsed: $((ELAPSED_TIME/60)) minutes)" + sleep 30 + fi +done + +# Get kubeconfig once the cluster is ready +echo "Saving kubeconfig to ~/.kube/$CS_NAMESPACE.$CL_NAME.yaml" +mkdir -p ~/.kube +clusterctl get kubeconfig -n "$CS_NAMESPACE" $CL_NAME > ~/.kube/$CS_NAMESPACE.$CL_NAME.yaml + +echo "Cluster $CL_NAME is ready and kubeconfig has been saved" +echo "You can access the cluster with: export KUBECONFIG=~/.kube/$CS_NAMESPACE.$CL_NAME.yaml" From e08a7726aa247715645e69e79332ee6271c3bcec Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Fri, 16 May 2025 05:51:54 +0200 Subject: [PATCH 03/11] Added Linux Support in Bootstrap Script --- README.md | 69 ++++++++++++++++++++++++----------- cluster-settings-template.env | 21 ++++++++++- cluster-settings.env.sample | 18 ++++++++- 3 files changed, 85 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 7166470..dedc369 100644 --- a/README.md +++ b/README.md @@ -3,44 +3,71 @@ Some helpful snippets of code to automate the creation of Cluster-Stacks ## Goals Creating cluster stacks and cluster based on these takes a significant -number of stacks that are hard to remember correctly for people that are -not cluster-API and Cluster Class expert. +number of steps that are hard to remember correctly for people that are +not cluster-API and Cluster Class experts. -We this hide them in a number of distinct steps that are in numbered scripts. -The reason for not doing everything in one script is that you do register -cloud secrets or install capi much less often than install cluster classses -which happens much less often than creating clusters. +We hide them in a number of distinct scripts. The reason for not doing +everything in one script is that you register cloud secrets or install CAPI +much less often than install cluster classes, which happens much less often +than creating clusters. ## Settings There is a [cluster-settings-template.env](cluster-settings-template.env) file that contains the parameters typically adjusted by users. Please create a -copy, fill it in, and pass it to the scripts. +copy, fill it in, and pass it to the scripts. You can also use the +[cluster-settings.env.sample](cluster-settings.env.sample) file as a reference, +which includes example values for all parameters. + +### Configuration Parameters + +#### Registry and Repository Settings +- `CS_REGISTRY=registry.scs.community/kaas/cluster-stacks`: Registry for cluster stacks +- `CSO_HELM_REPO=oci://registry.scs.community/cluster-stacks/cso`: Helm chart repository for CSO + +#### Namespace and Project Settings +- `CS_NAMESPACE=cluster`: Namespace for cluster resources +- `CLOUDS_YAML=~/.config/openstack/clouds.yaml`: Path to OpenStack credentials file +- `OS_CLOUD=${OS_CLOUD:-openstack}`: Name of the cloud in the clouds.yaml file + +#### Cluster Stack Settings +- `CS_MAINVER=`: Kubernetes major.minor version (e.g., 1.32) +- `CS_VERSION=`: Cluster stack template version (e.g., v1 or v0-sha.XXXXXXX) +- `CS_CHANNEL=custom`: Update channel for ClusterStack +- `CS_AUTO_SUBSCRIBE=false`: Whether to automatically subscribe to updates + +#### Workload Cluster Settings +- `CL_PATCHVER=`: Full Kubernetes version (e.g., 1.32.3) +- `CL_NAME=`: Cluster name +- `CL_PODCIDR=172.16.0.0/18`: Pod CIDR range +- `CL_SVCCIDR=10.96.0.0/14`: Service CIDR range +- `CL_CTRLNODES=1`: Number of control plane nodes +- `CL_WRKRNODES=1`: Number of worker nodes +- `CL_WORKER_CLASS=default-worker`: Worker class used for machine deployments +- `CL_LB_TYPE=octavia-ovn`: Load balancer type (depends on OpenStack environment) +- `CL_WAIT_TIMEOUT=3600`: Timeout for waiting on cluster provisioning (seconds) ## Scripts ### Once per management host -* 00-bootstrap-vm-cs.sh: Install the needed software to be able to do - cluster management on this host. (Developed for Debian 12.) +* `00-bootstrap-vm-cs.sh`: Install the needed software to be able to do + cluster management on this host. (Developed for Debian and Ubuntu.) This is only needed if you do not have the needed tools preinstalled. -* 01-kind-cluster.sh: Create kind cluster -* 02-deploy-capi.sh: Install ORC and CAPI. -* 03-deploy-cso.sh: Install the Cluster Stack Operator. +* `01-kind-cluster.sh`: Create kind cluster +* `02-deploy-capi.sh`: Install ORC and CAPI. +* `03-deploy-cso.sh`: Install the Cluster Stack Operator. ### Once per OpenStack Project in which we want to install clusters (NS) -* 04-cloud-secret.sh: Create namespace and secrets to work with the +* `04-cloud-secret.sh`: Create namespace and secrets to work with the wanted OpenStack project. ### Once per Kubernetes aka CS version (maj.min) -* 05-deploy-cstack.sh: Create the Cluster Stack which is a template +* `05-deploy-cstack.sh`: Create the Cluster Stack which is a template for various clusters with the same major minor version of k8s. Should trigger cluster class creation and image registration. -* 06-wait-clusterclass.sh: Wait for the cluster class (not yet implemented) +* `06-wait-clusterclass.sh`: Wait for the cluster class to be ready ### Once per cluster -* 07-create-cluster.sh: Create a workload cluster as per all the settings +* `07-create-cluster.sh`: Create a workload cluster as per all the settings that are passed. -* 08-wait-cluster.sh: Wait for the workload cluster (not yet implemented) - -* 17-delete-cluster.sh: Remove cluster again. - +* `08-wait-cluster.sh`: Wait for the workload cluster to be ready and save kubeconfig - +* `17-delete-cluster.sh`: Remove cluster when no longer needed. diff --git a/cluster-settings-template.env b/cluster-settings-template.env index 835e97c..714bf8b 100644 --- a/cluster-settings-template.env +++ b/cluster-settings-template.env @@ -11,12 +11,24 @@ CS_NAMESPACE=cluster CLOUDS_YAML=~/.config/openstack/clouds.yaml # Name of the cloud in there -- currently it must be called openstack OS_CLOUD=${OS_CLOUD:-openstack} + +### Registry and repository settings +# Registry for cluster stacks +CS_REGISTRY=registry.scs.community/kaas/cluster-stacks +# Helm chart repository for CSO +CSO_HELM_REPO=oci://registry.scs.community/cluster-stacks/cso + ### Per cluster stack settings # Kubernetes Maj.Min, e.g. 1.32 (without leading v) CS_MAINVER= # CS Template version that matches, e.g. v1 or v0-sha.XXXXXXX CS_VERSION= -### Now the per workload cluster settings +# Update channel for ClusterStack +CS_CHANNEL=custom +# Whether to automatically subscribe to updates +CS_AUTO_SUBSCRIBE=false + +### Per workload cluster settings # Full K8s Version Maj.Min.Patch, e.g. 1.32.3 (this is per cluster) CL_PATCHVER= # Cluster name @@ -29,6 +41,13 @@ CL_SVCCIDR=10.96.0.0/14 CL_CTRLNODES=1 # Number of (initial) worker nodes CL_WRKRNODES=1 +# Worker class used for machine deployments +CL_WORKER_CLASS=default-worker +# Load balancer type (depends on OpenStack environment) +CL_LB_TYPE=octavia-ovn +# Timeout for waiting on cluster provisioning (seconds) +CL_WAIT_TIMEOUT=3600 + ### Autofill magic, don't touch CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} diff --git a/cluster-settings.env.sample b/cluster-settings.env.sample index c218c58..afe85f9 100644 --- a/cluster-settings.env.sample +++ b/cluster-settings.env.sample @@ -10,13 +10,23 @@ CLOUDS_YAML=~/.config/openstack/clouds.yaml # Name of the cloud in there -- currently it must be called openstack OS_CLOUD=${OS_CLOUD:-openstack} +### Registry and repository settings +# Registry for cluster stacks +CS_REGISTRY=registry.scs.community/kaas/cluster-stacks +# Helm chart repository for CSO +CSO_HELM_REPO=oci://registry.scs.community/cluster-stacks/cso + ### Per cluster stack settings # Kubernetes Maj.Min, e.g. 1.30 (without leading v) CS_MAINVER=1.30 # CS Template version that matches, e.g. v1 or v0-sha.XXXXXXX CS_VERSION=v1 +# Update channel for ClusterStack +CS_CHANNEL=custom +# Whether to automatically subscribe to updates +CS_AUTO_SUBSCRIBE=false -### Now the per workload cluster settings +### Per workload cluster settings # Full K8s Version Maj.Min.Patch, e.g. 1.30.3 (this is per cluster) CL_PATCHVER=1.30.3 # Cluster name @@ -29,6 +39,12 @@ CL_SVCCIDR=10.96.0.0/14 CL_CTRLNODES=1 # Number of (initial) worker nodes CL_WRKRNODES=1 +# Worker class used for machine deployments +CL_WORKER_CLASS=default-worker +# Load balancer type (depends on OpenStack environment) +CL_LB_TYPE=octavia-ovn +# Timeout for waiting on cluster provisioning (seconds) +CL_WAIT_TIMEOUT=3600 ### Autofill magic, don't touch CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} \ No newline at end of file From d9debc6041af2f1d821684efe45a729df7fc6f1b Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Fri, 16 May 2025 05:55:50 +0200 Subject: [PATCH 04/11] Adjusted .gitignore to allow for cert-sample-files --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index aec524d..689117c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ CLAUDE.md # Certificate files -*.cert.* +*.cert # Environment files cluster-settings.env From b4885eff4aebfc7a6753ee98057c32f702b0fe7c Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Fri, 16 May 2025 06:02:34 +0200 Subject: [PATCH 05/11] Adjusted cluster-settings.env.sample to contain a feasible configuration --- .gitignore | 1 + cluster-settings.env.sample | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 689117c..546454e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ CLAUDE.md # Environment files cluster-settings.env +clouds.yaml diff --git a/cluster-settings.env.sample b/cluster-settings.env.sample index afe85f9..0c2e782 100644 --- a/cluster-settings.env.sample +++ b/cluster-settings.env.sample @@ -20,7 +20,7 @@ CSO_HELM_REPO=oci://registry.scs.community/cluster-stacks/cso # Kubernetes Maj.Min, e.g. 1.30 (without leading v) CS_MAINVER=1.30 # CS Template version that matches, e.g. v1 or v0-sha.XXXXXXX -CS_VERSION=v1 +CS_VERSION=v3 # Update channel for ClusterStack CS_CHANNEL=custom # Whether to automatically subscribe to updates @@ -28,7 +28,7 @@ CS_AUTO_SUBSCRIBE=false ### Per workload cluster settings # Full K8s Version Maj.Min.Patch, e.g. 1.30.3 (this is per cluster) -CL_PATCHVER=1.30.3 +CL_PATCHVER=1.30.11 # Cluster name CL_NAME=my-cluster # Pod CIDR (e.g. 172.16.0.0/16) From ffec87021fd7c204014db8f22efd373c7021c8b1 Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sat, 17 May 2025 05:02:56 +0200 Subject: [PATCH 06/11] Make CCM load balancer type configurable and simplify cluster wait script - Add CS_CCMLB parameter to configuration files for CCM load balancer type - Replace hardcoded OCTOVN setting in cloud-secret script - Simplify wait-cluster script to show status without looping - Remove .yaml extension from kubeconfig filename --- 04-cloud-secret.sh | 5 +- 08-wait-cluster.sh | 104 +++++++++++++--------------------- cluster-settings-template.env | 2 + cluster-settings.env.sample | 2 + 4 files changed, 46 insertions(+), 67 deletions(-) diff --git a/04-cloud-secret.sh b/04-cloud-secret.sh index 42d8049..6c7747b 100755 --- a/04-cloud-secret.sh +++ b/04-cloud-secret.sh @@ -26,9 +26,8 @@ kubectl create namespace "$CS_NAMESPACE" || true if ! test -r "$CLOUDS_YAML"; then echo "clouds.yaml $CLOUDS_YAML not readable"; exit 2; fi CA=$(grep -A12 "^\s\s*$OS_CLOUD:\s*\$" $CLOUDS_YAML | grep 'cacert:' | head -n1 | sed 's/^ *cacert: //') OS_CACERT=${OS_CACERT:-$CA} -# FIXME: We will provide more settings in cluster-settings.env later, hardcode it for now -#if test "$CS_CCMLB=octavia-ovn"; then OCTOVN="--set octavia_ovn=true"; else unset OCTOVN; fi -OCTOVN="--set octavia_ovn=true" + +if test "$CS_CCMLB" = "octavia-ovn"; then OCTOVN="--set octavia_ovn=true"; else unset OCTOVN; fi if test -n "$OS_CACERT"; then echo "Found CA cert file configured to be $OS_CACERT" if test ! -r "$OS_CACERT"; then echo "... but could not access it. FATAL."; exit 3; fi diff --git a/08-wait-cluster.sh b/08-wait-cluster.sh index 612f9a4..e469600 100755 --- a/08-wait-cluster.sh +++ b/08-wait-cluster.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Wait for cluster to be ready and save kubeconfig +# Check cluster status and save kubeconfig set -e # We need settings @@ -19,67 +19,43 @@ fi # Read settings -- make sure you can trust it source "$SET" -# Display initial cluster state -echo "Checking initial state of cluster $CL_NAME in namespace $CS_NAMESPACE..." -clusterctl describe cluster -n "$CS_NAMESPACE" $CL_NAME - -# Wait for cluster to be ready -echo "Waiting for cluster $CL_NAME to be ready..." -set -x - -TIMEOUT=3600 # 60 minutes timeout -START_TIME=$(date +%s) -READY=false - -# Disable command echo for cleaner output -set +x -echo "Will timeout after $((TIMEOUT/60)) minutes if not ready" - -while [ "$READY" = "false" ]; do - # Check if timeout has been reached - CURRENT_TIME=$(date +%s) - ELAPSED_TIME=$((CURRENT_TIME - START_TIME)) - ELAPSED_MINUTES=$((ELAPSED_TIME/60)) - ELAPSED_SECONDS=$((ELAPSED_TIME%60)) - - if [ $ELAPSED_TIME -gt $TIMEOUT ]; then - echo "Timeout waiting for cluster to be ready after $ELAPSED_MINUTES minutes" - exit 1 - fi - - # Check cluster status using kubectl instead of clusterctl json output - if kubectl get cluster -n "$CS_NAMESPACE" $CL_NAME -o jsonpath='{.status.phase}' 2>/dev/null; then - CLUSTER_STATUS=$(kubectl get cluster -n "$CS_NAMESPACE" $CL_NAME -o jsonpath='{.status.phase}' 2>/dev/null) - echo "Current cluster status: $CLUSTER_STATUS" - else - CLUSTER_STATUS="Unknown" - echo "Current cluster status: Unknown (cluster resource may not be fully created yet)" - fi - - # Check if the cluster is ready based on status - if [ "$CLUSTER_STATUS" = "Provisioned" ]; then - READY=true - echo "Cluster is ready!" - else - # Show current cluster state - echo "Current cluster details:" - clusterctl describe cluster -n "$CS_NAMESPACE" $CL_NAME - echo "Waiting for cluster to be ready... (elapsed: $ELAPSED_MINUTES minutes, $ELAPSED_SECONDS seconds)" - sleep 30 - fi -done +# Display cluster state +echo "Checking state of cluster $CL_NAME in namespace $CS_NAMESPACE..." +echo -# Get kubeconfig once the cluster is ready -echo "Creating ~/.kube directory if needed..." -mkdir -p ~/.kube - -echo "Saving kubeconfig to ~/.kube/$CS_NAMESPACE.$CL_NAME.yaml" -clusterctl get kubeconfig -n "$CS_NAMESPACE" $CL_NAME > ~/.kube/$CS_NAMESPACE.$CL_NAME.yaml -chmod 600 ~/.kube/$CS_NAMESPACE.$CL_NAME.yaml - -echo "Cluster $CL_NAME is ready and kubeconfig has been saved" -echo "You can access the cluster with: export KUBECONFIG=~/.kube/$CS_NAMESPACE.$CL_NAME.yaml" - -# Display a quick cluster summary -echo "Displaying cluster info:" -KUBECONFIG=~/.kube/$CS_NAMESPACE.$CL_NAME.yaml kubectl cluster-info +# Show cluster description +clusterctl describe cluster -n "$CS_NAMESPACE" $CL_NAME +echo + +# Get cluster status if available +if kubectl get cluster -n "$CS_NAMESPACE" $CL_NAME &>/dev/null; then + CLUSTER_STATUS=$(kubectl get cluster -n "$CS_NAMESPACE" $CL_NAME -o jsonpath='{.status.phase}') + echo "Cluster status: $CLUSTER_STATUS" + + # If cluster is ready, save kubeconfig + if [ "$CLUSTER_STATUS" = "Provisioned" ]; then + echo "Cluster is ready!" + + # Get kubeconfig + echo "Creating ~/.kube directory if needed..." + mkdir -p ~/.kube + + echo "Saving kubeconfig to ~/.kube/$CS_NAMESPACE.$CL_NAME" + clusterctl get kubeconfig -n "$CS_NAMESPACE" $CL_NAME > ~/.kube/$CS_NAMESPACE.$CL_NAME + chmod 600 ~/.kube/$CS_NAMESPACE.$CL_NAME + + echo "Kubeconfig has been saved" + echo "You can access the cluster with: export KUBECONFIG=~/.kube/$CS_NAMESPACE.$CL_NAME" + echo + + # Display cluster info + echo "Displaying cluster info:" + KUBECONFIG=~/.kube/$CS_NAMESPACE.$CL_NAME kubectl cluster-info + else + echo "Cluster is not yet ready (status: $CLUSTER_STATUS)" + echo "Run this script again after some time to check status and save kubeconfig when ready" + fi +else + echo "Cluster $CL_NAME not found or not accessible in namespace $CS_NAMESPACE" + echo "Please check if the cluster has been created and you have the necessary permissions" +fi \ No newline at end of file diff --git a/cluster-settings-template.env b/cluster-settings-template.env index ec62b4a..b43346e 100644 --- a/cluster-settings-template.env +++ b/cluster-settings-template.env @@ -11,6 +11,8 @@ CS_NAMESPACE=cluster CLOUDS_YAML=~/.config/openstack/clouds.yaml # Name of the cloud in there -- currently it must be called openstack OS_CLOUD=${OS_CLOUD:-openstack} +# Cloud controller manager load balancer type (octavia-ovn or octavia-amphora) +CS_CCMLB=octavia-ovn ### Registry and repository settings # Registry for cluster stacks diff --git a/cluster-settings.env.sample b/cluster-settings.env.sample index 0c2e782..0744fa6 100644 --- a/cluster-settings.env.sample +++ b/cluster-settings.env.sample @@ -9,6 +9,8 @@ CS_NAMESPACE=cluster CLOUDS_YAML=~/.config/openstack/clouds.yaml # Name of the cloud in there -- currently it must be called openstack OS_CLOUD=${OS_CLOUD:-openstack} +# Cloud controller manager load balancer type (octavia-ovn or octavia-amphora) +CS_CCMLB=octavia-ovn ### Registry and repository settings # Registry for cluster stacks From 16804372f0bb0e8c5e9b7888bd94b562c8cd8b30 Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sat, 17 May 2025 05:09:08 +0200 Subject: [PATCH 07/11] Removed Claude.md file Signed-off-by: Karsten Samaschke --- CLAUDE.md | 96 ------------------------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 81da6e1..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,96 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Repository Purpose - -This repository contains scripts to automate the creation of Kubernetes clusters using Cluster API and OpenStack. The scripts are organized into a sequence of numbered steps that correspond to different phases of the cluster lifecycle management process. - -## Environment Setup - -Before running the scripts: - -1. Create a copy of `cluster-settings-template.env` and fill in the required parameters -2. Source this file: `source your-settings.env` or pass it as a parameter to the individual scripts - -## Script Execution Flow - -The scripts are designed to be run in numerical order, based on the frequency and context of operations: - -### Management Host Setup (Run once per host) - -```bash -# Install required tools (Debian-based systems) -./00-bootstrap-vm-cs.sh - -# Create a local kind cluster -./01-create-kind-cluster.sh - -# Install Cluster API components -./02-deploy-capi.sh - -# Install Cluster Stack Operator -./03-deploy-cso.sh -``` - -### Cloud Access Configuration (Run once per OpenStack project) - -```bash -# Create namespace and cloud secrets -./04-cloud-secret.sh your-settings.env -``` - -### Kubernetes Version Setup (Run once per Kubernetes version) - -```bash -# Deploy the cluster stack for a specific K8s version -./05-deploy-cstack.sh your-settings.env - -# Wait for cluster class to be ready -./06-wait-clusterclass.sh your-settings.env -``` - -### Cluster Lifecycle Management (Run per cluster) - -```bash -# Create a new cluster -./07-create-cluster.sh your-settings.env - -# Wait for cluster to be ready -./08-wait-cluster.sh your-settings.env - -# Delete a cluster when no longer needed -./16-delete-cluster.sh your-settings.env -``` - -## Configuration Parameters - -Key parameters in the cluster settings file include: - -- `CS_NAMESPACE`: Namespace for cluster resources -- `CLOUDS_YAML`: Path to OpenStack credentials file -- `OS_CLOUD`: Name of the cloud in the clouds.yaml file -- `CS_MAINVER`: Kubernetes major.minor version (e.g., 1.32) -- `CS_VERSION`: Cluster stack template version -- `CL_PATCHVER`: Full Kubernetes version (e.g., 1.32.3) -- `CL_NAME`: Cluster name -- `CL_PODCIDR`: Pod CIDR range -- `CL_SVCCIDR`: Service CIDR range -- `CL_CTRLNODES`: Number of control plane nodes -- `CL_WRKRNODES`: Number of worker nodes - -## Implementation Notes - -- The scripts follow a modular design to accommodate different frequencies of operations -- They handle the installation of required tools, CAPI components, and the cluster stack operator -- Cloud credentials are managed through Kubernetes secrets -- Cluster deployment is templated based on the settings file -- Some scripts (e.g., waiting for resources) are placeholders with "not yet implemented" functionality - -## Troubleshooting - -If a script fails: -1. Check the error message for missing configuration parameters -2. Verify that previous steps completed successfully -3. Ensure your OpenStack credentials in `clouds.yaml` are correct -4. Verify the Kubernetes resources with `kubectl get` commands as shown in the scripts \ No newline at end of file From 81d2227100a04dc87ef9dc7c97879109d39303f2 Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sat, 17 May 2025 05:10:44 +0200 Subject: [PATCH 08/11] Remove CLAUDE.md from version control --- CLAUDE.md | 96 ------------------------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 81da6e1..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,96 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Repository Purpose - -This repository contains scripts to automate the creation of Kubernetes clusters using Cluster API and OpenStack. The scripts are organized into a sequence of numbered steps that correspond to different phases of the cluster lifecycle management process. - -## Environment Setup - -Before running the scripts: - -1. Create a copy of `cluster-settings-template.env` and fill in the required parameters -2. Source this file: `source your-settings.env` or pass it as a parameter to the individual scripts - -## Script Execution Flow - -The scripts are designed to be run in numerical order, based on the frequency and context of operations: - -### Management Host Setup (Run once per host) - -```bash -# Install required tools (Debian-based systems) -./00-bootstrap-vm-cs.sh - -# Create a local kind cluster -./01-create-kind-cluster.sh - -# Install Cluster API components -./02-deploy-capi.sh - -# Install Cluster Stack Operator -./03-deploy-cso.sh -``` - -### Cloud Access Configuration (Run once per OpenStack project) - -```bash -# Create namespace and cloud secrets -./04-cloud-secret.sh your-settings.env -``` - -### Kubernetes Version Setup (Run once per Kubernetes version) - -```bash -# Deploy the cluster stack for a specific K8s version -./05-deploy-cstack.sh your-settings.env - -# Wait for cluster class to be ready -./06-wait-clusterclass.sh your-settings.env -``` - -### Cluster Lifecycle Management (Run per cluster) - -```bash -# Create a new cluster -./07-create-cluster.sh your-settings.env - -# Wait for cluster to be ready -./08-wait-cluster.sh your-settings.env - -# Delete a cluster when no longer needed -./16-delete-cluster.sh your-settings.env -``` - -## Configuration Parameters - -Key parameters in the cluster settings file include: - -- `CS_NAMESPACE`: Namespace for cluster resources -- `CLOUDS_YAML`: Path to OpenStack credentials file -- `OS_CLOUD`: Name of the cloud in the clouds.yaml file -- `CS_MAINVER`: Kubernetes major.minor version (e.g., 1.32) -- `CS_VERSION`: Cluster stack template version -- `CL_PATCHVER`: Full Kubernetes version (e.g., 1.32.3) -- `CL_NAME`: Cluster name -- `CL_PODCIDR`: Pod CIDR range -- `CL_SVCCIDR`: Service CIDR range -- `CL_CTRLNODES`: Number of control plane nodes -- `CL_WRKRNODES`: Number of worker nodes - -## Implementation Notes - -- The scripts follow a modular design to accommodate different frequencies of operations -- They handle the installation of required tools, CAPI components, and the cluster stack operator -- Cloud credentials are managed through Kubernetes secrets -- Cluster deployment is templated based on the settings file -- Some scripts (e.g., waiting for resources) are placeholders with "not yet implemented" functionality - -## Troubleshooting - -If a script fails: -1. Check the error message for missing configuration parameters -2. Verify that previous steps completed successfully -3. Ensure your OpenStack credentials in `clouds.yaml` are correct -4. Verify the Kubernetes resources with `kubectl get` commands as shown in the scripts \ No newline at end of file From dff7a43049e8db860c0c7e0547caa4467db46330 Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sat, 17 May 2025 05:29:36 +0200 Subject: [PATCH 09/11] Add config preparation utility and remove obsolete timeout parameter - Add 99-prepare-files.sh to automate certificate and clouds.yaml setup - Update README with prerequisites and instructions for config preparation - Clarify that only the last certificate from ca-certificates.crt is needed - Remove unused CL_WAIT_TIMEOUT parameter from all configuration files - Document how to obtain and extract Cloud-in-a-Box certificate --- 99-prepare-files.sh | 58 +++++++++++++++++++++++++++++++++++ README.md | 51 ++++++++++++++++++++++++++++-- cluster-settings-template.env | 2 -- cluster-settings.env.sample | 2 -- 4 files changed, 106 insertions(+), 7 deletions(-) create mode 100755 99-prepare-files.sh diff --git a/99-prepare-files.sh b/99-prepare-files.sh new file mode 100755 index 0000000..8538af2 --- /dev/null +++ b/99-prepare-files.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +set -e + +# Create the openstack config directory if it doesn't exist +mkdir -p ~/.config/openstack + +# Check for cert files in the current directory +CERT_FILE=$(find . -maxdepth 1 -name "*.cert" ! -name "*.sample" | head -n 1) + +if [ -n "$CERT_FILE" ]; then + echo "Found certificate file: $CERT_FILE" + # Copy cert file to openstack config directory + cp "$CERT_FILE" ~/.config/openstack/ + CERT_FILENAME=$(basename "$CERT_FILE") + CERT_ABSOLUTE_PATH="$HOME/.config/openstack/$CERT_FILENAME" + echo "Copied $CERT_FILE to $CERT_ABSOLUTE_PATH" +fi + +# Check for clouds.yaml file +if [ -f "clouds.yaml" ]; then + echo "Found clouds.yaml file" + + # If both cert and clouds.yaml exist, update the cacert path + if [ -n "$CERT_FILE" ]; then + echo "Updating cacert path in clouds.yaml to: $CERT_ABSOLUTE_PATH" + + # Check if cacert line exists in the file + if grep -q "cacert:" clouds.yaml; then + # Update existing cacert line + sed "s|cacert:.*|cacert: \"$CERT_ABSOLUTE_PATH\"|g" clouds.yaml > clouds.yaml.tmp + else + # Add cacert line under the cloud config (assuming the cloud name is at the correct indentation) + awk -v cert="$CERT_ABSOLUTE_PATH" ' + /^ [a-zA-Z0-9_-]+:/ { cloud_found = 1 } + cloud_found && /^ auth:/ { + print + print " cacert: \"" cert "\"" + cloud_found = 0 + next + } + { print } + ' clouds.yaml > clouds.yaml.tmp + fi + mv clouds.yaml.tmp clouds.yaml + else + echo "No certificate file found, removing cacert entry if present" + # Remove cacert line if no cert file exists + sed '/cacert:/d' clouds.yaml > clouds.yaml.tmp + mv clouds.yaml.tmp clouds.yaml + fi + + # Copy clouds.yaml to openstack config directory + cp clouds.yaml ~/.config/openstack/ + echo "Copied clouds.yaml to ~/.config/openstack/" +fi + +echo "OpenStack configuration preparation complete" \ No newline at end of file diff --git a/README.md b/README.md index dedc369..5f38e56 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,43 @@ everything in one script is that you register cloud secrets or install CAPI much less often than install cluster classes, which happens much less often than creating clusters. +## Prerequisites and Setup + +### Obtaining the Cloud-in-a-Box Certificate + +If you're using SCS Cloud-in-a-Box (CiaB), you'll need to obtain the certificate. The certificates are located on the CiaB manager host at `/etc/ssl/certs/ca-certificates.crt`. + +To obtain it: + +1. SSH into your CiaB manager host (usually as user `dragon`) +2. Copy the certificate file: + ```bash + scp dragon@:/etc/ssl/certs/ca-certificates.crt ./ca-certificates.crt + ``` +3. Extract the last certificate from the file (the CiaB-specific certificate): + ```bash + # Extract the last certificate from the bundle + tac ca-certificates.crt | awk '/-----BEGIN CERTIFICATE-----/{flag=1} flag{print} /-----END CERTIFICATE-----/{exit}' | tac > ciab.cert + ``` +4. Place this `ciab.cert` file in the same directory as your scripts + +### Preparing Configuration Files + +Before running the main scripts, you may want to use the utility script to prepare your configuration: + +1. Place your `ciab.cert` file (or any other OpenStack certificate with `.cert` extension) in the script directory +2. Create your `clouds.yaml` file from the template +3. Run the preparation script: + ```bash + ./99-prepare-files.sh + ``` + +This script will: +- Create the `~/.config/openstack` directory if it doesn't exist +- Copy any `.cert` files to the config directory +- Update `clouds.yaml` to reference the certificate with its absolute path +- Copy the updated `clouds.yaml` to the config directory + ## Settings There is a [cluster-settings-template.env](cluster-settings-template.env) file that contains the parameters typically adjusted by users. Please create a @@ -28,6 +65,7 @@ which includes example values for all parameters. - `CS_NAMESPACE=cluster`: Namespace for cluster resources - `CLOUDS_YAML=~/.config/openstack/clouds.yaml`: Path to OpenStack credentials file - `OS_CLOUD=${OS_CLOUD:-openstack}`: Name of the cloud in the clouds.yaml file +- `CS_CCMLB=octavia-ovn`: Cloud controller manager load balancer type (octavia-ovn or octavia-amphora) #### Cluster Stack Settings - `CS_MAINVER=`: Kubernetes major.minor version (e.g., 1.32) @@ -44,7 +82,6 @@ which includes example values for all parameters. - `CL_WRKRNODES=1`: Number of worker nodes - `CL_WORKER_CLASS=default-worker`: Worker class used for machine deployments - `CL_LB_TYPE=octavia-ovn`: Load balancer type (depends on OpenStack environment) -- `CL_WAIT_TIMEOUT=3600`: Timeout for waiting on cluster provisioning (seconds) ## Scripts ### Once per management host @@ -57,7 +94,8 @@ which includes example values for all parameters. ### Once per OpenStack Project in which we want to install clusters (NS) * `04-cloud-secret.sh`: Create namespace and secrets to work with the - wanted OpenStack project. + wanted OpenStack project. Uses the `CS_CCMLB` setting to configure + the cloud controller manager load balancer type. ### Once per Kubernetes aka CS version (maj.min) * `05-deploy-cstack.sh`: Create the Cluster Stack which is a template @@ -68,6 +106,13 @@ which includes example values for all parameters. ### Once per cluster * `07-create-cluster.sh`: Create a workload cluster as per all the settings that are passed. -* `08-wait-cluster.sh`: Wait for the workload cluster to be ready and save kubeconfig +* `08-wait-cluster.sh`: Check cluster status and save kubeconfig if ready. + This script no longer waits in a loop but provides immediate status + feedback. If the cluster is ready, it saves the kubeconfig to + `~/.kube/.` (without the .yaml extension). * `17-delete-cluster.sh`: Remove cluster when no longer needed. + +### Utility scripts +* `99-prepare-files.sh`: Prepare configuration files by copying certificates + and updating paths in clouds.yaml \ No newline at end of file diff --git a/cluster-settings-template.env b/cluster-settings-template.env index b43346e..f8e905e 100644 --- a/cluster-settings-template.env +++ b/cluster-settings-template.env @@ -47,8 +47,6 @@ CL_WRKRNODES=1 CL_WORKER_CLASS=default-worker # Load balancer type (depends on OpenStack environment) CL_LB_TYPE=octavia-ovn -# Timeout for waiting on cluster provisioning (seconds) -CL_WAIT_TIMEOUT=3600 ### Autofill magic, don't touch CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} diff --git a/cluster-settings.env.sample b/cluster-settings.env.sample index 0744fa6..851f857 100644 --- a/cluster-settings.env.sample +++ b/cluster-settings.env.sample @@ -45,8 +45,6 @@ CL_WRKRNODES=1 CL_WORKER_CLASS=default-worker # Load balancer type (depends on OpenStack environment) CL_LB_TYPE=octavia-ovn -# Timeout for waiting on cluster provisioning (seconds) -CL_WAIT_TIMEOUT=3600 ### Autofill magic, don't touch CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} \ No newline at end of file From 75a97adb5d02760a82ff9f64b63ad66db9762cdf Mon Sep 17 00:00:00 2001 From: Kurt Garloff Date: Wed, 21 May 2025 11:32:57 +0200 Subject: [PATCH 10/11] Put auth section at the end, use test domain. 04-cloud-secret.sh can only append auth values to the end. Signed-off-by: Kurt Garloff --- clouds.yaml.sample | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/clouds.yaml.sample b/clouds.yaml.sample index 9e85f9b..918b774 100644 --- a/clouds.yaml.sample +++ b/clouds.yaml.sample @@ -1,14 +1,15 @@ clouds: openstack: + region_name: "RegionOne" + interface: "public" + identity_api_version: 3 + #verify: false # Skip TLS verification, insecure, use only if you can't get the ca-bundle (next line) for self-signed certs + cacert: "/path/to/ca-bundle.crt" auth: auth_url: "https://api.in-a-box.cloud:5000/v3" + project_name: "test" + project_domain_name: "test" + user_domain_name: "test" + # These are often split off into a secure.yaml username: "test" password: "test" - project_name: "test" - project_domain_name: "Default" - user_domain_name: "Default" - region_name: "RegionOne" - interface: "public" - identity_api_version: 3 - verify: false # Skip TLS verification for self-signed certs - cacert: "/path/to/ca-bundle.crt" \ No newline at end of file From 01be45dba6036884f1b1cd102385a5233b64dbe7 Mon Sep 17 00:00:00 2001 From: Kurt Garloff Date: Wed, 21 May 2025 11:46:54 +0200 Subject: [PATCH 11/11] Streamline variable naming. Use LBTYPE and WORKERCLASS. Adjust sample to be consistent. Signed-off-by: Kurt Garloff --- 07-create-cluster.sh | 8 +++---- README.md | 6 ++--- clouds.yaml.sample | 2 +- cluster-settings-template.env | 10 ++++----- cluster-settings.env.sample | 41 ++++++++++------------------------- 5 files changed, 25 insertions(+), 42 deletions(-) diff --git a/07-create-cluster.sh b/07-create-cluster.sh index e3124cb..c5903b0 100755 --- a/07-create-cluster.sh +++ b/07-create-cluster.sh @@ -57,16 +57,16 @@ spec: topology: class: openstack-scs-${CS_MAINVER/./-}-$CS_VERSION controlPlane: - replicas: $CL_CTRLNODES + replicas: $CL_CONTROLNODES version: v$CL_PATCHVER workers: machineDeployments: - - class: default-worker + - class: $CL_WORKERCLASS name: md-0 - replicas: $CL_WRKRNODES + replicas: $CL_WORKERNODES variables: - name: apiserver_loadbalancer - value: "octavia-ovn" + value: "$CL_LBTYPE" EOF echo "Applying cluster manifest..." diff --git a/README.md b/README.md index 5f38e56..724e293 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ which includes example values for all parameters. - `CSO_HELM_REPO=oci://registry.scs.community/cluster-stacks/cso`: Helm chart repository for CSO #### Namespace and Project Settings -- `CS_NAMESPACE=cluster`: Namespace for cluster resources +- `CS_NAMESPACE=clusterns`: Namespace for cluster resources - `CLOUDS_YAML=~/.config/openstack/clouds.yaml`: Path to OpenStack credentials file - `OS_CLOUD=${OS_CLOUD:-openstack}`: Name of the cloud in the clouds.yaml file - `CS_CCMLB=octavia-ovn`: Cloud controller manager load balancer type (octavia-ovn or octavia-amphora) @@ -81,7 +81,7 @@ which includes example values for all parameters. - `CL_CTRLNODES=1`: Number of control plane nodes - `CL_WRKRNODES=1`: Number of worker nodes - `CL_WORKER_CLASS=default-worker`: Worker class used for machine deployments -- `CL_LB_TYPE=octavia-ovn`: Load balancer type (depends on OpenStack environment) +- `CL_LBTYPE=octavia-ovn`: Load balancer type (depends on OpenStack environment) ## Scripts ### Once per management host @@ -115,4 +115,4 @@ which includes example values for all parameters. ### Utility scripts * `99-prepare-files.sh`: Prepare configuration files by copying certificates - and updating paths in clouds.yaml \ No newline at end of file + and updating paths in clouds.yaml diff --git a/clouds.yaml.sample b/clouds.yaml.sample index 918b774..b169bd6 100644 --- a/clouds.yaml.sample +++ b/clouds.yaml.sample @@ -1,5 +1,5 @@ clouds: - openstack: + ciab-test: region_name: "RegionOne" interface: "public" identity_api_version: 3 diff --git a/cluster-settings-template.env b/cluster-settings-template.env index 6bccb3a..560bc6c 100644 --- a/cluster-settings-template.env +++ b/cluster-settings-template.env @@ -40,14 +40,14 @@ CL_PODCIDR=172.16.0.0/18 # Service CIDR (e.g. 10.96.0.0/12) CL_SVCCIDR=10.96.0.0/14 # Number of (initial) control plane nodes -CL_CTRLNODES=1 +CL_CONTROLNODES=1 # Number of (initial) worker nodes -CL_WRKRNODES=1 +CL_WORKERNODES=1 # Worker class used for machine deployments -CL_WORKER_CLASS=default-worker +CL_WORKERCLASS=default-worker # Load balancer type (depends on OpenStack environment) -CL_LB_TYPE=octavia-ovn +CL_LBTYPE=octavia-ovn ### Autofill magic, don't touch CS_NAMESPACE=${CS_NAMESPACE:-$OS_CLOUD} -CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} \ No newline at end of file +CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} diff --git a/cluster-settings.env.sample b/cluster-settings.env.sample index 851f857..c8cb6f1 100644 --- a/cluster-settings.env.sample +++ b/cluster-settings.env.sample @@ -1,50 +1,33 @@ -# Cluster settings -# This is a file to be sourced from bash -# Don't do this with untrusted files! +# Cluster settings +# This is a minimal sample file, please see cluster-settings-template.env +# for a commented version with all settings. ### Per namespace: secrets -# The namespace to keep your CS objects in for a set of clusters -CS_NAMESPACE=cluster -# Location of the clouds.yaml +#CS_NAMESPACE=ciab CLOUDS_YAML=~/.config/openstack/clouds.yaml -# Name of the cloud in there -- currently it must be called openstack -OS_CLOUD=${OS_CLOUD:-openstack} -# Cloud controller manager load balancer type (octavia-ovn or octavia-amphora) +OS_CLOUD=ciab-test CS_CCMLB=octavia-ovn ### Registry and repository settings -# Registry for cluster stacks CS_REGISTRY=registry.scs.community/kaas/cluster-stacks -# Helm chart repository for CSO CSO_HELM_REPO=oci://registry.scs.community/cluster-stacks/cso ### Per cluster stack settings -# Kubernetes Maj.Min, e.g. 1.30 (without leading v) -CS_MAINVER=1.30 -# CS Template version that matches, e.g. v1 or v0-sha.XXXXXXX +#CS_MAINVER=1.30 # autofilled CS_VERSION=v3 -# Update channel for ClusterStack CS_CHANNEL=custom -# Whether to automatically subscribe to updates CS_AUTO_SUBSCRIBE=false ### Per workload cluster settings -# Full K8s Version Maj.Min.Patch, e.g. 1.30.3 (this is per cluster) CL_PATCHVER=1.30.11 -# Cluster name CL_NAME=my-cluster -# Pod CIDR (e.g. 172.16.0.0/16) CL_PODCIDR=172.16.0.0/18 -# Service CIDR (e.g. 10.96.0.0/12) CL_SVCCIDR=10.96.0.0/14 -# Number of (initial) control plane nodes -CL_CTRLNODES=1 -# Number of (initial) worker nodes -CL_WRKRNODES=1 -# Worker class used for machine deployments -CL_WORKER_CLASS=default-worker -# Load balancer type (depends on OpenStack environment) -CL_LB_TYPE=octavia-ovn +CL_CONTROLNODES=1 +CL_WORKERNODES=1 +CL_WORKERCLASS=default-worker +CL_LBTYPE=octavia-ovn ### Autofill magic, don't touch -CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}} \ No newline at end of file +CS_NAMESPACE=${CS_NAMESPACE:-$OS_CLOUD} +CS_MAINVER=${CS_MAINVER:-${CL_PATCHVER%.*}}