Skip to content

Commit abcccc2

Browse files
feat(ppg-packer): extend AMI factory to Rocky Linux 8/9/10
- Generalize the refresh template (oracle-linux.pkr.hcl -> refresh.pkr.hcl) with an os dimension (oraclelinux|rocky) threaded through tags, AMI names, the SSH login user, and the lineage source filter - Add a seed path (-var seed=true) sourcing the official Rocky community AMIs (include_deprecated covers the frozen Rocky 8) to root each lineage once per combo - Make validate.sh and the smoke template distro-aware (/etc/rocky-release, powertools/crb vs ol*_codeready_builder, rocky vs ec2-user login) - Rename the workflow to ppg-ami-factory.yml, add an os matrix dimension and a pull_request trigger for the no-AWS check job; default combos stay OL-only until the Rocky seeds are promoted - Thread os through the justfile (validate/build/bake/all/latest, os-aware prune sweeps) and add seed-rocky + ci-seed-rocky recipes
1 parent 80120bf commit abcccc2

6 files changed

Lines changed: 331 additions & 159 deletions

File tree

.github/workflows/ppg-oracle-ami-factory.yml renamed to .github/workflows/ppg-ami-factory.yml

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
1-
# Oracle Linux package-test AMI factory.
2-
# Bakes OL8/OL9 (and later OL10) x86_64+arm64 AMIs with Packer over AWS Session
1+
# EL package-test AMI factory: Oracle Linux + Rocky Linux.
2+
# Bakes OL and Rocky 8/9/10 x86_64+arm64 AMIs with Packer over AWS Session
33
# Manager (no inbound SSH), authenticated via GitHub OIDC (no static keys), and
44
# promotes each via a fresh-boot smoke test. Consumer: pg.cd molecule jobs select
55
# the newest role=ppg-package-test AMI by tag (vars/moleculeEnvPPG.groovy).
6+
# Rocky lineage roots are seeded once per combo via dispatch (just ci-seed-rocky).
67
#
7-
# Triggers: recipe change (paths), weekly security rebake (cron), manual.
8+
# Triggers: PR validate-only (pull_request -> check job, no AWS), recipe change
9+
# (paths), weekly security rebake (cron), manual.
810
# Actions are pinned to commit SHAs; the trailing "# vX.Y.Z" records the tag.
911

10-
name: ppg-oracle-ami-factory
11-
run-name: ppg OL AMI factory (${{ inputs.env || 'prod' }}, ${{ github.event_name }})
12+
name: ppg-ami-factory
13+
run-name: ppg AMI factory (${{ inputs.env || 'prod' }}, ${{ github.event_name }})
1214

1315
on:
1416
workflow_dispatch:
1517
inputs:
1618
combos:
17-
description: 'JSON matrix include list, e.g. [{"os_major":"9","arch":"x86_64"}]'
19+
description: 'JSON matrix include list, e.g. [{"os":"rocky","os_major":"9","arch":"x86_64","seed":true}]; os defaults to oraclelinux, seed to false'
1820
required: false
19-
default: '[{"os_major":"9","arch":"x86_64"}]'
21+
default: '[{"os":"oraclelinux","os_major":"9","arch":"x86_64"}]'
2022
env:
2123
description: 'prod (real factory tags) or test (isolated tags, never consumed by pg.cd)'
2224
required: false
@@ -27,9 +29,13 @@ on:
2729
branches: [master]
2830
paths:
2931
- 'ppg/packer/**'
30-
- '.github/workflows/ppg-oracle-ami-factory.yml'
32+
- '.github/workflows/ppg-ami-factory.yml'
33+
pull_request:
34+
paths:
35+
- 'ppg/packer/**'
36+
- '.github/workflows/ppg-ami-factory.yml'
3137
schedule:
32-
- cron: '0 6 * * 1' # weekly Mon 06:00 UTC - absorb Oracle security errata
38+
- cron: '0 6 * * 1' # weekly Mon 06:00 UTC - absorb OL + Rocky security errata
3339

