apiVersion: argoproj.io/v1alpha1 kind: WorkflowTemplate metadata: name: environment-provisioner namespace: argo-workflows spec: serviceAccountName: argo-workflow arguments: parameters: - name: namespace value: default - name: services value: "" - name: ttl value: "8" - name: owner value: unknown - name: componentMetadata value: '{}' - name: dependencyPreferences value: '{}' - name: valuesFileOverrides value: '{}' - name: manualDependencyUrls value: '{}' - name: helmParameterOverrides value: '{}' - name: helmExternalParams value: '{}' entrypoint: provision-environment templates: - inputs: parameters: - name: namespace - name: services - name: ttl - name: owner - name: componentMetadata - name: dependencyPreferences - name: valuesFileOverrides - name: manualDependencyUrls - name: helmParameterOverrides - name: helmExternalParams name: provision-environment steps: - - arguments: parameters: - name: namespace value: '{{inputs.parameters.namespace}}' - name: services value: '{{inputs.parameters.services}}' - name: ttl value: '{{inputs.parameters.ttl}}' - name: owner value: '{{inputs.parameters.owner}}' - name: componentMetadata value: '{{inputs.parameters.componentMetadata}}' - name: dependencyPreferences value: '{{inputs.parameters.dependencyPreferences}}' - name: valuesFileOverrides value: '{{inputs.parameters.valuesFileOverrides}}' - name: manualDependencyUrls value: '{{inputs.parameters.manualDependencyUrls}}' - name: helmParameterOverrides value: '{{inputs.parameters.helmParameterOverrides}}' - name: helmExternalParams value: '{{inputs.parameters.helmExternalParams}}' name: create-git-repo template: create-repo - - arguments: parameters: - name: namespace value: '{{inputs.parameters.namespace}}' - name: owner value: '{{inputs.parameters.owner}}' name: create-argocd-app template: create-argo-app - - arguments: parameters: - name: namespace value: '{{inputs.parameters.namespace}}' - name: services value: '{{inputs.parameters.services}}' name: register-hades-ips template: register-hades-ips - inputs: parameters: - name: namespace - name: services - name: ttl - name: owner - name: componentMetadata - name: dependencyPreferences - name: valuesFileOverrides - name: manualDependencyUrls - name: helmParameterOverrides - name: helmExternalParams name: create-repo script: command: - sh env: # Credentials from External Secrets (GCP Secret Manager) - name: GITEA_URL valueFrom: secretKeyRef: name: gitea-credentials key: url - name: GITEA_USER valueFrom: secretKeyRef: name: gitea-credentials key: username - name: GITEA_PASSWORD valueFrom: secretKeyRef: name: gitea-credentials key: password image: alpine/git:latest source: | #!/bin/sh set -e NAMESPACE="{{inputs.parameters.namespace}}" SERVICES="{{inputs.parameters.services}}" TTL="{{inputs.parameters.ttl}}" OWNER="{{inputs.parameters.owner}}" COMPONENT_METADATA='{{inputs.parameters.componentMetadata}}' DEPENDENCY_PREFS='{{inputs.parameters.dependencyPreferences}}' VALUES_FILE_OVERRIDES='{{inputs.parameters.valuesFileOverrides}}' MANUAL_DEPENDENCY_URLS='{{inputs.parameters.manualDependencyUrls}}' HELM_PARAMETER_OVERRIDES='{{inputs.parameters.helmParameterOverrides}}' HELM_EXTERNAL_PARAMS='{{inputs.parameters.helmExternalParams}}' # Sanitize namespace name (replace spaces and special chars with hyphens) NAMESPACE=$(echo "$NAMESPACE" | tr '[:upper:]' '[:lower:]' | tr ' _' '-' | sed 's/[^a-z0-9-]//g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//') # Sanitize owner name for labels (Kubernetes labels have strict requirements) OWNER_LABEL=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]' | tr ' _' '-' | sed 's/[^a-z0-9-]//g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//') echo "Creating Git repository for namespace: $NAMESPACE" # Install curl and jq apk add --no-cache curl jq # Create repository in Gitea REPO_NAME="environment-$NAMESPACE" # Extract hostname from GITEA_URL for git credentials GITEA_HOST=$(echo "$GITEA_URL" | sed 's|https://||' | sed 's|http://||' | cut -d'/' -f1) echo "Creating Gitea repository: $REPO_NAME" curl -X POST "$GITEA_URL/api/v1/user/repos" \ -u "$GITEA_USER:$GITEA_PASSWORD" \ -H "Content-Type: application/json" \ -d "{ \"name\": \"$REPO_NAME\", \"description\": \"Environment for $NAMESPACE (owner: $OWNER, ttl: ${TTL}h)\", \"private\": false, \"auto_init\": true }" || echo "Repository may already exist" # Clone the repository git config --global http.sslVerify true git config --global user.email "backstage@osmos.io" git config --global user.name "Backstage" # URL encode the password for git credentials ENCODED_PASSWORD=$(printf %s "$GITEA_PASSWORD" | jq -sRr @uri) # Configure credentials git config --global credential.helper store printf "https://%s:%s@%s\n" "$GITEA_USER" "$ENCODED_PASSWORD" "$GITEA_HOST" > $HOME/.git-credentials REPO_URL="$GITEA_URL/$GITEA_USER/$REPO_NAME.git" git clone "$REPO_URL" /tmp/repo 2>&1 || (cd /tmp/repo && git pull 2>&1) cd /tmp/repo # Create manifests directory mkdir -p manifests # Create namespace manifest cat > manifests/namespace.yaml < manifests/networkpolicy.yaml < "manifests/$APP_NAME-application.yaml" < "manifests/$SANITIZED_SERVICE-deployment.yaml" < README.md </dev/null || echo "") if [ -z "$MYSQL_POD" ]; then # Try alternative label MYSQL_POD=$(kubectl get pods -n "$NAMESPACE" -o name 2>/dev/null | grep -i "mysql" | head -1 | sed 's/pod\///') fi if [ -z "$MYSQL_POD" ]; then echo "Warning: Could not find Hades MySQL pod" echo "Skipping IP registration - Hades may not be deployed" exit 0 fi echo "Found MySQL pod: $MYSQL_POD" # Wait for Hades MySQL to be ready (should already be running from previous steps) echo "Waiting for Hades MySQL to be ready..." MAX_WAIT=120 WAIT_TIME=0 while [ $WAIT_TIME -lt $MAX_WAIT ]; do if kubectl exec -n "$NAMESPACE" "$MYSQL_POD" -- mysql -u "$MYSQL_USER" -p"$MYSQL_PWD" -e "SELECT 1" "$MYSQL_DB" > /dev/null 2>&1; then echo "Hades MySQL is ready" break fi echo " Waiting for MySQL... (${WAIT_TIME}s)" sleep 5 WAIT_TIME=$((WAIT_TIME + 5)) done if [ $WAIT_TIME -ge $MAX_WAIT ]; then echo "Warning: Could not connect to Hades MySQL after ${MAX_WAIT}s" echo "Skipping IP registration - Hades may not be deployed" exit 0 fi # Wait for Hades dbinit job to complete (creates the database schema) echo "Waiting for Hades dbinit job to complete..." DBINIT_MAX_WAIT=120 DBINIT_WAIT_TIME=0 while [ $DBINIT_WAIT_TIME -lt $DBINIT_MAX_WAIT ]; do # Check if dbinit job exists and is complete DBINIT_JOB=$(kubectl get jobs -n "$NAMESPACE" -o name 2>/dev/null | grep -i "hades.*dbinit" | head -1) || DBINIT_JOB="" if [ -n "$DBINIT_JOB" ]; then DBINIT_COMPLETE=$(kubectl get "$DBINIT_JOB" -n "$NAMESPACE" -o jsonpath='{.status.succeeded}' 2>/dev/null) || DBINIT_COMPLETE="0" if [ "$DBINIT_COMPLETE" = "1" ]; then echo "Hades dbinit job completed" break fi fi echo " Waiting for dbinit job... (${DBINIT_WAIT_TIME}s)" sleep 5 DBINIT_WAIT_TIME=$((DBINIT_WAIT_TIME + 5)) done if [ $DBINIT_WAIT_TIME -ge $DBINIT_MAX_WAIT ]; then echo "Warning: Hades dbinit job not completed after ${DBINIT_MAX_WAIT}s" echo "Tables may not exist - attempting registration anyway" fi # Helper function to run MySQL commands via kubectl exec run_mysql() { kubectl exec -n "$NAMESPACE" "$MYSQL_POD" -- mysql -u "$MYSQL_USER" -p"$MYSQL_PWD" "$MYSQL_DB" -sN -e "$1" 2>/dev/null || true } # Function to register IP for an app register_ip() { local app_name="$1" local ip="$2" if [ -z "$app_name" ] || [ -z "$ip" ]; then return 0 fi # Get or create app_id APP_ID=$(run_mysql "SELECT id FROM apps WHERE name='$app_name'" || echo "") if [ -z "$APP_ID" ]; then echo " Creating app entry for: $app_name" run_mysql "INSERT INTO apps (name, token, creation_date, last_update, is_deleted) VALUES ('$app_name', UUID(), NOW(), NOW(), 0)" || true APP_ID=$(run_mysql "SELECT id FROM apps WHERE name='$app_name'" || echo "") fi if [ -n "$APP_ID" ]; then # Check if IP already exists EXISTING=$(run_mysql "SELECT id FROM app_ips_accesses WHERE app_id=$APP_ID AND ipaddress='$ip'" || echo "") if [ -z "$EXISTING" ]; then echo " Registering IP $ip for app $app_name (app_id: $APP_ID)" run_mysql "INSERT INTO app_ips_accesses (app_id, ipaddress, creation_date, last_update, is_deleted) VALUES ($APP_ID, '$ip', NOW(), NOW(), 0)" || true else echo " IP $ip already registered for $app_name" fi fi return 0 } # 1. Collect common bridge/gateway IPs (handles SNAT across different clusters) COMMON_BRIDGE_IPS="10.244.0.1 10.96.0.1 172.17.0.1" echo "Common bridge IPs: $COMMON_BRIDGE_IPS" # 2. Get node internal IPs NODE_IPS=$(kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}' 2>/dev/null || echo "") echo "Node IPs: $NODE_IPS" # 3. Get pod network CIDR gateway POD_CIDR=$(kubectl get nodes -o jsonpath='{.items[0].spec.podCIDR}' 2>/dev/null || echo "") if [ -n "$POD_CIDR" ]; then GATEWAY_IP=$(echo "$POD_CIDR" | sed 's/\.[0-9]*\/.*/.1/') COMMON_BRIDGE_IPS="$COMMON_BRIDGE_IPS $GATEWAY_IP" echo "Pod CIDR gateway: $GATEWAY_IP" fi # Combine all IPs to register ALL_IPS="$COMMON_BRIDGE_IPS $NODE_IPS" echo "" # Process each service SERVICES_LIST=$(echo "$SERVICES" | tr ',' ' ') for service in $SERVICES_LIST; do service=$(echo "$service" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') [ -z "$service" ] && continue # Skip resource references if echo "$service" | grep -q "^resource:"; then continue fi echo "Processing service: $service" # Get actual pod IPs for this service POD_IPS=$(kubectl get pods -n "$NAMESPACE" \ -l "app.kubernetes.io/instance=${service}" \ -o jsonpath='{.items[*].status.podIP}' 2>/dev/null) || POD_IPS="" # Register pod IPs for ip in $POD_IPS; do [ -n "$ip" ] && register_ip "$service" "$ip" done # Register bridge/gateway/node IPs for ip in $ALL_IPS; do [ -n "$ip" ] && register_ip "$service" "$ip" done echo "" done # Register IPs for 'nagios' app (used by health probes) echo "Registering IPs for nagios (health probes)..." for ip in $ALL_IPS; do [ -n "$ip" ] && register_ip "nagios" "$ip" done echo "" echo "=== IP Registration Summary ===" kubectl exec -n "$NAMESPACE" "$MYSQL_POD" -- mysql -u "$MYSQL_USER" -p"$MYSQL_PWD" "$MYSQL_DB" -e \ "SELECT a.name as app_name, i.ipaddress FROM apps a JOIN app_ips_accesses i ON a.id = i.app_id WHERE i.is_deleted = 0 ORDER BY a.name, i.ipaddress" 2>/dev/null || echo "Could not retrieve summary" echo "" echo "Hades IP registration completed"