osmos-infra/environment-provisioner/workflow-template.yaml
2026-01-19 12:27:12 +05:30

794 lines
29 KiB
YAML

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: valuesContentEdits
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: valuesContentEdits
- 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: valuesContentEdits
value: '{{inputs.parameters.valuesContentEdits}}'
- 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: valuesContentEdits
- 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=$(cat <<'EOFPARAM'
{{inputs.parameters.componentMetadata}}
EOFPARAM
)
DEPENDENCY_PREFS=$(cat <<'EOFPARAM'
{{inputs.parameters.dependencyPreferences}}
EOFPARAM
)
VALUES_FILE_OVERRIDES=$(cat <<'EOFPARAM'
{{inputs.parameters.valuesFileOverrides}}
EOFPARAM
)
MANUAL_DEPENDENCY_URLS=$(cat <<'EOFPARAM'
{{inputs.parameters.manualDependencyUrls}}
EOFPARAM
)
VALUES_CONTENT_EDITS=$(cat <<'EOFPARAM'
{{inputs.parameters.valuesContentEdits}}
EOFPARAM
)
HELM_EXTERNAL_PARAMS=$(cat <<'EOFPARAM'
{{inputs.parameters.helmExternalParams}}
EOFPARAM
)
# 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 <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: $NAMESPACE
labels:
name: $NAMESPACE
environment: test
owner: $OWNER_LABEL
ttl: ${TTL}h
provisioned-by: backstage
managed-by: argocd
annotations:
ttl-hours: "$TTL"
created-by: backstage-environment-provisioner
description: "Test environment for $OWNER"
original-owner: "$OWNER"
EOF
# Create NetworkPolicy for isolation (allows ingress from same namespace and ingress-nginx)
cat > manifests/networkpolicy.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: $NAMESPACE
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: $NAMESPACE
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
EOF
# Create service manifests
# Convert comma-separated services to space-separated for iteration
SERVICES_LIST=$(echo "$SERVICES" | tr ',' ' ')
for service in $SERVICES_LIST; do
service=$(echo "$service" | xargs) # trim whitespace
if [ -n "$service" ]; then
# Skip resource references (already filtered in frontend, but double-check)
if echo "$service" | grep -q "^resource:"; then
echo "Skipping resource reference: $service"
continue
fi
echo "Processing service: $service"
# Sanitize service name for filename (remove special chars)
SANITIZED_SERVICE=$(echo "$service" | tr '[:upper:]' '[:lower:]' | tr ' /:' '-' | sed 's/[^a-z0-9-]//g')
# Create truncated app name (Helm release names must be <= 53 chars)
APP_NAME=$(echo "$NAMESPACE-$SANITIZED_SERVICE" | cut -c1-53 | sed 's/-$//')
# Check if component has Helm chart annotations
HELM_REPO=$(echo "$COMPONENT_METADATA" | jq -r ".[\"$service\"][\"helm.cnoe.io/git-repo\"] // empty")
HELM_PATH=$(echo "$COMPONENT_METADATA" | jq -r ".[\"$service\"][\"helm.cnoe.io/chart-path\"] // empty")
HELM_VALUES_DEFAULT=$(echo "$COMPONENT_METADATA" | jq -r ".[\"$service\"][\"helm.cnoe.io/values-file\"] // \"values.yaml\"")
HELM_VALUES=$(echo "$VALUES_FILE_OVERRIDES" | jq -r ".[\"$service\"] // \"$HELM_VALUES_DEFAULT\"")
# DEBUG: Log what we're checking
echo " Checking for Helm annotations..."
echo " HELM_REPO: $HELM_REPO"
echo " HELM_PATH: $HELM_PATH"
echo " HELM_VALUES_DEFAULT: $HELM_VALUES_DEFAULT"
echo " HELM_VALUES (after override): $HELM_VALUES"
if [ -n "$HELM_REPO" ]; then
# Component has Helm chart - create ArgoCD Application manifest
echo "Creating ArgoCD Application for Helm chart: $service"
# Check dependency preferences for MySQL
MYSQL_ENABLED=$(echo "$DEPENDENCY_PREFS" | jq -r '.mysql // "true"')
echo " MySQL enabled: $MYSQL_ENABLED"
# Process dependency URL mappings
DEPENDENCY_URL_PARAMS=""
DEPENDENCY_URLS=$(echo "$COMPONENT_METADATA" | jq -r ".[\"$service\"][\"helm.cnoe.io/dependency-urls\"] // empty")
if [ -n "$DEPENDENCY_URLS" ]; then
echo " Processing dependency URL mappings..."
for dep_key in $(echo "$DEPENDENCY_URLS" | jq -r 'keys[]'); do
echo " Checking dependency: $dep_key"
# Check if this dependency is in our resolved services list
DEP_IN_SERVICES=$(echo "$SERVICES" | tr ',' '\n' | grep -c "^${dep_key}$" || echo "0")
# Get dependency configuration
SERVICE_DNS=$(echo "$DEPENDENCY_URLS" | jq -r ".[\"$dep_key\"].serviceDns // \"$dep_key\"")
SERVICE_PORT=$(echo "$DEPENDENCY_URLS" | jq -r ".[\"$dep_key\"].port // 8080")
HELM_PARAM=$(echo "$DEPENDENCY_URLS" | jq -r ".[\"$dep_key\"].helmParam")
# Sanitize service DNS name
SERVICE_DNS=$(echo "$SERVICE_DNS" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]//g')
CONSTRUCTED_URL=""
if [ "$DEP_IN_SERVICES" -gt 0 ]; then
# Dependency is being deployed to same namespace - use short DNS
echo " Dependency $dep_key is being deployed to same namespace"
CONSTRUCTED_URL="http://${SERVICE_DNS}:${SERVICE_PORT}"
else
# Check for manual URL override
MANUAL_URL=$(echo "$MANUAL_DEPENDENCY_URLS" | jq -r ".[\"$service\"][\"$HELM_PARAM\"] // empty")
if [ -n "$MANUAL_URL" ]; then
echo " Using manual URL for $dep_key"
CONSTRUCTED_URL="$MANUAL_URL"
else
echo " Dependency $dep_key NOT in deployed services and no manual URL - using defaults"
fi
fi
if [ -n "$CONSTRUCTED_URL" ] && [ -n "$HELM_PARAM" ]; then
echo " Setting $HELM_PARAM = $CONSTRUCTED_URL"
DEPENDENCY_URL_PARAMS="$DEPENDENCY_URL_PARAMS
- name: $HELM_PARAM
value: \"$CONSTRUCTED_URL\""
fi
done
fi
# Check for edited values content - write to override file if exists
INLINE_VALUES=""
EDITED_CONTENT=$(echo "$VALUES_CONTENT_EDITS" | jq -r ".\"$service\" // empty")
if [ -n "$EDITED_CONTENT" ] && [ "$EDITED_CONTENT" != "null" ]; then
echo " Writing edited values content to override file..."
# Write the edited content to a values override file
VALUES_OVERRIDE_FILE="values-override-${SANITIZED_SERVICE}.yaml"
echo "$EDITED_CONTENT" > "manifests/$VALUES_OVERRIDE_FILE"
echo " Created: manifests/$VALUES_OVERRIDE_FILE"
# Set inline values for ArgoCD - indent the content properly
INDENTED_CONTENT=$(echo "$EDITED_CONTENT" | sed 's/^/ /')
INLINE_VALUES="
values: |
$INDENTED_CONTENT"
fi
# Process external params for disabled Helm dependencies (e.g., external database config)
CUSTOM_HELM_PARAMS=""
SERVICE_EXTERNAL_PARAMS=$(echo "$HELM_EXTERNAL_PARAMS" | jq -r ".\"$service\" // {}")
if [ "$SERVICE_EXTERNAL_PARAMS" != "{}" ] && [ -n "$SERVICE_EXTERNAL_PARAMS" ]; then
echo " Processing external params for disabled dependencies..."
for param_key in $(echo "$SERVICE_EXTERNAL_PARAMS" | jq -r 'keys[]'); do
PARAM_VALUE=$(echo "$SERVICE_EXTERNAL_PARAMS" | jq -r ".\"$param_key\"")
if [ -n "$PARAM_VALUE" ]; then
echo " Setting $param_key = $PARAM_VALUE"
# Use param_key directly - dots are Helm nested path separators, not literal chars
CUSTOM_HELM_PARAMS="${CUSTOM_HELM_PARAMS}
- name: ${param_key}
value: \"${PARAM_VALUE}\""
fi
done
fi
# Extract ingress hostname from GITEA_URL domain
INGRESS_DOMAIN=$(echo "$GITEA_URL" | sed 's|https://gitea\.||' | sed 's|http://gitea\.||')
cat > "manifests/$APP_NAME-application.yaml" <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: $APP_NAME
namespace: argocd
labels:
environment: $NAMESPACE
provisioned-by: backstage
spec:
project: default
source:
repoURL: $HELM_REPO
targetRevision: HEAD
path: $HELM_PATH
helm:
valueFiles:
- $HELM_VALUES$INLINE_VALUES
parameters:
- name: mysql.enabled
value: "$MYSQL_ENABLED"
# Override fullname to keep K8s resource names short (max 63 chars)
- name: fullnameOverride
value: "$SANITIZED_SERVICE"
# Add commonLabels for hades resources
- name: commonLabels.backstage\.io/kubernetes-id
value: "$service"
# Add podLabels for MySQL resources
- name: mysql.podLabels.backstage\.io/kubernetes-id
value: "$service"
# Enable ingress via Helm chart
- name: ingress.enabled
value: "true"
- name: ingress.className
value: "nginx"
- name: ingress.hosts[0].host
value: "${SANITIZED_SERVICE}-${NAMESPACE}.${INGRESS_DOMAIN}"
- name: ingress.hosts[0].paths[0].path
value: "/"
- name: ingress.hosts[0].paths[0].pathType
value: "Prefix"$DEPENDENCY_URL_PARAMS$CUSTOM_HELM_PARAMS
destination:
server: https://kubernetes.default.svc
namespace: $NAMESPACE
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=false
- ServerSideApply=true
EOF
echo " Ingress URL: http://${SANITIZED_SERVICE}-${NAMESPACE}.${INGRESS_DOMAIN}"
else
# No Helm chart - create simple deployment manifest
echo "Creating deployment manifest for service: $service"
cat > "manifests/$SANITIZED_SERVICE-deployment.yaml" <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: $service
namespace: $NAMESPACE
labels:
app: $service
provisioned-by: backstage
managed-by: argocd
spec:
replicas: 1
selector:
matchLabels:
app: $service
template:
metadata:
labels:
app: $service
spec:
containers:
- name: $service
image: nginx:alpine
ports:
- containerPort: 80
name: http
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: $service
namespace: $NAMESPACE
labels:
app: $service
provisioned-by: backstage
managed-by: argocd
spec:
type: ClusterIP
selector:
app: $service
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
EOF
fi
fi
done
# Create README
cat > README.md <<EOF
# Environment: $NAMESPACE
**Owner:** $OWNER
**TTL:** $TTL hours
**Services:** $SERVICES
## Description
This is an automatically provisioned test environment managed by ArgoCD.
## Resources
- Namespace with resource quotas and limits
- Network policies for isolation
- Deployed services: $(echo $SERVICES | tr ',' ' ')
## Management
This environment is managed by ArgoCD. Any changes pushed to this repository will be automatically synced to the cluster.
**Note:** This environment will be cleaned up after $TTL hours.
EOF
# Commit and push
git add .
git commit -m "Initial environment setup for $NAMESPACE by $OWNER"
git push origin main || git push origin master
echo "Git repository created and pushed successfully"
echo "Repository URL: $GITEA_URL/$GITEA_USER/$REPO_NAME"
- inputs:
parameters:
- name: namespace
- name: owner
name: create-argo-app
script:
command:
- sh
env:
# Credentials from External Secrets (GCP Secret Manager)
- name: ARGOCD_SERVER
valueFrom:
secretKeyRef:
name: argocd-credentials
key: server
- name: ARGOCD_USERNAME
valueFrom:
secretKeyRef:
name: argocd-credentials
key: username
- name: ARGOCD_PASSWORD
valueFrom:
secretKeyRef:
name: argocd-credentials
key: password
- name: GITEA_URL
valueFrom:
secretKeyRef:
name: gitea-credentials
key: url
- name: GITEA_USER
valueFrom:
secretKeyRef:
name: gitea-credentials
key: username
image: argoproj/argocd:latest
source: |
#!/bin/sh
set -e
NAMESPACE="{{inputs.parameters.namespace}}"
OWNER="{{inputs.parameters.owner}}"
# 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 label (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/-$//')
REPO_NAME="environment-$NAMESPACE"
echo "Creating ArgoCD application for namespace: $NAMESPACE"
# Login to ArgoCD (TLS enabled for production)
argocd login $ARGOCD_SERVER \
--username $ARGOCD_USERNAME \
--password $ARGOCD_PASSWORD \
--grpc-web
# Create ArgoCD application
argocd app create "$NAMESPACE-environment" \
--repo "$GITEA_URL/$GITEA_USER/$REPO_NAME.git" \
--path manifests \
--dest-server https://kubernetes.default.svc \
--dest-namespace $NAMESPACE \
--sync-policy automated \
--auto-prune \
--self-heal \
--sync-option CreateNamespace=true \
--upsert \
--label "owner=$OWNER_LABEL" \
--label "provisioned-by=backstage" \
--label "managed-by=argocd"
echo "ArgoCD application created successfully"
echo "Auto-sync is enabled - ArgoCD will automatically sync the application"
echo "View in ArgoCD UI: https://$ARGOCD_SERVER/applications/$NAMESPACE-environment"
- inputs:
parameters:
- name: namespace
- name: services
name: register-hades-ips
script:
command:
- bash
image: bitnami/kubectl:latest
source: |
#!/bin/bash
set -e
NAMESPACE="{{inputs.parameters.namespace}}"
SERVICES="{{inputs.parameters.services}}"
# Sanitize namespace
NAMESPACE=$(echo "$NAMESPACE" | tr '[:upper:]' '[:lower:]' | tr ' _' '-' | sed 's/[^a-z0-9-]//g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
# MySQL connection details for Hades
MYSQL_HOST="hades-mysql-db-service.${NAMESPACE}.svc.cluster.local"
MYSQL_PORT="3306"
MYSQL_USER="root"
MYSQL_PWD="local-root-pass"
MYSQL_DB="hades_db"
echo "=== Hades IP Registration ==="
echo "Namespace: $NAMESPACE"
echo "MySQL Host: $MYSQL_HOST"
echo ""
# kubectl is already available in this image
# Find the Hades MySQL pod
MYSQL_POD=$(kubectl get pods -n "$NAMESPACE" -l "app.kubernetes.io/name=mysql" -o jsonpath='{.items[0].metadata.name}' 2>/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"