3440
permissions:
3541
contents: read # workflow-level default: read-only
@@ -67,35 +73,48 @@ jobs:
6773
packer fmt -check -diff .
6874
packer fmt -check -diff smoke
6975
packer init .
70-
for m in 8 9 10; do
71-
for a in x86_64 arm64; do
72-
echo "validate OL$m $a"
73-
packer validate -var "os_major=$m" -var "arch=$a" .
76+
for os_name in oraclelinux rocky; do
77+
for major in 8 9 10; do
78+
for arch in x86_64 arm64; do
79+
echo "validate $os_name $major $arch"
80+
packer validate -var "os=$os_name" -var "os_major=$major" -var "arch=$arch" .
81+
done
7482
done
7583
done
76-
( cd smoke && packer init . && packer validate \
84+
for major in 8 9 10; do
85+
for arch in x86_64 arm64; do
86+
echo "validate rocky seed $major $arch"
87+
packer validate -var os=rocky -var seed=true -var "os_major=$major" -var "arch=$arch" .
88+
done
89+
done
90+
( cd smoke && packer init . \
91+
&& packer validate \
92+
-var candidate_ami=ami-00000000000000000 \
93+
-var os=oraclelinux -var os_major=9 -var arch=x86_64 . \
94+
&& packer validate \
7795
-var candidate_ami=ami-00000000000000000 \
78-
-var os_major=9 -var arch=x86_64 . )
96+
-var os=rocky -var os_major=8 -var arch=x86_64 . )
7997
# drift guard (mirrors `just check`): justfile root_gib must equal var.volume_size,
8098
# else a reimaged base would be the wrong root size for the refresh to launch.
8199
rg=$(awk -F'"' '/^root_gib[[:space:]]*:=/{print $2; exit}' justfile)
82-
vs=$(awk '/variable "volume_size"/{f=1} f&&/default/{gsub(/[^0-9]/,"");print;exit}' oracle-linux.pkr.hcl)
100+
vs=$(awk '/variable "volume_size"/{f=1} f&&/default/{gsub(/[^0-9]/,"");print;exit}' refresh.pkr.hcl)
83101
[[ -n "$rg" && "$rg" == "$vs" ]] || { echo "DRIFT: justfile root_gib=$rg != var.volume_size=$vs"; exit 1; }
84102
echo "check OK"
85103
86104
bake:
87-
name: OL${{ matrix.os_major }} ${{ matrix.arch }}
105+
name: ${{ matrix.os || 'oraclelinux' }} ${{ matrix.os_major }} ${{ matrix.arch }}
88106
needs: check # no EC2 launches until templates validate
107+
if: github.event_name != 'pull_request' # PRs run the no-AWS check only
89108
runs-on: ubuntu-latest
90109
timeout-minutes: 60
91110
permissions:
92111
contents: read
93112
id-token: write # ONLY this job mints the OIDC token
94113
strategy:
95114
fail-fast: false
96-
max-parallel: 2
115+
max-parallel: 12
97116
matrix:
98-
include: ${{ fromJSON(github.event.inputs.combos || '[{"os_major":"8","arch":"x86_64"},{"os_major":"8","arch":"arm64"},{"os_major":"9","arch":"x86_64"},{"os_major":"9","arch":"arm64"},{"os_major":"10","arch":"x86_64"},{"os_major":"10","arch":"arm64"}]') }}
117+
include: ${{ fromJSON(github.event.inputs.combos || '[{"os":"oraclelinux","os_major":"8","arch":"x86_64"},{"os":"oraclelinux","os_major":"8","arch":"arm64"},{"os":"oraclelinux","os_major":"9","arch":"x86_64"},{"os":"oraclelinux","os_major":"9","arch":"arm64"},{"os":"oraclelinux","os_major":"10","arch":"x86_64"},{"os":"oraclelinux","os_major":"10","arch":"arm64"}]') }}
99118
steps:
100119
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
101120

@@ -129,18 +148,22 @@ jobs:
129148
working-directory: ppg/packer
130149
run: packer init .
131150

