Skip to content

fix: refactor PR Validation workflow to use Replicated actions #142

fix: refactor PR Validation workflow to use Replicated actions

fix: refactor PR Validation workflow to use Replicated actions #142

---
name: WG-Easy PR Validation - build, release, install
on:
pull_request:
branches: [main]
paths:
- 'applications/wg-easy/**'
- '.github/workflows/wg-easy-pr-validation.yaml'
workflow_dispatch:
inputs:
test_mode:
description: 'Run in test mode'
required: false
default: 'true'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
APP_DIR: applications/wg-easy
REPLICATED_API_TOKEN: ${{ secrets.WG_EASY_REPLICATED_API_TOKEN }}
REPLICATED_APP: ${{ vars.WG_EASY_REPLICATED_APP }}
HELM_VERSION: "3.17.3"
KUBECTL_VERSION: "v1.30.0"
jobs:
setup:
runs-on: ubuntu-22.04
outputs:
branch-name: ${{ steps.vars.outputs.branch-name }}
channel-name: ${{ steps.vars.outputs.channel-name }}
customer-name: ${{ steps.vars.outputs.customer-name }}
steps:
- name: Set branch and channel variables
id: vars
run: |
# Branch name preserves original case for resource naming (clusters, customers)
BRANCH_NAME="${{ github.head_ref || github.ref_name }}"
# Channel name is normalized to lowercase with hyphens for Replicated channels
CHANNEL_NAME=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]' | tr '/' '-')
# Customer name includes run number to ensure uniqueness across workflow runs
CUSTOMER_NAME="${CHANNEL_NAME}-${{ github.run_number }}"
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
echo "channel-name=$CHANNEL_NAME" >> $GITHUB_OUTPUT
echo "customer-name=$CUSTOMER_NAME" >> $GITHUB_OUTPUT
echo "Branch: $BRANCH_NAME, Channel: $CHANNEL_NAME, Customer: $CUSTOMER_NAME"
validate-charts:
runs-on: ubuntu-22.04
needs: setup
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Validate charts
uses: ./.github/actions/chart-validate
with:
app-dir: ${{ env.APP_DIR }}
helm-version: ${{ env.HELM_VERSION }}
- name: Validate Taskfile syntax
run: task --list-all
working-directory: ${{ env.APP_DIR }}
build-and-package:
runs-on: ubuntu-22.04
needs: [setup, validate-charts]
outputs:
release-path: ${{ steps.package.outputs.release-path }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Package charts
id: package
uses: ./.github/actions/chart-package
with:
app-dir: ${{ env.APP_DIR }}
helm-version: ${{ env.HELM_VERSION }}
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: wg-easy-release-${{ github.run_number }}
path: ${{ steps.package.outputs.release-path }}
retention-days: 7
create-resources:
runs-on: ubuntu-22.04
needs: [setup, build-and-package]
outputs:
channel-slug: ${{ steps.set-outputs.outputs.channel-slug }}
release-sequence: ${{ steps.set-outputs.outputs.release-sequence }}
customer-id: ${{ steps.set-outputs.outputs.customer-id }}
license-id: ${{ steps.set-outputs.outputs.license-id }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download release artifacts
uses: actions/download-artifact@v4
with:
name: wg-easy-release-${{ github.run_number }}
path: ${{ env.APP_DIR }}/release
- name: Check if channel exists
id: check-channel
run: |
set -e
echo "Checking for existing channel: ${{ needs.setup.outputs.channel-name }}"
# Get channels with error handling
RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \
"https://api.replicated.com/vendor/v3/apps/${{ env.REPLICATED_APP }}/channels")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" != "200" ]; then
echo "API request failed with HTTP $HTTP_CODE"
echo "Response: $BODY"
echo "channel-exists=false" >> $GITHUB_OUTPUT
exit 0
fi
# Parse JSON response safely
CHANNEL_ID=$(echo "$BODY" | jq -r --arg name "${{ needs.setup.outputs.channel-name }}" \
'if .channels then .channels[] | select(.name == $name) | .id else empty end' 2>/dev/null | head -1)
if [ -n "$CHANNEL_ID" ] && [ "$CHANNEL_ID" != "null" ]; then
echo "Found existing channel: $CHANNEL_ID"
echo "channel-exists=true" >> $GITHUB_OUTPUT
echo "channel-id=$CHANNEL_ID" >> $GITHUB_OUTPUT
echo "channel-slug=${{ needs.setup.outputs.channel-name }}" >> $GITHUB_OUTPUT
else
echo "Channel does not exist"
echo "channel-exists=false" >> $GITHUB_OUTPUT
fi
- name: Create Replicated release
id: release
uses: replicatedhq/replicated-actions/[email protected]
with:
app-slug: ${{ env.REPLICATED_APP }}
api-token: ${{ env.REPLICATED_API_TOKEN }}
yaml-dir: ${{ env.APP_DIR }}/release
promote-channel: ${{ needs.setup.outputs.channel-name }}
- name: Check if customer exists
id: check-customer
run: |
set -e
CUSTOMER_NAME="${{ needs.setup.outputs.customer-name }}"
echo "Checking for existing customer: $CUSTOMER_NAME"
# Get customers with error handling
RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \
"https://api.replicated.com/vendor/v3/customers")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" != "200" ]; then
echo "API request failed with HTTP $HTTP_CODE"
echo "Response: $BODY"
echo "customer-exists=false" >> $GITHUB_OUTPUT
exit 0
fi
# Parse JSON response safely - select most recent customer by creation date
CUSTOMER_DATA=$(echo "$BODY" | jq -r --arg name "$CUSTOMER_NAME" \
'if .customers then .customers[] | select(.name == $name) | {id: .id, created: .createdAt} else empty end' 2>/dev/null \
| jq -s 'sort_by(.created) | reverse | .[0] // empty' 2>/dev/null)
CUSTOMER_ID=$(echo "$CUSTOMER_DATA" | jq -r '.id // empty' 2>/dev/null)
if [ -n "$CUSTOMER_DATA" ] && [ "$CUSTOMER_DATA" != "null" ] && [ "$CUSTOMER_DATA" != "{}" ]; then
CUSTOMER_COUNT=$(echo "$BODY" | jq -r --arg name "$CUSTOMER_NAME" \
'if .customers then [.customers[] | select(.name == $name)] | length else 0 end' 2>/dev/null)
echo "Found $CUSTOMER_COUNT customer(s) with name '$CUSTOMER_NAME', using most recent: $CUSTOMER_ID"
fi
if [ -n "$CUSTOMER_ID" ] && [ "$CUSTOMER_ID" != "null" ]; then
echo "Found existing customer: $CUSTOMER_ID"
echo "customer-exists=true" >> $GITHUB_OUTPUT
echo "customer-id=$CUSTOMER_ID" >> $GITHUB_OUTPUT
# Get license ID for existing customer with error handling
LICENSE_RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \
"https://api.replicated.com/vendor/v3/customer/$CUSTOMER_ID")
LICENSE_HTTP_CODE=$(echo "$LICENSE_RESPONSE" | tail -n1)
LICENSE_BODY=$(echo "$LICENSE_RESPONSE" | sed '$d')
if [ "$LICENSE_HTTP_CODE" = "200" ]; then
LICENSE_ID=$(echo "$LICENSE_BODY" | jq -r '.customer.installationId // empty' 2>/dev/null)
echo "license-id=$LICENSE_ID" >> $GITHUB_OUTPUT
else
echo "Failed to get license ID for customer $CUSTOMER_ID"
echo "customer-exists=false" >> $GITHUB_OUTPUT
fi
else
echo "Customer does not exist"
echo "customer-exists=false" >> $GITHUB_OUTPUT
fi
- name: Create customer
id: create-customer
if: steps.check-customer.outputs.customer-exists == 'false'
uses: replicatedhq/replicated-actions/[email protected]
with:
app-slug: ${{ env.REPLICATED_APP }}
api-token: ${{ env.REPLICATED_API_TOKEN }}
customer-name: ${{ needs.setup.outputs.customer-name }}
channel-slug: ${{ steps.check-channel.outputs.channel-exists == 'true' && steps.check-channel.outputs.channel-slug || steps.release.outputs.channel-slug }}
license-type: dev
- name: Set consolidated outputs
id: set-outputs
run: |
# Set channel outputs
if [ "${{ steps.check-channel.outputs.channel-exists }}" == "true" ]; then
echo "channel-slug=${{ steps.check-channel.outputs.channel-slug }}" >> $GITHUB_OUTPUT
else
echo "channel-slug=${{ steps.release.outputs.channel-slug }}" >> $GITHUB_OUTPUT
fi
echo "release-sequence=${{ steps.release.outputs.release-sequence }}" >> $GITHUB_OUTPUT
# Set customer outputs
if [ "${{ steps.check-customer.outputs.customer-exists }}" == "true" ]; then
echo "customer-id=${{ steps.check-customer.outputs.customer-id }}" >> $GITHUB_OUTPUT
echo "license-id=${{ steps.check-customer.outputs.license-id }}" >> $GITHUB_OUTPUT
else
echo "customer-id=${{ steps.create-customer.outputs.customer-id }}" >> $GITHUB_OUTPUT
echo "license-id=${{ steps.create-customer.outputs.license-id }}" >> $GITHUB_OUTPUT
fi
test-deployment:
runs-on: ubuntu-22.04
needs: [setup, create-resources]
strategy:
matrix:
include:
# k3s single-node configurations (latest patch versions)
- k8s-version: "v1.31.10"
distribution: "k3s"
nodes: 1
instance-type: "r1.small"
timeout-minutes: 15
- k8s-version: "v1.32.6"
distribution: "k3s"
nodes: 1
instance-type: "r1.small"
timeout-minutes: 15
# k3s multi-node configurations
- k8s-version: "v1.32.6"
distribution: "k3s"
nodes: 3
instance-type: "r1.medium"
timeout-minutes: 20
# kind configurations (maximum 1 node supported, distribution-specific patch versions)
- k8s-version: "v1.31.9"
distribution: "kind"
nodes: 1
instance-type: "r1.small"
timeout-minutes: 20
- k8s-version: "v1.32.5"
distribution: "kind"
nodes: 1
instance-type: "r1.small"
timeout-minutes: 20
# EKS configurations (major.minor versions only)
- k8s-version: "v1.31"
distribution: "eks"
nodes: 2
instance-type: "c5.large"
timeout-minutes: 30
- k8s-version: "v1.32"
distribution: "eks"
nodes: 2
instance-type: "c5.large"
timeout-minutes: 30
exclude: []
fail-fast: false
max-parallel: 4
outputs:
cluster-id: ${{ steps.set-cluster-outputs.outputs.cluster-id }}
steps:
- name: Set concurrency group
run: |
echo "CONCURRENCY_GROUP=cluster-${{ needs.setup.outputs.channel-name }}-${{ matrix.k8s-version }}-${{ matrix.distribution }}" >> $GITHUB_ENV
echo "Starting matrix job: ${{ matrix.k8s-version }}-${{ matrix.distribution }}-${{ matrix.nodes }}nodes"
- name: Checkout code
uses: actions/checkout@v4
- name: Setup tools
uses: ./.github/actions/setup-tools
with:
helm-version: ${{ env.HELM_VERSION }}
install-helmfile: 'true'
- name: Configure distribution-specific settings
id: dist-config
run: |
case "${{ matrix.distribution }}" in
"k3s")
echo "cluster-disk-size=50" >> $GITHUB_OUTPUT
echo "cluster-ttl=4h" >> $GITHUB_OUTPUT
echo "resource-priority=high" >> $GITHUB_OUTPUT
;;
"kind")
echo "cluster-disk-size=50" >> $GITHUB_OUTPUT
echo "cluster-ttl=4h" >> $GITHUB_OUTPUT
echo "resource-priority=medium" >> $GITHUB_OUTPUT
;;
"eks")
echo "cluster-disk-size=50" >> $GITHUB_OUTPUT
echo "cluster-ttl=6h" >> $GITHUB_OUTPUT
echo "resource-priority=low" >> $GITHUB_OUTPUT
;;
*)
echo "cluster-disk-size=50" >> $GITHUB_OUTPUT
echo "cluster-ttl=4h" >> $GITHUB_OUTPUT
echo "resource-priority=medium" >> $GITHUB_OUTPUT
;;
esac
# Set resource limits based on node count and instance type
case "${{ matrix.nodes }}" in
"1")
echo "max-parallel-jobs=3" >> $GITHUB_OUTPUT
;;
"2")
echo "max-parallel-jobs=2" >> $GITHUB_OUTPUT
;;
"3")
echo "max-parallel-jobs=1" >> $GITHUB_OUTPUT
;;
*)
echo "max-parallel-jobs=2" >> $GITHUB_OUTPUT
;;
esac
echo "Distribution: ${{ matrix.distribution }}, Nodes: ${{ matrix.nodes }}, Instance: ${{ matrix.instance-type }}"
echo "Resource Priority: $(echo '${{ steps.dist-config.outputs.resource-priority }}' || echo 'medium')"
- name: Check if cluster exists
id: check-cluster
run: |
set -e
CLUSTER_NAME="${{ needs.setup.outputs.channel-name }}-${{ matrix.k8s-version }}-${{ matrix.distribution }}"
echo "Checking for existing cluster: $CLUSTER_NAME"
# Get clusters with error handling
RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \
"https://api.replicated.com/vendor/v3/clusters")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" != "200" ]; then
echo "API request failed with HTTP $HTTP_CODE"
echo "Response: $BODY"
echo "cluster-exists=false" >> $GITHUB_OUTPUT
exit 0
fi
# Parse JSON response safely - check cluster status and readiness
CLUSTER_DATA=$(echo "$BODY" | jq -r --arg name "$CLUSTER_NAME" \
'if .clusters then .clusters[] | select(.name == $name and .status != "terminated") | {id: .id, status: .status} else empty end' 2>/dev/null | head -1)
CLUSTER_ID=$(echo "$CLUSTER_DATA" | jq -r '.id // empty' 2>/dev/null)
CLUSTER_STATUS=$(echo "$CLUSTER_DATA" | jq -r '.status // empty' 2>/dev/null)
if [ -n "$CLUSTER_ID" ] && [ "$CLUSTER_ID" != "null" ]; then
echo "Found existing cluster: $CLUSTER_ID with status: $CLUSTER_STATUS"
# Only consider cluster as existing if it's ready, otherwise treat as needs creation
if [ "$CLUSTER_STATUS" = "running" ]; then
echo "Cluster is running, attempting to get kubeconfig"
echo "cluster-exists=true" >> $GITHUB_OUTPUT
echo "cluster-id=$CLUSTER_ID" >> $GITHUB_OUTPUT
# Wait for kubeconfig to be available and functional
echo "Waiting for kubeconfig to be ready..."
RETRY_COUNT=0
MAX_RETRIES=12 # 12 * 30s = 6 minutes max wait
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
# Try to get kubeconfig
KUBECONFIG_RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \
"https://api.replicated.com/vendor/v3/cluster/$CLUSTER_ID/kubeconfig")
KUBECONFIG_HTTP_CODE=$(echo "$KUBECONFIG_RESPONSE" | tail -n1)
KUBECONFIG_BODY=$(echo "$KUBECONFIG_RESPONSE" | sed '$d')
if [ "$KUBECONFIG_HTTP_CODE" = "200" ]; then
# Extract and decode the kubeconfig from JSON response
KUBECONFIG_CONTENT=$(echo "$KUBECONFIG_BODY" | jq -r '.kubeconfig // empty' 2>/dev/null)
if [ -n "$KUBECONFIG_CONTENT" ] && [ "$KUBECONFIG_CONTENT" != "null" ] && [ "$KUBECONFIG_CONTENT" != "empty" ]; then
# Decode base64 kubeconfig content and write to file
echo "$KUBECONFIG_CONTENT" | base64 -d > /tmp/kubeconfig 2>/dev/null || echo "$KUBECONFIG_CONTENT" > /tmp/kubeconfig
if [ -s /tmp/kubeconfig ]; then
# Test actual connectivity to the cluster API server
if timeout 30s kubectl --kubeconfig=/tmp/kubeconfig cluster-info &>/dev/null; then
echo "KUBECONFIG=/tmp/kubeconfig" >> $GITHUB_ENV
echo "Successfully validated kubeconfig and cluster connectivity"
break
else
echo "Kubeconfig file exists but cluster API is not ready yet (attempt $((RETRY_COUNT+1))/$MAX_RETRIES)"
fi
else
echo "Failed to write kubeconfig to file (attempt $((RETRY_COUNT+1))/$MAX_RETRIES)"
fi
else
echo "Kubeconfig content is empty or null (attempt $((RETRY_COUNT+1))/$MAX_RETRIES)"
fi
else
echo "Failed to get kubeconfig HTTP $KUBECONFIG_HTTP_CODE (attempt $((RETRY_COUNT+1))/$MAX_RETRIES)"
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
echo "Waiting 30 seconds before retry..."
sleep 30
fi
done
# If we exhausted retries without success, treat cluster as not ready
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "Cluster exists but kubeconfig is not ready after $((MAX_RETRIES * 30)) seconds"
echo "Will create a new cluster instead"
echo "cluster-exists=false" >> $GITHUB_OUTPUT
fi
else
echo "Cluster exists but status is '$CLUSTER_STATUS' (not running)"
echo "Will create a new cluster instead"
echo "cluster-exists=false" >> $GITHUB_OUTPUT
fi
else
echo "Cluster does not exist"
echo "cluster-exists=false" >> $GITHUB_OUTPUT
fi
- name: Create cluster
id: create-cluster
if: steps.check-cluster.outputs.cluster-exists == 'false'
uses: replicatedhq/replicated-actions/[email protected]
with:
api-token: ${{ env.REPLICATED_API_TOKEN }}
kubernetes-distribution: ${{ matrix.distribution }}
kubernetes-version: ${{ matrix.k8s-version }}
cluster-name: ${{ needs.setup.outputs.channel-name }}-${{ matrix.k8s-version }}-${{ matrix.distribution }}
ttl: ${{ steps.dist-config.outputs.cluster-ttl }}
nodes: ${{ matrix.nodes }}
instance-type: ${{ matrix.instance-type }}
disk: ${{ steps.dist-config.outputs.cluster-disk-size }}
export-kubeconfig: 'true'
- name: Set cluster outputs
id: set-cluster-outputs
run: |
if [ "${{ steps.check-cluster.outputs.cluster-exists }}" == "true" ]; then
echo "cluster-id=${{ steps.check-cluster.outputs.cluster-id }}" >> $GITHUB_OUTPUT
else
echo "cluster-id=${{ steps.create-cluster.outputs.cluster-id }}" >> $GITHUB_OUTPUT
fi
- name: Setup cluster ports
working-directory: ${{ env.APP_DIR }}
run: |
task cluster-ports-expose CLUSTER_NAME="${{ needs.setup.outputs.channel-name }}-${{ matrix.k8s-version }}-${{ matrix.distribution }}"
- name: Validate cluster readiness
run: |
echo "Validating cluster readiness for ${{ matrix.distribution }} ${{ matrix.k8s-version }}"
# Ensure kubeconfig is available
if [ ! -f "$KUBECONFIG" ] || [ ! -s "$KUBECONFIG" ]; then
echo "ERROR: kubeconfig file not found or empty at: $KUBECONFIG"
echo "This indicates a problem with cluster creation or kubeconfig export"
exit 1
fi
echo "Found kubeconfig at: $KUBECONFIG"
# Test kubectl client is working
if ! kubectl version --client &>/dev/null; then
echo "ERROR: kubectl client is not working properly"
exit 1
fi
echo "kubectl client is functional"
# Wait for cluster API server to be accessible with retries
echo "Testing cluster API connectivity..."
RETRY_COUNT=0
MAX_API_RETRIES=20 # 20 * 15s = 5 minutes max wait for API
while [ $RETRY_COUNT -lt $MAX_API_RETRIES ]; do
if timeout 30s kubectl cluster-info &>/dev/null; then
echo "✅ Cluster API server is accessible"
break
else
echo "⏳ Cluster API not ready yet (attempt $((RETRY_COUNT+1))/$MAX_API_RETRIES)"
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_API_RETRIES ]; then
echo "Waiting 15 seconds before retry..."
sleep 15
fi
fi
done
if [ $RETRY_COUNT -eq $MAX_API_RETRIES ]; then
echo "ERROR: Cluster API server not accessible after $((MAX_API_RETRIES * 15)) seconds"
echo "Cluster info debug:"
kubectl cluster-info || true
exit 1
fi
# Wait for cluster nodes to be ready
echo "Waiting for cluster nodes to be ready..."
if ! kubectl wait --for=condition=Ready nodes --all --timeout=300s; then
echo "ERROR: Cluster nodes did not become ready within 5 minutes"
echo "Node status:"
kubectl get nodes -o wide || true
exit 1
fi
echo "✅ All cluster nodes are ready"
# Validate cluster nodes
echo "Cluster nodes:"
kubectl get nodes -o wide
echo "Cluster info:"
kubectl cluster-info
- name: Deploy application
working-directory: ${{ env.APP_DIR }}
run: |
task customer-helm-install \
CUSTOMER_NAME="${{ needs.setup.outputs.customer-name }}" \
CLUSTER_NAME="${{ needs.setup.outputs.channel-name }}-${{ matrix.k8s-version }}-${{ matrix.distribution }}" \
CHANNEL_SLUG="${{ needs.create-resources.outputs.channel-slug }}" \
REPLICATED_LICENSE_ID="${{ needs.create-resources.outputs.license-id }}"
timeout-minutes: ${{ matrix.timeout-minutes }}
- name: Run tests
working-directory: ${{ env.APP_DIR }}
run: task test
timeout-minutes: 10
- name: Run distribution-specific tests
run: |
echo "Running ${{ matrix.distribution }}-specific tests..."
# Test node configuration based on matrix
EXPECTED_NODES=${{ matrix.nodes }}
ACTUAL_NODES=$(kubectl get nodes --no-headers | wc -l)
if [ "$ACTUAL_NODES" -eq "$EXPECTED_NODES" ]; then
echo "✅ Node count validation passed: $ACTUAL_NODES/$EXPECTED_NODES"
else
echo "❌ Node count validation failed: $ACTUAL_NODES/$EXPECTED_NODES"
exit 1
fi
# Distribution-specific storage tests
case "${{ matrix.distribution }}" in
"k3s")
echo "Testing k3s local-path storage..."
kubectl get storageclass local-path -o yaml | grep provisioner | grep rancher.io/local-path
;;
"kind")
echo "Testing kind standard storage..."
kubectl get storageclass standard -o yaml | grep provisioner | grep rancher.io/local-path
;;
"eks")
echo "Testing EKS GP2 storage..."
kubectl get storageclass gp2 -o yaml | grep provisioner | grep ebs.csi.aws.com || echo "EKS storage validation skipped"
;;
esac
# Test cluster resources
echo "Cluster resource utilization:"
kubectl top nodes --no-headers 2>/dev/null || echo "Metrics not available"
echo "Pod distribution across nodes:"
kubectl get pods -A -o wide | awk '{print $7}' | sort | uniq -c
# Performance monitoring
echo "=== Performance Metrics ==="
echo "Test Environment: ${{ matrix.distribution }} ${{ matrix.k8s-version }} (${{ matrix.nodes }} nodes)"
echo "Instance Type: ${{ matrix.instance-type }}"
echo "Priority: ${{ steps.dist-config.outputs.resource-priority }}"
echo "Deployment Timeout: ${{ matrix.timeout-minutes }} minutes"
# Resource consumption validation
echo "=== Resource Validation ==="
kubectl describe nodes | grep -E "(Name:|Allocatable:|Allocated resources:)" | head -20
# Collect performance timings
echo "=== Test Completion Summary ==="
echo "Matrix Job: ${{ matrix.k8s-version }}-${{ matrix.distribution }}-${{ matrix.nodes }}nodes"
echo "Started: $(date -u)"
echo "Status: Complete"
- name: Upload debug logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: debug-logs-${{ github.run_number }}-${{ matrix.k8s-version }}-${{ matrix.distribution }}
path: |
/tmp/*.log
~/.replicated/