From 29621df44ee9544d1214ca791874aac77f887360 Mon Sep 17 00:00:00 2001 From: alex-local Date: Fri, 5 Sep 2025 23:53:41 +0200 Subject: [PATCH] ci: add dry-run check and release artifact; matrix runners --- .github/workflows/ci.yml | 29 ++++++++++- setup_awx_local.sh | 104 ++++++++++++++++++++++++++++++++------- 2 files changed, 113 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2253ee..98b1bfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,10 @@ on: jobs: lint: name: Lint scripts & YAML - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04, ubuntu-20.04] steps: - name: Checkout uses: actions/checkout@v4 @@ -49,6 +52,12 @@ jobs: echo "No YAML files found to lint" fi + - name: Dry-run setup script + run: | + set -e + chmod +x awx-local-setup/setup_awx_local.sh + ./awx-local-setup/setup_awx_local.sh --dry-run --repo-root="$(pwd)" + kustomize-validate: name: Validate kustomize builds (if any) runs-on: ubuntu-latest @@ -75,3 +84,21 @@ jobs: fi done $ok || (echo "One or more kustomize builds failed" && exit 1) + + release: + name: Create release artifact + runs-on: ubuntu-latest + needs: [lint, kustomize-validate] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Create tarball + run: | + tar -czf awx-local-setup.tar.gz awx-local-setup + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: awx-local-setup + path: awx-local-setup.tar.gz diff --git a/setup_awx_local.sh b/setup_awx_local.sh index 4a40cae..b9b9e07 100755 --- a/setup_awx_local.sh +++ b/setup_awx_local.sh @@ -9,6 +9,7 @@ PORT=8082 NODEPORT=30081 AWX_NAMESPACE=awx REPO_ROOT="$(pwd)" +DRY_RUN=0 while [[ $# -gt 0 ]]; do case "$1" in @@ -16,6 +17,7 @@ while [[ $# -gt 0 ]]; do --nodeport) NODEPORT="$2"; shift 2 ;; --namespace) AWX_NAMESPACE="$2"; shift 2 ;; --repo-root) REPO_ROOT="$2"; shift 2 ;; + --dry-run) DRY_RUN=1; shift 1 ;; -h|--help) echo "Usage: $0 [--port PORT] [--nodeport NODEPORT] [--namespace NAMESPACE] [--repo-root PATH]"; exit 0 ;; *) echo "Unknown arg: $1"; exit 1 ;; esac @@ -25,35 +27,57 @@ echo "Running AWX local setup" echo "Port-forward local port: ${PORT} -> service:80 (bind 0.0.0.0)" echo "AWX NodePort to request: ${NODEPORT}" +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: cluster operations will be skipped" +fi + # prerequisites for cmd in kubectl minikube curl ss ip; do + if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + # skip checking cluster commands in dry-run mode + break + fi if ! command -v $cmd >/dev/null 2>&1; then echo "Missing prerequisite: $cmd"; exit 1 fi done # start minikube if needed -if ! kubectl get nodes >/dev/null 2>&1; then - echo "No Kubernetes cluster detected. Starting minikube..." - minikube start +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: skipping cluster start/check" +else + if ! kubectl get nodes >/dev/null 2>&1; then + echo "No Kubernetes cluster detected. Starting minikube..." + minikube start + fi fi # ensure namespace kubectl get ns ${AWX_NAMESPACE} >/dev/null 2>&1 || kubectl create ns ${AWX_NAMESPACE} # apply CRDs and operator manager from the awx-operator repo -if [ -d "${REPO_ROOT}/config/crd" ]; then - kubectl apply -k "${REPO_ROOT}/config/crd" -fi -if [ -d "${REPO_ROOT}/config/rbac" ]; then - kubectl apply -k "${REPO_ROOT}/config/rbac" -fi -if [ -d "${REPO_ROOT}/config/manager" ]; then - kubectl apply -k "${REPO_ROOT}/config/manager" -n ${AWX_NAMESPACE} || true +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: would apply CRDs and manager from ${REPO_ROOT}/config" +else + if [ -d "${REPO_ROOT}/config/crd" ]; then + kubectl apply -k "${REPO_ROOT}/config/crd" + fi + if [ -d "${REPO_ROOT}/config/rbac" ]; then + kubectl apply -k "${REPO_ROOT}/config/rbac" + fi + if [ -d "${REPO_ROOT}/config/manager" ]; then + kubectl apply -k "${REPO_ROOT}/config/manager" -n ${AWX_NAMESPACE} || true + fi fi # create leader-election RBAC if missing -cat <<'EOF' | kubectl apply -n ${AWX_NAMESPACE} -f - +cat <<'EOF' | ( + if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + cat >/dev/null + else + kubectl apply -n ${AWX_NAMESPACE} -f - + fi +) apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -64,7 +88,30 @@ rules: verbs: ["get","create","update","patch","delete","watch","list"] EOF -kubectl get rolebinding awx-operator-leader-election -n ${AWX_NAMESPACE} >/dev/null 2>&1 || cat <<'EOF' | kubectl apply -n ${AWX_NAMESPACE} -f - +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: would ensure rolebinding awx-operator-leader-election in ${AWX_NAMESPACE}" +else + kubectl get rolebinding awx-operator-leader-election -n ${AWX_NAMESPACE} >/dev/null 2>&1 || cat <<'EOF' | kubectl apply -n ${AWX_NAMESPACE} -f - +EOF +fi + +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + : +else + cat <<'EOF' | kubectl apply -n ${AWX_NAMESPACE} -f - +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: awx-operator-leader-election +subjects: +- kind: ServiceAccount + name: controller-manager +roleRef: + kind: Role + name: awx-operator-leader-election + apiGroup: rbac.authorization.k8s.io +EOF +fi apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -80,7 +127,11 @@ EOF # wait for operator deployment to be available echo "Waiting for controller-manager deployment to be available..." -kubectl -n ${AWX_NAMESPACE} wait --for=condition=available deployment/controller-manager --timeout=180s || true +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: skipping wait for controller-manager" +else + kubectl -n ${AWX_NAMESPACE} wait --for=condition=available deployment/controller-manager --timeout=180s || true +fi # Create corrected AWX CR yaml in /tmp and apply TMP_CR=/tmp/awx-cr-generated.yaml @@ -101,22 +152,37 @@ spec: EOF echo "Applying AWX CR from ${TMP_CR}" -kubectl apply -f ${TMP_CR} +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: would apply ${TMP_CR}" +else + kubectl apply -f ${TMP_CR} +fi # force reconcile -kubectl -n ${AWX_NAMESPACE} annotate awx awx awx-operator-refresh=$(date +%s) --overwrite || true +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: would annotate AWX CR to force reconcile" +else + kubectl -n ${AWX_NAMESPACE} annotate awx awx awx-operator-refresh=$(date +%s) --overwrite || true +fi # wait for awx-web pod readiness (give it a long timeout because migrations run) echo "Waiting for awx-web pod to become ready (this may take several minutes)..." -# wait until a pod with label app.kubernetes.io/name=awx-web is ready -kubectl -n ${AWX_NAMESPACE} wait --for=condition=ready pod -l app.kubernetes.io/name=awx-web --timeout=900s || true +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: skipping wait for awx-web readiness" +else + kubectl -n ${AWX_NAMESPACE} wait --for=condition=ready pod -l app.kubernetes.io/name=awx-web --timeout=900s || true +fi # choose a free local port and start port-forward bound to 0.0.0.0 for p in ${PORT} $(seq 18080 18090); do ss -ltn | awk '{print $4}' | grep -q ":$p$" || { LISTEN_PORT=$p; break; } done if [ -z "${LISTEN_PORT:-}" ]; then echo "No free port found to bind port-forward"; exit 1; fi -kubectl -n ${AWX_NAMESPACE} port-forward --address 0.0.0.0 svc/awx-service ${LISTEN_PORT}:80 &>/tmp/awx-port-forward.log & echo $! > /tmp/awx-pf.pid +if [ "${DRY_RUN}" = "1" ] || [ "${SKIP_CLUSTER:-0}" = "1" ]; then + echo "DRY RUN: would start port-forward: host-port ${LISTEN_PORT} -> awx-service:80" +else + kubectl -n ${AWX_NAMESPACE} port-forward --address 0.0.0.0 svc/awx-service ${LISTEN_PORT}:80 &>/tmp/awx-port-forward.log & echo $! > /tmp/awx-pf.pid +fi sleep 1 echo "Port-forward started: host-port ${LISTEN_PORT} -> awx-service:80"