132-
- name: Build candidate AMI (OL${{ matrix.os_major }} ${{ matrix.arch }}, env=${{ env.FACTORY_ENV }})
151+
- name: Build candidate AMI (${{ matrix.os || 'oraclelinux' }} ${{ matrix.os_major }} ${{ matrix.arch }}, env=${{ env.FACTORY_ENV }})
133152
id: build
134153
working-directory: ppg/packer
135154
# Matrix values via env, not inline ${{ }}, so they reach packer as data
136155
# (quoted shell vars) and cannot break out of the run shell.
137156
env:
157+
OS: ${{ matrix.os || 'oraclelinux' }}
158+
SEED: ${{ matrix.seed || false }}
138159
OS_MAJOR: ${{ matrix.os_major }}
139160
ARCH: ${{ matrix.arch }}
140161
run: |
141162
set -euo pipefail
142163
rm -f manifest.json
143164
packer build -color=false \
165+
-var "os=${OS}" \
166+
-var "seed=${SEED}" \
144167
-var "os_major=${OS_MAJOR}" \
145168
-var "arch=${ARCH}" \
146169
-var "region=${AWS_REGION}" \
@@ -149,22 +172,23 @@ jobs:
149172
echo "ami=$AMI" >> "$GITHUB_OUTPUT"
150173
echo "built candidate: $AMI"
151174
{
152-
echo "### OL${OS_MAJOR} ${ARCH}"
175+
echo "### ${OS} ${OS_MAJOR} ${ARCH}"
153176
echo "- built: $AMI"
154177
} >> "$GITHUB_STEP_SUMMARY"
155178
156179
- name: Smoke test (boot + install)
157180
working-directory: ppg/packer
158181
env:
159182
AMI: ${{ steps.build.outputs.ami }}
183+
OS: ${{ matrix.os || 'oraclelinux' }}
160184
OS_MAJOR: ${{ matrix.os_major }}
161185
ARCH: ${{ matrix.arch }}
162186
run: |
163187
set -euo pipefail
164188
echo "smoke candidate: $AMI"
165189
# Deregister ONLY on a real boot/install failure (never on a later promote-tag failure).
166190
( cd smoke && packer init . && packer build -color=false \
167-
-var "candidate_ami=$AMI" -var "os_major=${OS_MAJOR}" \
191+
-var "candidate_ami=$AMI" -var "os=${OS}" -var "os_major=${OS_MAJOR}" \
168192
-var "arch=${ARCH}" -var "region=${AWS_REGION}" . ) \
169193
|| { echo "smoke (boot+install) failed; deregistering $AMI + its snapshots"; aws ec2 deregister-image --delete-associated-snapshots --region "$AWS_REGION" --image-id "$AMI"; echo "- smoke: FAIL (deregistered $AMI)" >> "$GITHUB_STEP_SUMMARY"; exit 1; }
170194
echo "- smoke: pass" >> "$GITHUB_STEP_SUMMARY"
@@ -202,4 +226,4 @@ jobs:
202226
webhook: ${{ env.SLACK_WEBHOOK }}
203227
webhook-type: incoming-webhook
204228
payload: |
205-
text: "PPG-OL-AMI factory FAILED - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
229+
text: "PPG AMI factory FAILED - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"

ppg/packer/README.md

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
# PPG Oracle Linux AMI factory
1+
# PPG EL AMI factory (Oracle Linux + Rocky Linux)
22

3-
Builds the Oracle Linux package-test target AMIs that PG release testing runs
4-
against, so we stop hand-maintaining them (launch base, `dnf update`, snapshot).
3+
Builds the Oracle Linux and Rocky Linux package-test target AMIs that PG release
4+
testing runs against, so we stop hand-maintaining them (launch base,
5+
`dnf update`, snapshot).
56

67
## Why this exists
78

8-
There is no off-the-shelf AWS image that is all of: genuine Oracle Linux,
9-
OL8/OL9/OL10, x86_64 **and** arm64, free of software fees, and maintained.
9+
**Oracle Linux**: there is no off-the-shelf AWS image that is all of: genuine
10+
Oracle Linux, OL8/OL9/OL10, x86_64 **and** arm64, free of software fees, and
11+
maintained.
1012

