GCP Image Test #21
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: GCP cloud-image-tests | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version_major: | |
| description: 'AlmaLinux major version' | |
| required: true | |
| default: '10' | |
| type: choice | |
| options: | |
| - 10-kitten | |
| - 10 | |
| - 9 | |
| - 8 | |
| arch: | |
| description: 'Architecture to test' | |
| required: true | |
| default: 'ALL' | |
| type: choice | |
| options: | |
| - ALL | |
| - x86_64 | |
| - aarch64 | |
| image_override: | |
| description: 'Image to test, overrides version_major to test a direct image instead. Architecture must be set properly for the image being passed. This must be a full path to a GCP image, for example, projects/almalinux-dev-images-469421/global/images/almalinux-9-v20230920' | |
| required: false | |
| default: '' | |
| # notify_mattermost: | |
| # description: "Send notification to Mattermost" | |
| # required: true | |
| # type: boolean | |
| # default: false | |
| jobs: | |
| init-data: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| image_path: ${{ steps.determine_image.outputs.image_path }} | |
| steps: | |
| - name: Determine image to test | |
| id: determine_image | |
| run: | | |
| if [ -n "${{ inputs.image_override }}" ]; then | |
| echo "Using image override: ${{ inputs.image_override }}" | |
| image_path="${{ inputs.image_override }}" | |
| elif [ "${{ inputs.arch == 'ALL' }}" ]; then | |
| echo "Using version major: ${{ inputs.version_major }}" | |
| echo "Using all architectures" | |
| image_path="projects/almalinux-dev-images-469421/global/images/family/almalinux-${{ inputs.version_major }}" | |
| else | |
| echo "Using version major: ${{ inputs.version_major }}" | |
| echo "Using arch: ${{ inputs.arch }}" | |
| image_path="projects/almalinux-dev-images-469421/global/images/family/almalinux-${{ inputs.version_major }}" | |
| fi | |
| echo "Determined image path: ${image_path}" | |
| echo "image_path=${image_path}" >> $GITHUB_OUTPUT | |
| - name: Build randomized per-arch shape lists | |
| id: rand-shapes | |
| shell: bash | |
| run: | | |
| python3 - <<'PY' > /tmp/generate_shapes.py | |
| import json, random, heapq, sys | |
| from collections import deque, defaultdict | |
| # change these lists to match the shapes you're using | |
| shapes_by_arch = { | |
| "x86_64": [ | |
| "n4-standard-2","n4-standard-80", | |
| "n2-standard-2","n2-standard-128", | |
| "n2d-standard-2","n2d-standard-224", | |
| "n1-standard-1","n1-standard-96", | |
| "c4-standard-2","c4-standard-288","c4-standard-288-metal","c4-standard-4-lssd","c4-standard-288-lssd", | |
| "c4d-standard-2","c4d-standard-384","c4d-standard-384-metal","c4d-standard-8-lssd","c4d-standard-384-lssd", | |
| "c3-standard-4","c3-standard-176","c3-standard-192-metal","c3-standard-4-lssd","c3-standard-176-lssd", | |
| "c3d-standard-4","c3d-standard-360","c3d-standard-8-lssd","c3d-standard-360-lssd", | |
| "e2-standard-2","e2-standard-32","e2-medium", | |
| "t2d-standard-1","t2d-standard-60" | |
| ], | |
| "aarch64": [ | |
| "c4a-standard-1","c4a-standard-72","c4a-standard-4-lssd","c4a-standard-72-lssd", | |
| "t2a-standard-1","t2a-standard-48" | |
| ] | |
| } | |
| # parameters: k-distance -> no repeat within next k elements (k=2 as you requested) | |
| K = 3 | |
| def family_of(shape): | |
| return shape.split('-', 1)[0] | |
| # greedy scheduler with cooldown | |
| def randomize_list(shapes, k=K, max_attempts=200): | |
| if not shapes: | |
| return [] | |
| # group by family | |
| buckets = defaultdict(list) | |
| for s in shapes: | |
| buckets[family_of(s)].append(s) | |
| families = list(buckets.keys()) | |
| def attempt(): | |
| # create max-heap by remaining count | |
| heap = [(-len(buckets[f]), f) for f in families if buckets[f]] | |
| heapq.heapify(heap) | |
| # local copy of buckets' lists | |
| local = {f: list(buckets[f]) for f in buckets} | |
| result = [] | |
| cooldown = deque() # stores families in the last k chosen | |
| while heap: | |
| popped = [] | |
| chosen = None | |
| while heap: | |
| cnt, fam = heapq.heappop(heap) | |
| cnt = -cnt | |
| if fam in cooldown: | |
| popped.append((-cnt, fam)) | |
| continue | |
| # choose this family | |
| lst = local[fam] | |
| # pick a random shape from the family | |
| s = random.choice(lst) | |
| lst.remove(s) | |
| result.append(s) | |
| # update counts and reinsert | |
| if len(lst) > 0: | |
| heapq.heappush(heap, (-len(lst), fam)) | |
| # push back popped families | |
| for item in popped: | |
| heapq.heappush(heap, item) | |
| # update cooldown | |
| cooldown.append(fam) | |
| if len(cooldown) > k: | |
| cooldown.popleft() | |
| chosen = True | |
| break | |
| if not chosen: | |
| # dead end | |
| return None | |
| return result | |
| for _ in range(max_attempts): | |
| out = attempt() | |
| if out is not None: | |
| return out | |
| return None | |
| out = {} | |
| for arch, shapes in shapes_by_arch.items(): | |
| randomized = randomize_list(shapes, K) | |
| if randomized is None: | |
| print(f"ERROR: failed to generate sequence for {arch}", file=sys.stderr) | |
| sys.exit(1) | |
| out[arch] = randomized | |
| print(json.dumps(out)) | |
| PY | |
| # run generator and capture result | |
| result=$(python3 /tmp/generate_shapes.py) | |
| echo "randomized=$result" >&2 | |
| # extract per-arch arrays and export to GITHUB_OUTPUT | |
| # set outputs like matrix_shapes_x86_64 and matrix_shapes_aarch64 | |
| x86=$(python3 - <<'PY' | |
| import json,sys | |
| d=json.loads(sys.stdin.read()) | |
| print(json.dumps(d.get('x86_64', []))) | |
| PY | |
| <<<"$result") | |
| aarch=$(python3 - <<'PY' | |
| import json,sys | |
| d=json.loads(sys.stdin.read()) | |
| print(json.dumps(d.get('aarch64', []))) | |
| PY | |
| <<<"$result") | |
| echo "matrix_shapes_x86_64=$x86" >> $GITHUB_OUTPUT | |
| echo "matrix_shapes_aarch64=$aarch" >> $GITHUB_OUTPUT | |
| # this initial test does the generic suite of tests not assigned to any specific shape - letting the test system | |
| # choose its own shapes and sizes. This is run first to catch any major issues before running the per-shape tests | |
| # which take a long time and use a lot of resources. Think of this as a smoke test to catch major issues early. | |
| test-gcp-initialtest: | |
| name: AlmaLinux ${{ inputs.image_override || format('{0} {1}', inputs.version_major, matrix.arch) }} Generic Full Run | |
| needs: init-data | |
| permissions: | |
| id-token: write | |
| contents: read | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # this sets the arch matrix based on the input | |
| # if input is ALL, then set to both x86_64 and aarch64 | |
| # otherwise set to the selected arch | |
| arch: ${{ fromJSON(inputs.arch == 'ALL' && '["x86_64","aarch64"]' || format('["{0}"]', inputs.arch)) }} | |
| steps: | |
| # we don't need the checked out files, but this is required for the google auth action to work | |
| - uses: 'actions/checkout@v5' | |
| - id: 'google-auth-image-testing' | |
| uses: 'google-github-actions/auth@v2' | |
| with: | |
| workload_identity_provider: 'projects/527193872801/locations/global/workloadIdentityPools/github-actions/providers/github' | |
| service_account: 'github-actions-image-testing@almalinux-image-testing-469421.iam.gserviceaccount.com' | |
| - name: 'Set up Google Cloud SDK' | |
| uses: 'google-github-actions/setup-gcloud@v3.0.0' | |
| - name: 'Run Google cloud-image-testing tests which are hard-coded to specific shapes' | |
| shell: bash | |
| run: | | |
| docker run \ | |
| -v ${{ env.GOOGLE_GHA_CREDS_PATH }}:/creds/auth.json \ | |
| -e GOOGLE_APPLICATION_CREDENTIALS=/creds/auth.json \ | |
| gcr.io/compute-image-tools/cloud-image-tests:latest \ | |
| -project almalinux-image-testing-469421 \ | |
| -parallel_stagger 10s \ | |
| -parallel_count 20 \ | |
| -filter '^(cvm|livemigrate|suspendresume|loadbalancer|guestagent|hostnamevalidation|imageboot|licensevalidation|network|security|hotattach|packagevalidation|ssh|metadata|disk|lssd|vmspec)$' \ | |
| -images '${{ needs.init-data.outputs.image_path }}${{ inputs.image_override == '' && matrix.arch == 'aarch64' && '-arm64' || ''}}' | |
| test-gcp-pershape-x86_64: | |
| name: AlmaLinux ${{ inputs.image_override || format('{0}', inputs.version_major) }} x86_64 ${{ matrix.shape }} | |
| needs: [init-data, test-gcp-initialtest] | |
| permissions: | |
| id-token: write | |
| contents: read | |
| runs-on: ubuntu-24.04 | |
| if: inputs.arch == 'ALL' || inputs.arch == 'x86_64' | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 2 | |
| matrix: | |
| shape: ${{ fromJSON(needs.init-data.outputs.matrix_shapes_x86_64) }} | |
| steps: | |
| # we don't need the checked out files, but this is required for the google auth action to work | |
| - uses: 'actions/checkout@v5' | |
| - id: 'google-auth-image-testing' | |
| uses: 'google-github-actions/auth@v2' | |
| with: | |
| workload_identity_provider: 'projects/527193872801/locations/global/workloadIdentityPools/github-actions/providers/github' | |
| service_account: 'github-actions-image-testing@almalinux-image-testing-469421.iam.gserviceaccount.com' | |
| - name: 'Set up Google Cloud SDK' | |
| uses: 'google-github-actions/setup-gcloud@v3.0.0' | |
| - name: 'Run Google cloud-image-testing tests on ${{ matrix.shape }}' | |
| shell: bash | |
| run: | | |
| docker run \ | |
| -v ${{ env.GOOGLE_GHA_CREDS_PATH }}:/creds/auth.json \ | |
| -e GOOGLE_APPLICATION_CREDENTIALS=/creds/auth.json \ | |
| gcr.io/compute-image-tools/cloud-image-tests:latest \ | |
| -project almalinux-image-testing-469421 \ | |
| -parallel_stagger 10s \ | |
| -parallel_count 20 \ | |
| -x86_shape ${{ matrix.shape }} \ | |
| -filter '^(cvm|livemigrate|suspendresume|loadbalancer|guestagent|hostnamevalidation|imageboot|licensevalidation|network|security|hotattach|packagevalidation|ssh|metadata)$' \ | |
| -images '${{ needs.init-data.outputs.image_path || inputs.image_override }}${{ inputs.image_override == '' && matrix.arch == 'aarch64' && '-arm64' || ''}}' | |
| test-gcp-pershape-aarch64: | |
| name: AlmaLinux ${{ inputs.image_override || format('{0}', inputs.version_major) }} aarch64 ${{ matrix.shape }} | |
| needs: [init-data, test-gcp-initialtest] | |
| permissions: | |
| id-token: write | |
| contents: read | |
| runs-on: ubuntu-24.04 | |
| if: inputs.arch == 'ALL' || inputs.arch == 'aarch64' | |
| strategy: | |
| fail-fast: false | |
| # the arm sizes are smaller so we can do more concurrently | |
| max-parallel: 3 | |
| matrix: | |
| shape: ${{ fromJSON(needs.init-data.outputs.matrix_shapes_aarch64) }} | |
| steps: | |
| # we don't need the checked out files, but this is required for the google auth action to work | |
| - uses: 'actions/checkout@v5' | |
| - id: 'google-auth-image-testing' | |
| uses: 'google-github-actions/auth@v2' | |
| with: | |
| workload_identity_provider: 'projects/527193872801/locations/global/workloadIdentityPools/github-actions/providers/github' | |
| service_account: 'github-actions-image-testing@almalinux-image-testing-469421.iam.gserviceaccount.com' | |
| - name: 'Set up Google Cloud SDK' | |
| uses: 'google-github-actions/setup-gcloud@v3.0.0' | |
| - name: 'Run Google cloud-image-testing tests on ${{ matrix.shape }}' | |
| shell: bash | |
| run: | | |
| docker run \ | |
| -v ${{ env.GOOGLE_GHA_CREDS_PATH }}:/creds/auth.json \ | |
| -e GOOGLE_APPLICATION_CREDENTIALS=/creds/auth.json \ | |
| gcr.io/compute-image-tools/cloud-image-tests:latest \ | |
| -project almalinux-image-testing-469421 \ | |
| -parallel_stagger 10s \ | |
| -parallel_count 20 \ | |
| -arm64_shape ${{ matrix.shape }} \ | |
| -filter '^(cvm|livemigrate|suspendresume|loadbalancer|guestagent|hostnamevalidation|imageboot|licensevalidation|network|security|hotattach|packagevalidation|ssh|metadata)$' \ | |
| -images '${{ needs.init-data.outputs.image_path || inputs.image_override }}${{ inputs.image_override == '' && matrix.arch == 'aarch64' && '-arm64' || ''}}' |