Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5e5fcf3
feat: add unified airgap RKE2 infra pipeline (Jenkinsfile.airgap-rke2…
floatingman Mar 31, 2026
83c2c75
fix: add repo URL parameters to standardCheckout
floatingman Mar 31, 2026
3d703e3
fix: load all credentials once at stages level
floatingman Mar 31, 2026
fc4912f
fix: wrap all stages in single property.useWithProperties block
floatingman Mar 31, 2026
fe058f4
debug: add logging before S3 upload to diagnose empty params
floatingman Mar 31, 2026
0b3d102
fix: use local variable for workspaceName instead of env
floatingman Mar 31, 2026
3753779
fix: pass S3 bucket/region overrides to shared s3 functions
floatingman Mar 31, 2026
f96e2e8
feat: generate Ansible inventory from tofu airgap_inventory_json output
floatingman Apr 1, 2026
cf2f126
refactor: use generate_inventory.py instead of inline Groovy inventory
floatingman Apr 1, 2026
de2d73e
refactor: eliminate S3 tfvars round-trip, reconstruct from job config
floatingman Apr 1, 2026
e5befd9
fix: add SSH key copy stage before RKE2 deploy
floatingman Apr 1, 2026
dbc343b
fix: add debug logging to post-failure teardown
floatingman Apr 1, 2026
4cd926b
fix: read workspace name from file in post-failure teardown
floatingman Apr 1, 2026
2e227af
feat: split airgap RKE2 infra pipeline into setup/destroy jobs
floatingman Apr 1, 2026
8d2c8fb
fix: call make.runTarget() directly instead of new make()
floatingman Apr 1, 2026
832530e
fix: replace YAML pipe syntax with Groovy triple-quoted string in des…
floatingman Apr 1, 2026
1df9764
chore: remove unified Jenkinsfile.airgap-rke2-infra (replaced by spli…
floatingman Apr 1, 2026
883dddf
fix: address PR #595 review comments
floatingman Apr 1, 2026
1ae5f96
docs: add PR review guidelines to copilot-instructions
floatingman Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,43 @@ Always update relevant documentation when making changes:
* return pointers to objects where possible in helper functions
* if the case of ignoring errors arises, always justify via comment as to why it is ignored
* strings should be a const wherever possible except for logs and error messages

## Pull Request Review Guidelines

When reviewing PRs, Copilot should check for the following patterns specific to this repository.

### Jenkinsfile reviews (`validation/pipeline/Jenkinsfile.*`)

**Security**
- No hardcoded secrets in `defaultValue` blocks — use `${VARIABLE}` substitution and `password` parameter types
- `password` type parameters for all credentials (registry passwords, Rancher passwords, API keys)
- `useWithProperties` blocks must include every credential referenced in their scope's substitution maps

**Jenkins pipeline correctness**
- Use `env.X` not `params.X` for the `library` directive at the top of Jenkinsfiles — `params` is not resolved at library load time
- `text` parameters use `defaultValue: '''...'''` (Groovy triple-quoted), never `default: |` (YAML pipe syntax)
- Paths should use `env.*_DIR` variables (set by `airgap.standardCheckout`), not hardcoded directory names like `'qa-infra-automation'`
- `archiveArtifacts` should not use `fingerprint: true` for transient build artifacts

**Parameterization**
- Ansible variable templates should reference pipeline parameters, not hardcode values (e.g., `${RANCHER_BOOTSTRAP_PASSWORD}` not `"rancherrocks"`)
- Boolean-like Ansible variables (`enable_private_registry`, `deploy_rancher`) should reflect pipeline parameters, not be hardcoded to `true`

### Go test reviews

**Build tags**
- Every test file must have a `//go:build` line with appropriate tags
- Version tags must follow the existing patterns in the codebase (e.g., `2.13`, `!2.8`)
- Do not add `pit` tags unless the test explicitly uses external non-Rancher APIs

**Code organization**
- Helper functions that return errors are preferred over helpers accepting `testing.T`
- Use `logrus` for logging, never `testing.T.Log`
- Shared helpers belong in `actions/`, not duplicated across test packages
- Functions matching the extension criteria should target the [shepherd](https://github.com/rancher/shepherd) repo instead

**Test quality**
- At least 2 tests per suite when possible: one dynamic (config-driven) and one static
- Feature name only in `SuiteName`, not in individual test names
- Validate in a separate function, not inline in the test
- Use `session.Cleanup()` in `TearDownSuite` for resource cleanup
1 change: 1 addition & 0 deletions validation/pipeline/Dockerfile.infra
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ RUN apk add --no-cache \
bash \
curl \
git \
make \
openssh-client \
python3 \
py3-pip \
Expand Down
201 changes: 201 additions & 0 deletions validation/pipeline/Jenkinsfile.destroy.airgap-rke2-infra
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#!groovy
/**
* Airgap RKE2 infrastructure destroy pipeline.
*
* Tears down AWS infrastructure provisioned by the setup pipeline.
* Uses the Jenkins library directly (NOT the Makefile) because:
* - The Makefile's infra-down target has an interactive confirmation prompt
* - The Makefile has no workspace management (select/delete)
* - The Jenkins library's tofu.teardownInfrastructure handles the full
* sequence: selectWorkspace → destroy → deleteWorkspace
*
* Consumes shared functions from qa-jenkins-library:
* airgap.standardCheckout, airgap.teardownInfrastructure,
* tofu.initBackend, infrastructure.parseAndSubstituteVars,
* infrastructure.writeConfig, infrastructure.cleanupArtifacts
*/

def libraryBranch = env.QA_JENKINS_LIBRARY_BRANCH ?: 'main'
library "qa-jenkins-library@${libraryBranch}"

pipeline {
agent any

options {
ansiColor('xterm')
timeout(time: 120, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '30'))
}

parameters {
string(
name: 'TARGET_WORKSPACE',
defaultValue: '',
description: 'Terraform workspace to destroy (e.g. jenkins_airgap_ansible_workspace_42_hostname)'
)
string(
name: 'QA_JENKINS_LIBRARY_BRANCH',
defaultValue: 'main',
description: 'Branch of qa-jenkins-library to use'
)
string(
name: 'TESTS_REPO_URL',
defaultValue: 'https://github.com/rancher/tests',
description: 'URL of rancher/tests repository'
)
string(
name: 'TESTS_BRANCH',
defaultValue: 'main',
description: 'Branch of rancher/tests repository'
)
string(
name: 'QA_INFRA_REPO_URL',
defaultValue: 'https://github.com/rancher/qa-infra-automation',
description: 'URL of qa-infra-automation repository'
)
string(
name: 'QA_INFRA_BRANCH',
defaultValue: 'main',
description: 'Branch of qa-infra-automation repository'
)
string(
name: 'HOSTNAME_PREFIX',
defaultValue: '',
description: 'Hostname prefix used during setup (for tfvars substitution)'
)
string(
name: 'S3_BUCKET_NAME',
defaultValue: 'jenkins-terraform-state-storage',
description: 'S3 bucket name where Terraform state is stored'
)
string(
name: 'S3_BUCKET_REGION',
defaultValue: 'us-east-2',
description: 'AWS region where the S3 bucket is located'
)
string(
name: 'S3_KEY_PREFIX',
defaultValue: 'terraform.tfstate',
description: 'S3 key prefix for the Terraform state files'
)
text(
name: 'TERRAFORM_CONFIG',
description: 'Terraform config values (same as used during setup)',
defaultValue: '''aws_access_key = "${AWS_ACCESS_KEY_ID}"
aws_secret_key = "${AWS_SECRET_ACCESS_KEY}"
aws_ami = "ami-09457fad1d2c34c31"
instance_type = "t3a.xlarge"
aws_security_group = ["sg-08e8243a8cfbea8a0"]
aws_subnet = "subnet-ee8cac86"
aws_volume_size = 100
aws_hostname_prefix = "${HOSTNAME_PREFIX}"
aws_region = "us-east-2"
aws_route53_zone = "qa.rancher.space"
aws_ssh_user = "ubuntu"
aws_vpc = "vpc-bfccf4d7"
user_id = "ubuntu"
ssh_key = "/root/.ssh/jenkins-elliptic-validation.pem"
ssh_key_name = "jenkins-elliptic-validation"
provision_registry = false'''
)
}

stages {
stage('Checkout') {
steps {
script {
def dirs = airgap.standardCheckout(
testsRepo: [url: params.TESTS_REPO_URL, branch: params.TESTS_BRANCH],
infraRepo: [url: params.QA_INFRA_REPO_URL, branch: params.QA_INFRA_BRANCH]
)
env.TESTS_DIR = dirs.testsDir
env.INFRA_DIR = dirs.infraDir
}
}
}

stage('Build Infrastructure Tools Image') {
steps {
sh "docker build --platform linux/amd64 -t rancher-infra-tools:latest -f ${env.TESTS_DIR}/validation/pipeline/Dockerfile.infra ."
}
}

stage('Destroy Infrastructure') {
steps {
script {
property.useWithProperties([
'AWS_ACCESS_KEY_ID',
'AWS_SECRET_ACCESS_KEY'
]) {
def tofuModulePath = "${env.INFRA_DIR}/tofu/aws/modules/airgap"

// ── Validate parameters ────────────────────────────
stage('Validate Parameters') {
if (!params.TARGET_WORKSPACE?.trim()) {
error 'TARGET_WORKSPACE parameter is required for destroy action'
}
echo "Target workspace for destruction: ${params.TARGET_WORKSPACE}"
}

// ── Initialize Tofu backend ────────────────────────
stage('Initialize Tofu Backend') {
tofu.initBackend(
dir: tofuModulePath,
bucket: env.S3_BUCKET_NAME,
key: env.S3_KEY_PREFIX,
region: env.S3_BUCKET_REGION,
backendInitScript: './scripts/init-backend.sh'
)
}

// ── Configure Tofu variables ───────────────────────
stage('Configure Tofu Variables') {
def terraformConfig = infrastructure.parseAndSubstituteVars(
content: env.TERRAFORM_CONFIG,
envVars: [
'AWS_ACCESS_KEY_ID': env.AWS_ACCESS_KEY_ID,
'AWS_SECRET_ACCESS_KEY': env.AWS_SECRET_ACCESS_KEY,
'HOSTNAME_PREFIX': env.HOSTNAME_PREFIX
]
)

infrastructure.writeConfig(
path: "${tofuModulePath}/terraform.tfvars",
content: terraformConfig
)
}

// ── Teardown infrastructure ────────────────────────
stage('Teardown Infrastructure') {
airgap.teardownInfrastructure(
dir: tofuModulePath,
name: params.TARGET_WORKSPACE,
varFile: 'terraform.tfvars'
)
}

// ── Cleanup local artifacts ────────────────────────
stage('Cleanup Local Artifacts') {
infrastructure.cleanupArtifacts(
paths: [
"${tofuModulePath}/.terraform",
"${tofuModulePath}/terraform.tfstate*",
"${tofuModulePath}/*.tfvars"
],
force: true
)
}

// ── Summary ────────────────────────────────────────
stage('Summary') {
echo '=== Infrastructure Destruction Complete ==='
echo "Workspace: ${params.TARGET_WORKSPACE}"
echo 'All resources have been destroyed and cleaned up'
echo '==========================================='
}
}
}
}
}
}
}
Loading
Loading