1113
- Oracle stopped publishing official AWS AMIs. The newest public Oracle images
1214
(owner `131827586825`) are OL8.9 / OL9.3 from Feb 2024 and were all deprecated
@@ -17,6 +19,16 @@ OL8/OL9/OL10, x86_64 **and** arm64, free of software fees, and maintained.
1719
- AlmaLinux is free + current + both-arch, but it is an EL clone, fine as
1820
supplemental smoke coverage, not a replacement for the Oracle Linux gate.
1921

22+
**Rocky Linux**: official community AMIs exist (owner `792107900819`, no fees,
23+
no product codes), but upstream stopped publishing Rocky 8 images at
24+
`8.10-20240528` and AWS auto-deprecated them in 2026, so the previously
25+
hand-pinned Rocky 8 AMI aged two years of errata into every molecule run. The
26+
factory seeds each Rocky lineage once from the official AMI, then refreshes it
27+
weekly like OL, which keeps Rocky 8 current even though upstream no longer
28+
ships it. The official 10 GiB roots restore fine onto the factory's
29+
`var.volume_size` (EBS only refuses to shrink), so Rocky needs no
30+
bootstrap/re-image machinery.
31+
2032
So we bake our own, on a schedule, in the CI build account (eu-central-1). The
2133
same Packer templates + scripts run whether driven locally (`justfile`) or by the
2234
GitHub Actions workflow. Builds connect over AWS Session Manager (no inbound SSH)
@@ -26,7 +38,8 @@ and authenticate via GitHub OIDC (no static keys).
2638

2739
| Path | Covers | Mechanism |
2840
|------|--------|-----------|
29-
| **Refresh** (`oracle-linux.pkr.hcl`) | OL8, OL9, OL10 (x86_64 + arm64) | `amazon-ebs`: launch the latest self-owned base of the same major+arch, `dnf update`, validate (fail-closed), snapshot. Each build's output is the next build's source. |
41+
| **Refresh** (`refresh.pkr.hcl`) | OL + Rocky 8/9/10 (x86_64 + arm64) | `amazon-ebs`: launch the latest self-owned base of the same os+major+arch, `dnf update`, validate (fail-closed), snapshot. Each build's output is the next build's source. |
42+
| **Seed** (`refresh.pkr.hcl -var seed=true`) | Rocky lineage roots, one-time per combo | Same bake, but sourced from the official Rocky community AMI (`include_deprecated` covers the frozen Rocky 8). Promoted seeds become the refresh lineage. |
3043
| **Bootstrap** (`scripts/bootstrap-ol10.sh`) | OL10 lineage root, one-time per arch | Import Oracle's official OL10 cloud image (arm64 `*-kvm-cloud-*.qcow2`, x86_64 `*-aws-*.vmdk`) via `aws ec2 import-snapshot`, register the raw base, then `packer build bootstrap/finalize-ol10.pkr.hcl` (adds `ec2-user` + `amazon-ssm-agent` + `dnf update`). Needed because Oracle ships no OL10 AWS AMI. Promotes to a `prebase` role: Oracle's full-size root cannot launch on `var.volume_size`, so `reimage-ol10` shrinks it before it becomes the consumed base. Idempotent. |
3144
| **Re-image** (`scripts/reimage-ol10.sh`) | OL10 root shrink, one-time per arch | Copy the current base's root onto a fresh `var.volume_size` GiB volume on a builder, snapshot + register, then `reimage-ol10-verify` boot-tests two sizes + smoke + size-gate + promote. Needed because EBS cannot restore a volume below its source snapshot and XFS cannot shrink in place. Full internals (`dd` /boot, LVM-to-plain, the gates): [docs/reimage.md](docs/reimage.md). |
3245

