Skip to content

Commit c691642

Browse files
smoke: add upgrade test (legacy 3.8.8 → modern 3.9.2) (#629)
TestUpgradeLegacyToModern provisions a 6-node legacy Linux cluster (RHEL8/Rocky8/Ubuntu22, MCR stable-25.0, MKE 3.8.8), then upgrades it in place to MCR stable-29.2 / MKE 3.9.2 using a second Apply() call on the same Terraform infrastructure. Design: - runUpgradeTest() follows the same infra pattern as runSmokeTest() (defer terraform.Destroy first, tagged resources, temp SSH dir). - bumpVersions() unmarshals the Terraform-generated launchpad YAML, updates spec.mcr.channel and spec.mke.version, and re-marshals — preserving host addresses, SANs, LB names, and install flags verbatim. - Two sequential Apply() calls: base install then upgrade. Launchpad's UpgradeMCR and UpgradeMKE phases handle the delta automatically. - Reset() is best-effort (same rationale as the other smoke tests). Infra: - 90m go test timeout (install ~20min + upgrade ~20min + reset + buffer). - smoke-upgrade CI job gated by smoke-upgrade or smoke-test PR label. - Makefile target: make smoke-upgrade
1 parent 5224c5d commit c691642

3 files changed

Lines changed: 229 additions & 0 deletions

File tree

.github/workflows/smoke-tests.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,20 @@ jobs:
6868
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
6969
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
7070
run: make smoke-windows
71+
72+
smoke-upgrade:
73+
runs-on: ubuntu-latest
74+
if: |
75+
github.event_name == 'push' ||
76+
contains(github.event.pull_request.labels.*.name, 'smoke-test') ||
77+
contains(github.event.pull_request.labels.*.name, 'smoke-upgrade')
78+
steps:
79+
- name: Checkout code
80+
uses: actions/checkout@v4
81+
- name: Setup Terraform
82+
uses: hashicorp/setup-terraform@v3
83+
- name: Run upgrade smoke test
84+
env:
85+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
86+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
87+
run: make smoke-upgrade

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ smoke-legacy:
6363
.PHONY: smoke-windows
6464
smoke-windows:
6565
go test -count=1 -v ./test/smoke/... -run TestWindowsCluster -timeout 60m
66+
.PHONY: smoke-upgrade
67+
smoke-upgrade:
68+
go test -count=1 -v ./test/smoke/... -run TestUpgrade -timeout 90m
6669
.PHONY: clean-launchpad-chart
6770
clean-launchpad-chart:
6871
terraform -chdir=./examples/tf-aws/launchpad apply --auto-approve --destroy

test/smoke/upgrade_test.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package smoke_test
2+
3+
// Upgrade smoke test: provision a cluster at a baseline version, upgrade it
4+
// to a target version using a second Apply() call, verify the cluster is
5+
// healthy, then tear down.
6+
//
7+
// Design notes
8+
// ============
9+
// Launchpad's Apply() is idempotent and version-aware: when the installed MCR
10+
// channel or MKE version differs from the config, UpgradeMCR / UpgradeMKE
11+
// phases run automatically. So an upgrade test is just two sequential Apply()
12+
// calls on the same infrastructure with different version configs.
13+
//
14+
// YAML mutation between the two calls is done by unmarshaling the Terraform
15+
// output into a generic map, updating the relevant fields, and re-marshaling —
16+
// this avoids fragile string replacement and handles any extra fields the
17+
// Terraform module injects (SANs, LB addresses, etc.).
18+
//
19+
// Upgrade paths tested
20+
// ====================
21+
//
22+
// TestUpgradeLegacyToModern
23+
// install: MCR stable-25.0 / MKE 3.8.8 (legacy baseline)
24+
// upgrade: MCR stable-29.2 / MKE 3.9.2 (modern target)
25+
// nodes: rhel8/rocky8/ubuntu22 (same as TestLegacyCluster)
26+
//
27+
// These are the only hosts Launchpad CI provisions in the legacy matrix; they
28+
// are also the most representative real-world upgrade path (customer sites
29+
// running 3.8 that need to reach 3.9).
30+
31+
import (
32+
"fmt"
33+
"testing"
34+
35+
"github.com/Mirantis/launchpad/pkg/config"
36+
"github.com/Mirantis/launchpad/test"
37+
"github.com/gruntwork-io/terratest/modules/terraform"
38+
"github.com/stretchr/testify/assert"
39+
"github.com/stretchr/testify/require"
40+
"gopkg.in/yaml.v2"
41+
)
42+
43+
// upgradeConfig pairs a base install smokeConfig with target upgrade versions.
44+
type upgradeConfig struct {
45+
Base smokeConfig
46+
UpgradeMCRChannel string
47+
UpgradeMKEVersion string
48+
}
49+
50+
// runUpgradeTest provisions the cluster with Base versions, upgrades to the
51+
// target versions, then resets and destroys.
52+
func runUpgradeTest(t *testing.T, cfg upgradeConfig) {
53+
t.Helper()
54+
55+
uTestId := test.GenerateRandomAlphaNumericString(5)
56+
name := fmt.Sprintf("smoke-%s-%s", cfg.Base.Name, uTestId)
57+
58+
mkePassword := test.GenerateRandomAlphaNumericString(12)
59+
60+
mkeConnect := map[string]interface{}{
61+
"username": "admin",
62+
"password": mkePassword,
63+
"insecure": true,
64+
}
65+
66+
launchpad := map[string]interface{}{
67+
"drain": false,
68+
"mcr_channel": cfg.Base.MCRChannel,
69+
"mke_version": cfg.Base.MKEVersion,
70+
"msr_version": cfg.Base.MSRVersion,
71+
"mke_connect": mkeConnect,
72+
}
73+
74+
ngKeys := make([]string, 0, len(cfg.Base.Nodegroups))
75+
for k := range cfg.Base.Nodegroups {
76+
ngKeys = append(ngKeys, k)
77+
}
78+
79+
subnets := map[string]interface{}{
80+
"main": map[string]interface{}{
81+
"cidr": "172.31.0.0/17",
82+
"private": false,
83+
"nodegroups": ngKeys,
84+
},
85+
}
86+
87+
tempSSHKeyPathDir := t.TempDir()
88+
89+
vars := map[string]interface{}{
90+
"name": name,
91+
"aws": awsConfig,
92+
"launchpad": launchpad,
93+
"network": networkConfig,
94+
"subnets": subnets,
95+
"ssh_pk_location": tempSSHKeyPathDir,
96+
"nodegroups": cfg.Base.Nodegroups,
97+
"ssh_key_algorithm": cfg.Base.SSHKeyAlgorithm,
98+
"extra_tags": map[string]string{
99+
"launchpad-smoke-test": "true",
100+
"launchpad-smoke-test-name": cfg.Base.Name,
101+
},
102+
}
103+
104+
options := terraform.Options{
105+
TerraformDir: "../../examples/terraform/aws-simple",
106+
Vars: vars,
107+
}
108+
109+
terraformOptions := terraform.WithDefaultRetryableErrors(t, &options)
110+
defer terraform.Destroy(t, terraformOptions)
111+
112+
if _, err := terraform.InitAndApplyE(t, terraformOptions); err != nil {
113+
t.Fatal(err)
114+
}
115+
116+
baseYAML := terraform.Output(t, terraformOptions, "launchpad_yaml")
117+
118+
// ── Step 1: install at base versions ─────────────────────────────────────
119+
t.Logf("installing base: MCR %s / MKE %s", cfg.Base.MCRChannel, cfg.Base.MKEVersion)
120+
121+
baseProduct, err := config.ProductFromYAML([]byte(baseYAML))
122+
require.NoError(t, err, "parse base launchpad YAML")
123+
124+
err = baseProduct.Apply(true, true, 3, true)
125+
require.NoError(t, err, "base install Apply()")
126+
127+
// ── Step 2: build upgrade YAML ────────────────────────────────────────────
128+
// Unmarshal the Terraform-generated YAML into a generic map so we can
129+
// update version fields without disturbing host addresses, SANs, LB names,
130+
// or any other infrastructure-specific values the module injected.
131+
upgradeYAML, err := bumpVersions(baseYAML, cfg.UpgradeMCRChannel, cfg.UpgradeMKEVersion)
132+
require.NoError(t, err, "mutate YAML for upgrade")
133+
134+
// ── Step 3: upgrade ───────────────────────────────────────────────────────
135+
t.Logf("upgrading to: MCR %s / MKE %s", cfg.UpgradeMCRChannel, cfg.UpgradeMKEVersion)
136+
137+
upgradeProduct, err := config.ProductFromYAML([]byte(upgradeYAML))
138+
require.NoError(t, err, "parse upgrade launchpad YAML")
139+
140+
err = upgradeProduct.Apply(true, true, 3, true)
141+
assert.NoError(t, err, "upgrade Apply()")
142+
143+
// ── Step 4: reset (best-effort) ───────────────────────────────────────────
144+
// See smoke_test.go for rationale on non-fatal Reset().
145+
if err = upgradeProduct.Reset(); err != nil {
146+
t.Logf("WARN: product.Reset() failed (non-fatal): %v", err)
147+
}
148+
}
149+
150+
// bumpVersions deserialises yamlStr, replaces spec.mcr.channel and
151+
// spec.mke.version with the supplied values, and returns the re-serialised
152+
// YAML. The rest of the document (hosts, SANs, LB addresses, flags, …) is
153+
// preserved verbatim so the upgrade runs against the same infrastructure that
154+
// was just provisioned.
155+
func bumpVersions(yamlStr, mcrChannel, mkeVersion string) (string, error) {
156+
var doc map[interface{}]interface{}
157+
if err := yaml.Unmarshal([]byte(yamlStr), &doc); err != nil {
158+
return "", fmt.Errorf("unmarshal cluster YAML: %w", err)
159+
}
160+
161+
spec, ok := doc["spec"].(map[interface{}]interface{})
162+
if !ok {
163+
return "", fmt.Errorf("cluster YAML missing spec")
164+
}
165+
166+
if mcr, ok := spec["mcr"].(map[interface{}]interface{}); ok {
167+
mcr["channel"] = mcrChannel
168+
} else {
169+
spec["mcr"] = map[interface{}]interface{}{"channel": mcrChannel}
170+
}
171+
172+
if mke, ok := spec["mke"].(map[interface{}]interface{}); ok {
173+
mke["version"] = mkeVersion
174+
} else {
175+
return "", fmt.Errorf("cluster YAML missing spec.mke")
176+
}
177+
178+
out, err := yaml.Marshal(doc)
179+
if err != nil {
180+
return "", fmt.Errorf("re-marshal upgraded YAML: %w", err)
181+
}
182+
return string(out), nil
183+
}
184+
185+
// TestUpgradeLegacyToModern installs MKE 3.8.8 / MCR stable-25.0 on a
186+
// legacy Linux matrix (rhel8, rocky8, ubuntu22) and then upgrades it to
187+
// MKE 3.9.2 / MCR stable-29.2 in place. This is the primary real-world
188+
// upgrade path for customers running the 3.8 stack.
189+
func TestUpgradeLegacyToModern(t *testing.T) {
190+
runUpgradeTest(t, upgradeConfig{
191+
Base: smokeConfig{
192+
Name: "upgrade",
193+
MCRChannel: "stable-25.0",
194+
MKEVersion: "3.8.8",
195+
MSRVersion: "2.9.28",
196+
SSHKeyAlgorithm: "ed25519",
197+
Nodegroups: map[string]interface{}{
198+
"MngrRhel8": test.Platforms["Rhel8"].GetManager(),
199+
"MngrRocky8": test.Platforms["Rocky8"].GetManager(),
200+
"MngrUbuntu22": test.Platforms["Ubuntu22"].GetManager(),
201+
"WrkRhel8": test.Platforms["Rhel8"].GetWorker(),
202+
"WrkRocky8": test.Platforms["Rocky8"].GetWorker(),
203+
"WrkUbuntu22": test.Platforms["Ubuntu22"].GetWorker(),
204+
},
205+
},
206+
UpgradeMCRChannel: "stable-29.2",
207+
UpgradeMKEVersion: "3.9.2",
208+
})
209+
}

0 commit comments

Comments
 (0)