Deploy Testrigs of mosip using Helmsman #151
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy Testrigs of mosip using Helmsman | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| profile: | |
| description: "Choose deployment profile" | |
| required: true | |
| default: "mosip-platform-java11" | |
| type: choice | |
| options: | |
| - mosip-platform-java11 | |
| - mosip-platform-java21 | |
| - esignet | |
| mode: | |
| description: "Choose Helmsman mode: dry-run or apply" | |
| required: true | |
| default: "dry-run" | |
| type: choice | |
| options: | |
| - dry-run | |
| - apply | |
| domain_name: | |
| description: "Domain name for this environment (e.g. example.xyz.net)" | |
| required: false | |
| type: string | |
| db_port: | |
| description: "PostgreSQL port for MOSIP platform external postgres (e.g. 5433)" | |
| required: false | |
| type: string | |
| esignet_db_port: | |
| description: "PostgreSQL port for eSignet standalone container postgres (e.g. 5432)" | |
| required: false | |
| type: string | |
| env_name: | |
| description: "Environment name (e.g. sandbox, dev, staging)" | |
| required: false | |
| type: string | |
| slack_channel_name: | |
| description: "Slack channel name for alerting (e.g. #mosip-alerts)" | |
| required: false | |
| type: string | |
| slack_webhook_url: | |
| description: "Slack webhook URL for alerting (configure as secrets.SLACK_WEBHOOK_URL in the environment)" | |
| required: false | |
| type: string | |
| push: | |
| paths: | |
| - Helmsman/dsf/**/testrigs-dsf.yaml | |
| permissions: | |
| actions: write | |
| contents: read | |
| jobs: | |
| validate-inputs: | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: ${{ github.ref_name }} | |
| steps: | |
| - name: Validate required variables | |
| run: | | |
| errors=() | |
| DOMAIN="${{ github.event.inputs.domain_name || vars.DOMAIN_NAME }}" | |
| ENV="${{ github.event.inputs.env_name || vars.ENV_NAME }}" | |
| SLACK_CH="${{ github.event.inputs.slack_channel_name || vars.SLACK_CHANNEL_NAME }}" | |
| DB_PORT="${{ github.event.inputs.db_port || vars.DB_PORT }}" | |
| ESIGNET_DB_PORT="${{ github.event.inputs.esignet_db_port || vars.ESIGNET_DB_PORT }}" | |
| [ -z "$DOMAIN" ] && errors+=("domain_name is empty — set vars.DOMAIN_NAME under Environment '${{ github.ref_name }}'") | |
| [ -z "$ENV" ] && errors+=("env_name is empty — set vars.ENV_NAME under Environment '${{ github.ref_name }}'") | |
| [ -z "$SLACK_CH" ] && errors+=("slack_channel_name is empty — set vars.SLACK_CHANNEL_NAME under Environment '${{ github.ref_name }}'") | |
| [ -z "$DB_PORT" ] && errors+=("db_port is empty — set vars.DB_PORT under Environment '${{ github.ref_name }}'") | |
| [ -z "$ESIGNET_DB_PORT" ] && errors+=("esignet_db_port is empty — set vars.ESIGNET_DB_PORT under Environment '${{ github.ref_name }}'") | |
| if [ ${#errors[@]} -gt 0 ]; then | |
| echo "❌ Required variables missing:" | |
| printf ' - %s\n' "${errors[@]}" | |
| echo "Go to: Settings → Environments → ${{ github.ref_name }} → Variables" | |
| exit 1 | |
| fi | |
| echo "✓ domain_name = $DOMAIN" | |
| echo "✓ env_name = $ENV" | |
| echo "✓ slack_channel_name = $SLACK_CH" | |
| echo "✓ db_port = $DB_PORT" | |
| echo "✓ esignet_db_port = $ESIGNET_DB_PORT" | |
| deploy: | |
| runs-on: ubuntu-latest | |
| needs: validate-inputs | |
| environment: | |
| name: ${{ github.ref_name }} | |
| env: | |
| KUBECONFIG: ${{ github.workspace }}/.kube/config | |
| PATH: ${{ github.workspace }}/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin | |
| KUBECTL_PATH: ${{ github.workspace }}/.local/bin/kubectl | |
| domain_name: ${{ github.event.inputs.domain_name || vars.DOMAIN_NAME }} | |
| db_port: ${{ github.event.inputs.db_port || vars.DB_PORT }} | |
| esignet_db_port: ${{ github.event.inputs.esignet_db_port || vars.ESIGNET_DB_PORT }} | |
| env_name: ${{ github.event.inputs.env_name || vars.ENV_NAME }} | |
| slack_channel_name: ${{ github.event.inputs.slack_channel_name || vars.SLACK_CHANNEL_NAME }} | |
| SLACK_WEBHOOK_URL: ${{ github.event.inputs.slack_webhook_url || secrets.SLACK_WEBHOOK_URL }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Persist workflow inputs as environment variables | |
| if: github.event_name == 'workflow_dispatch' | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_INFRA_PAT }} | |
| run: | | |
| REPO="${{ github.repository }}" | |
| ENVIRONMENT="${{ github.ref_name }}" | |
| save_var() { | |
| local name=$1 value=$2 | |
| if [ -z "$value" ]; then echo "⏭ Skipping $name — no input provided, existing value preserved"; return; fi | |
| STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X PATCH \ | |
| -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ | |
| "https://api.github.com/repos/$REPO/environments/$ENVIRONMENT/variables/$name" \ | |
| -d "{\"name\":\"$name\",\"value\":\"$value\"}") | |
| if [ "$STATUS" = "404" ]; then | |
| STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ | |
| -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ | |
| "https://api.github.com/repos/$REPO/environments/$ENVIRONMENT/variables" \ | |
| -d "{\"name\":\"$name\",\"value\":\"$value\"}") | |
| if [ "$STATUS" = "201" ] || [ "$STATUS" = "200" ]; then | |
| echo "✓ Created $name" | |
| else | |
| echo "✗ Failed to create $name (HTTP $STATUS)"; exit 1 | |
| fi | |
| elif [ "$STATUS" = "200" ] || [ "$STATUS" = "204" ]; then | |
| echo "✓ Updated $name" | |
| else | |
| echo "✗ Failed to save $name (HTTP $STATUS)"; exit 1 | |
| fi | |
| } | |
| save_var "DOMAIN_NAME" "${{ github.event.inputs.domain_name }}" | |
| save_var "DB_PORT" "${{ github.event.inputs.db_port }}" | |
| save_var "ESIGNET_DB_PORT" "${{ github.event.inputs.esignet_db_port }}" | |
| save_var "ENV_NAME" "${{ github.event.inputs.env_name }}" | |
| save_var "SLACK_CHANNEL_NAME" "${{ github.event.inputs.slack_channel_name }}" | |
| - name: Setup kubectl and kubeconfig | |
| env: | |
| KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }} | |
| run: | | |
| # Create directories | |
| mkdir -p ${{ github.workspace }}/.local/bin | |
| mkdir -p ${{ github.workspace }}/.kube | |
| # Install kubectl | |
| curl -LO https://dl.k8s.io/release/v1.31.3/bin/linux/amd64/kubectl | |
| chmod +x kubectl | |
| mv ./kubectl ${{ github.workspace }}/.local/bin/kubectl | |
| # Setup kubeconfig | |
| echo "$KUBECONFIG_CONTENT" > ${{ github.workspace }}/.kube/config | |
| chmod 400 ${{ github.workspace }}/.kube/config | |
| # Add kubectl to GitHub PATH for subsequent steps | |
| echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH | |
| # Export environment variables for immediate use | |
| echo "KUBECTL_PATH=${{ github.workspace }}/.local/bin/kubectl" >> $GITHUB_ENV | |
| # Verify installation | |
| ${{ github.workspace }}/.local/bin/kubectl version --client | |
| ${{ github.workspace }}/.local/bin/kubectl config view | |
| - name: Set Default Mode | |
| run: | | |
| if [ -z "${{ github.event.inputs.mode }}" ]; then | |
| echo "HELMSMAN_MODE=apply" >> $GITHUB_ENV | |
| else | |
| echo "HELMSMAN_MODE=${{ github.event.inputs.mode }}" >> $GITHUB_ENV | |
| fi | |
| - name: Set Profile | |
| run: | | |
| if [ -n "${{ github.event.inputs.profile }}" ]; then | |
| PROFILE="${{ github.event.inputs.profile }}" | |
| else | |
| # Auto-detect profile from push trigger — extract profile dir name from changed files | |
| BEFORE="${{ github.event.before }}" | |
| SHA="${{ github.sha }}" | |
| if [[ -z "$BEFORE" || "$BEFORE" == "0000000000000000000000000000000000000000" ]]; then | |
| BEFORE="HEAD~1" | |
| fi | |
| CHANGED_FILES=$(git diff --name-only "$BEFORE" "$SHA" -- 'Helmsman/dsf/' 2>/dev/null || \ | |
| git diff --name-only HEAD~1 HEAD -- 'Helmsman/dsf/' 2>/dev/null || echo "") | |
| PROFILE=$(echo "$CHANGED_FILES" | grep 'testrigs-dsf\.yaml' | head -1 | sed 's|Helmsman/dsf/\([^/]*\)/.*|\1|') | |
| if [[ -z "$PROFILE" ]]; then | |
| echo "Error: could not detect profile from changed DSF files." | |
| exit 1 | |
| fi | |
| fi | |
| echo "PROFILE=$PROFILE" >> "$GITHUB_ENV" | |
| echo "Using profile: $PROFILE" | |
| - name: Setup ufw firewall | |
| run: | | |
| sudo ufw enable | |
| sudo ufw allow ssh | |
| sudo ufw allow 51820/udp | |
| sudo ufw status | |
| - name: Install WireGuard | |
| run: sudo apt-get install -y wireguard | |
| - name: Configure WireGuard | |
| run: | | |
| echo "${{ secrets.CLUSTER_WIREGUARD_WG0 }}" | sudo tee /etc/wireguard/wg0.conf | |
| - name: Start WireGuard | |
| run: | | |
| sudo chmod 600 /etc/wireguard/wg0.conf | |
| sudo chmod 700 /etc/wireguard/ | |
| sudo chmod 644 /lib/systemd/system/wg-quick@.service | |
| sudo systemctl daemon-reload | |
| sudo wg-quick up wg0 | |
| sudo wg show wg0 | |
| - name: Setup Helm | |
| run: | | |
| curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | |
| sudo chmod 700 get_helm.sh | |
| sudo ./get_helm.sh | |
| helm version --client | |
| - name: Install Helmsman | |
| run: | | |
| curl -L https://github.com/Praqma/helmsman/releases/download/v3.17.1/helmsman_3.17.1_linux_amd64.tar.gz -o helmsman.tar.gz | |
| tar xzf helmsman.tar.gz | |
| sudo mv helmsman /usr/local/bin | |
| # Verify tools are available | |
| echo "Tools verification:" | |
| echo "kubectl: $(which kubectl)" | |
| echo "helmsman: $(which helmsman)" | |
| kubectl version --client | |
| helmsman -v | |
| - name: Verify cluster access | |
| run: | | |
| # Verify tools are accessible | |
| echo "Verifying tool availability:" | |
| echo "kubectl path: $(which kubectl)" | |
| echo "KUBECONFIG: $KUBECONFIG" | |
| # Test kubectl functionality | |
| kubectl version --client | |
| kubectl get nodes | |
| kubectl cluster-info | |
| - name: Get MinIO root password (esignet profile) | |
| if: env.PROFILE == 'esignet' | |
| run: | | |
| if kubectl -n minio get secret minio &>/dev/null; then | |
| MINIO_ROOT_PASSWORD=$(kubectl -n minio get secret minio -o jsonpath='{.data.root-password}' | base64 -d) | |
| if [ -n "$MINIO_ROOT_PASSWORD" ]; then | |
| echo "::add-mask::$MINIO_ROOT_PASSWORD" | |
| echo "MINIO_ROOT_PASSWORD=$MINIO_ROOT_PASSWORD" >> $GITHUB_ENV | |
| echo "✅ MINIO_ROOT_PASSWORD retrieved" | |
| else | |
| echo "❌ root-password is empty in minio/minio secret"; exit 1 | |
| fi | |
| else | |
| echo "❌ secret minio not found in minio namespace"; exit 1 | |
| fi | |
| - name: Check if mosip-dsf label is completed | |
| if: env.PROFILE != 'esignet' | |
| run: | | |
| STATUS=$(kubectl get namespace default -o jsonpath='{.metadata.labels.mosip-dsf}') | |
| if [[ "$STATUS" != "completed" ]]; then | |
| echo "❌ MOSIP DSF not completed." | |
| exit 1 | |
| fi | |
| - name: Check if esignet-dsf label is completed | |
| if: env.PROFILE == 'esignet' | |
| run: | | |
| STATUS=$(kubectl get namespace default -o jsonpath='{.metadata.labels.esignet-dsf}' 2>/dev/null || echo "") | |
| if [[ "$STATUS" != "completed" ]]; then | |
| echo "❌ eSignet DSF not completed. Run helmsman_esignet.yml first." | |
| exit 1 | |
| fi | |
| echo "✅ eSignet DSF is completed. Proceeding with testrig deployment." | |
| - name: Show deployment variables | |
| run: | | |
| echo "domain_name = $domain_name" | |
| echo "env_name = $env_name" | |
| echo "slack_channel_name = $slack_channel_name" | |
| - name: Initiate helmsman to apply the DSF configurations | |
| run: | | |
| export HOME="${{ github.workspace }}" | |
| export WORKDIR="$HOME/Helmsman" | |
| # Verify tools are available | |
| echo "Using kubectl: $(which kubectl)" | |
| echo "Using kubeconfig: $KUBECONFIG" | |
| # Run helmsman with the determined mode and profile | |
| echo "Using profile: $PROFILE" | |
| helmsman --${HELMSMAN_MODE} -f $WORKDIR/dsf/${PROFILE}/testrigs-dsf.yaml |