@@ -35,24 +48,30 @@ and authenticate via GitHub OIDC (no static keys).
3548
```bash
3649
export AWS_PROFILE=percona-dev-admin
3750
just check # fmt-check + validate every combo (no AWS)
38-
just bake 9 x86_64 # build + smoke + promote one combo
51+
just bake 9 x86_64 # build + smoke + promote one OL combo
52+
just bake 9 x86_64 prod rocky # same for a seeded Rocky combo
53+
just seed-rocky 9 x86_64 prod # one-time Rocky lineage root (bake + smoke + promote)
3954
just all # build + promote every OL major x arch
55+
just all prod rocky # same for Rocky (after seeding)
4056
just bootstrap-ol10-prep # one-time: import bucket + vmimport role + boot-test SG/key
4157
just bootstrap-ol10 x86_64 # one-time OL10 lineage root for an arch
4258
just bootstrap-ol10-verify <ami> x86_64 # boot-validate a candidate then promote it (prebase)
4359
just reimage-ol10 x86_64 # shrink the OL10 base root to var.volume_size (lineage shrink)
4460
just reimage-ol10-verify <ami> x86_64 # boot-test two sizes + smoke + size-gate + promote
4561
```
4662

47-
Each build registers `OL<major>-<arch>-<UTCstamp>` tagged `os=oraclelinux`,
63+
Each build registers `OL<major>-<arch>-<UTCstamp>` / `Rocky<major>-<arch>-<UTCstamp>`
64+
tagged `os=oraclelinux|rocky`,
4865
`os_major`, `arch`, `source=factory` with **`role=ppg-candidate`**, then the
4966
native-Packer smoke (`smoke/smoke.pkr.hcl`) fresh-boot-tests it. The smoke
5067
template ONLY validates; **promotion to `role=ppg-package-test` is a separate,
5168
retried step** in the `justfile`/workflow that runs after a passing smoke, so a
5269
transient tag-API failure can never deregister a smoke-passed AMI. A real
5370
boot/install failure deregisters the candidate + its snapshots.
5471
Consumers and the next build's source filter select only `role=ppg-package-test`,
55-
so a non-booting image is never selectable and AMI IDs are never pinned. "Latest"
72+
so a non-booting image is never selectable and AMI IDs are never pinned.
73+
Login user: `ec2-user` on OL, `rocky` on Rocky (the images keep their distro's
74+
cloud-init default user, matching what the molecule scenarios already use). "Latest"
5675
is the newest `role=ppg-package-test` AMI by `CreationDate` (no SSM parameter).
5776

5877
## Housekeeping (all via `just`, fail-safe)
@@ -74,7 +93,8 @@ just ci-validate # trigger the GHA workflow matrix (env=test)
7493
## Consumer lookup (replaces hardcoded IDs)
7594

7695
`vars/moleculeEnvPPG.groovy` resolves each Oracle target at pipeline time
77-
(fail-closed: the job aborts if no base AMI is found):
96+
(fail-closed: the job aborts if no base AMI is found); Rocky targets switch to
97+
the same lookup with `Name=tag:os,Values=rocky` once the seeds are promoted:
7898

7999
```bash
80100
ami_ol9_x86_64=$(aws ec2 describe-images --region eu-central-1 --owners self \
@@ -89,11 +109,12 @@ ami_ol9_x86_64=$(aws ec2 describe-images --region eu-central-1 --owners self \
89109
`scripts/validate.sh` runs as the last provisioner; any failure aborts the build
90110
so a broken/mislabeled image is never registered:
91111

92-
1. `/etc/oracle-release` major matches the target (fidelity).
112+
1. `/etc/oracle-release` / `/etc/rocky-release` major matches the target (fidelity).
93113
2. `uname -m` matches the target arch.
94-
3. `ol<major>_codeready_builder` repo is defined (PG CRB deps resolvable).
114+
3. The CRB repo is defined: `ol<major>_codeready_builder` (OL), `powertools`
115+
(Rocky 8) or `crb` (Rocky 9/10). PG -devel deps resolve from it.
95116
4. `cloud-init` present.
96-
5. SELinux `enforcing` in `/etc/selinux/config` (fail-closed; stock OL ships enforcing).
117+
5. SELinux `enforcing` in `/etc/selinux/config` (fail-closed; stock OL and Rocky ship enforcing).
97118
6. `dnf makecache` succeeds (repos healthy).
98119

99120
De-instancing is native: Packer's `ssh_clear_authorized_keys` strips the temp key,

0 commit comments

Comments
 (0)