Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
236de9c
feat(pmm-ha): add ROSA HCP testing pipelines
nogueiraanderson Dec 1, 2025
66148d9
fix(pmm-ha-rosa): add library import for shared functions
nogueiraanderson Dec 1, 2025
427d741
fix(pmm-ha-rosa): detect architecture for CLI downloads (x86_64/arm64)
nogueiraanderson Dec 1, 2025
37c10cc
fix(pmm-ha-rosa): use agent-amd64-ol9 (ROSA CLI has no ARM64 build)
nogueiraanderson Dec 1, 2025
0b6c98e
fix(pmm-ha-rosa): set AWS region for ROSA login
nogueiraanderson Dec 1, 2025
8ffc950
fix(pmm-ha-rosa): use @Field annotation for script-level constants
nogueiraanderson Dec 1, 2025
f4ae997
fix(pmm-ha-rosa): resolve full version for ROSA HCP (4.16 -> 4.16.52)
nogueiraanderson Dec 1, 2025
6ed9b7d
fix(pmm-ha-rosa): add OIDC config ID for ROSA HCP clusters
nogueiraanderson Dec 1, 2025
61809e9
fix(pmm-ha-rosa): set AWS region for OIDC config creation
nogueiraanderson Dec 1, 2025
e05b76c
fix(pmm-ha-rosa): set AWS region for OIDC listing and use -o json for…
nogueiraanderson Dec 1, 2025
8a58697
fix(pmm-ha-rosa): create VPC network stack for HCP clusters
nogueiraanderson Dec 1, 2025
f5ff873
fix(pmm-ha-rosa): convert BUILD_NUMBER to int for VPC CIDR
nogueiraanderson Dec 1, 2025
aa4ac96
fix(pmm-ha-rosa): set AWS region for VPC network creation
nogueiraanderson Dec 1, 2025
58cde92
fix(pmm-ha-rosa): add machine-cidr to match VPC CIDR
nogueiraanderson Dec 1, 2025
6afd5c8
fix(pmm-ha-rosa): add AWS_DEFAULT_REGION to all rosa CLI commands
nogueiraanderson Dec 1, 2025
f25cef9
fix(pmm-ha-rosa): replace non-existent rosa wait with polling loop
nogueiraanderson Dec 1, 2025
b09ab75
fix(pmm-ha-rosa): use local var instead of env var for Map return value
nogueiraanderson Dec 1, 2025
adf1d8d
fix(pmm-ha-rosa): add AWS_DEFAULT_REGION to configureAccess
nogueiraanderson Dec 1, 2025
3353388
fix(pmm-ha-rosa): fix password extraction from rosa create admin
nogueiraanderson Dec 1, 2025
2d59576
chore(pmm-ha-rosa): disable cluster cleanup on failure for debugging
nogueiraanderson Dec 1, 2025
60a8a6f
fix(pmm-ha-rosa): use pattern matching for ROSA password extraction
nogueiraanderson Dec 1, 2025
124a2dd
fix(pmm-ha-rosa): ROSA HCP compatibility and PMM HA installation fixes
nogueiraanderson Dec 1, 2025
03d48cd
feat(pmm-ha-rosa): add Docker Hub pull secret to avoid rate limiting
nogueiraanderson Dec 1, 2025
510911e
fix(pmm-ha-rosa): use global OpenShift pull secret for Docker Hub auth
nogueiraanderson Dec 1, 2025
b0f263b
test: add temporary job to validate pmm-secret structure
nogueiraanderson Dec 1, 2025
87c38a9
fix(pmm-ha-rosa): add all required keys to pmm-secret for helm chart
nogueiraanderson Dec 1, 2025
c70a1b7
chore: remove temporary test job
nogueiraanderson Dec 1, 2025
f1c27d6
fix(pmm-ha-rosa): add pmm-ha-secret-generator SA to custom SCC
nogueiraanderson Dec 1, 2025
c3e6ce5
fix(pmm-ha-rosa): add AWS credentials to cleanup job login stage
nogueiraanderson Dec 1, 2025
f901106
feat(pmm-ha-rosa): enhance cleanup job with list deletion and skip ne…
nogueiraanderson Dec 1, 2025
18d082c
fix(pmm-ha-rosa): improve listClusters debug output and JSON parsing
nogueiraanderson Dec 1, 2025
6b2eefe
fix(pmm-ha-rosa): set AWS_DEFAULT_REGION in listClusters function
nogueiraanderson Dec 1, 2025
1f7c220
fix(pmm-ha-rosa): use theTibi helm chart repo with PMM-14420 branch
nogueiraanderson Dec 1, 2025
16dadc9
fix(pmm-ha-rosa): use toSorted instead of sort to return list
nogueiraanderson Dec 1, 2025
af2ab89
fix(pmm-ha-rosa): use Collections.sort for Jenkins sandbox compatibility
nogueiraanderson Dec 1, 2025
e82af68
fix(pmm-ha-rosa): use Groovy sort in-place and tail() for sandbox
nogueiraanderson Dec 1, 2025
3b22ca4
fix(pmm-ha-rosa): fix sort order to properly keep newest cluster
nogueiraanderson Dec 1, 2025
7199622
fix(pmm-ha-rosa): make cluster deletion non-blocking
nogueiraanderson Dec 1, 2025
36f3168
fix(pmm-ha-rosa): use Date.parse for proper descending sort
nogueiraanderson Dec 1, 2025
ac3bad6
fix(pmm-ha-rosa): use sandbox-safe string sort for clusters
nogueiraanderson Dec 1, 2025
c827340
fix(pmm-ha-rosa): use toSorted for CPS compatibility
nogueiraanderson Dec 1, 2025
1e01c83
fix(pmm-ha-rosa): move sort to listClusters to avoid CPS issues
nogueiraanderson Dec 1, 2025
5d12ef6
fix(pmm-ha-rosa): try theTibi fork before percona for helm charts
nogueiraanderson Dec 1, 2025
87b134a
fix(pmm-ha-rosa): use chart default image with pmm-encryption-rotation
nogueiraanderson Dec 1, 2025
b997c50
fix(pmm-ha-rosa): use array join for helm args to avoid escaping issues
nogueiraanderson Dec 1, 2025
3692867
fix(pmm-ha-rosa): fix Route53 variable interpolation in shell blocks
nogueiraanderson Dec 2, 2025
8583858
fix(pmm-ha-rosa): use literal string for ECR_PREFIX static field
nogueiraanderson Dec 2, 2025
12b7d46
feat(pmm-ha-eks-cleanup): add DELETE_OLD as manual parameter option
nogueiraanderson Dec 2, 2025
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
13 changes: 7 additions & 6 deletions pmm/v3/pmm3-ha-eks-cleanup.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ pipeline {
parameters {
choice(
name: 'ACTION',
choices: ['LIST_ONLY', 'DELETE_CLUSTER', 'DELETE_ALL'],
choices: ['LIST_ONLY', 'DELETE_OLD', 'DELETE_CLUSTER', 'DELETE_ALL'],
description: '''
LIST_ONLY - list all test clusters<br/>
DELETE_OLD - delete clusters older than 24 hours<br/>
DELETE_CLUSTER - delete a specific cluster (requires CLUSTER_NAME)<br/>
DELETE_ALL - delete all test clusters<br/><br/>
Note: Daily cron automatically deletes clusters older than 1 day.
Note: Daily cron automatically runs DELETE_OLD.
'''
)
string(name: 'CLUSTER_NAME', defaultValue: '', description: 'Required only for DELETE_CLUSTER')
Expand All @@ -26,8 +27,8 @@ pipeline {
}

environment {
REGION = "us-east-2"
CLUSTER_PREFIX = "pmm-ha-test-"
REGION = 'us-east-2'
CLUSTER_PREFIX = 'pmm-ha-test-'
}

stages {
Expand All @@ -36,14 +37,14 @@ pipeline {
script {
if (currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause')) {
env.ACTION = 'DELETE_OLD'
echo "Triggered by cron - will delete clusters older than 1 day."
echo 'Triggered by cron - will delete clusters older than 1 day.'
} else {
env.ACTION = params.ACTION
echo "Manual run with ACTION=${params.ACTION}"
}

if (env.ACTION == 'DELETE_CLUSTER' && !params.CLUSTER_NAME) {
error("CLUSTER_NAME is required for DELETE_CLUSTER.")
error('CLUSTER_NAME is required for DELETE_CLUSTER.')
}
if (params.CLUSTER_NAME && !params.CLUSTER_NAME.startsWith(env.CLUSTER_PREFIX)) {
error("Cluster name must start with ${env.CLUSTER_PREFIX}")
Expand Down
328 changes: 328 additions & 0 deletions pmm/v3/pmm3-ha-rosa-cleanup.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
library changelog: false, identifier: 'lib@feature/pmm-ha-rosa', retriever: modernSCM([
$class: 'GitSCMSource',
remote: 'https://github.com/Percona-Lab/jenkins-pipelines'
])

pipeline {
agent {
label 'agent-amd64-ol9'
}

triggers {
cron('H 0,12 * * *') // Runs twice daily at 00:00 & 12:00
}

parameters {
choice(
name: 'ACTION',
choices: ['LIST_ONLY', 'DELETE_CLUSTERS', 'DELETE_ALL'],
description: '''
LIST_ONLY - list all PMM HA ROSA clusters<br/>
DELETE_CLUSTERS - delete specific clusters (comma-separated list)<br/>
DELETE_ALL - delete all PMM HA ROSA clusters<br/><br/>
Note: Daily cron automatically deletes clusters older than 1 day.
'''
)
string(
name: 'CLUSTER_NAMES',
defaultValue: '',
description: 'Comma-separated list of cluster names to delete (e.g., pmm-ha-rosa-23,pmm-ha-rosa-24)'
)
booleanParam(
name: 'SKIP_NEWEST',
defaultValue: true,
description: 'Skip the most recently created cluster (useful when a new cluster is being created)'
)
}

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

environment {
REGION = 'us-east-2'
CLUSTER_PREFIX = 'pmm-ha-rosa-'
R53_ZONE_NAME = 'cd.percona.com'
}

stages {
stage('Install CLI Tools') {
steps {
script {
pmmHaRosa.installRosaCli()
pmmHaRosa.installOcCli()
}
}
}

stage('Login to ROSA') {
steps {
withCredentials([
aws(credentialsId: 'pmm-staging-slave'),
string(credentialsId: 'REDHAT_OFFLINE_TOKEN', variable: 'ROSA_TOKEN')
]) {
script {
pmmHaRosa.login([
token: env.ROSA_TOKEN,
region: env.REGION
])
}
}
}
}

stage('Detect Run Type') {
steps {
script {
if (currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause')) {
env.ACTION = 'DELETE_OLD'
echo 'Triggered by cron - will delete clusters older than 1 day.'
} else {
env.ACTION = params.ACTION
echo "Manual run with ACTION=${params.ACTION}"
}

if (env.ACTION == 'DELETE_CLUSTERS' && !params.CLUSTER_NAMES?.trim()) {
error('CLUSTER_NAMES is required for DELETE_CLUSTERS action.')
}

// Validate cluster names if provided
if (params.CLUSTER_NAMES?.trim()) {
def names = params.CLUSTER_NAMES.split(',').collect { it.trim() }
names.each { name ->
if (!name.startsWith(env.CLUSTER_PREFIX)) {
error("Cluster name '${name}' must start with ${env.CLUSTER_PREFIX}")
}
}
}
}
}
}

stage('List Clusters') {
when { expression { env.ACTION == 'LIST_ONLY' } }
steps {
withCredentials([
aws(credentialsId: 'pmm-staging-slave'),
string(credentialsId: 'REDHAT_OFFLINE_TOKEN', variable: 'ROSA_TOKEN')
]) {
script {
def clusters = pmmHaRosa.listClusters([
region: env.REGION
])

if (clusters.isEmpty()) {
echo "No clusters found with prefix '${env.CLUSTER_PREFIX}'."
} else {
echo "Found ${clusters.size()} cluster(s):"
echo ''
clusters.each { cluster ->
def ageHours = pmmHaRosa.getClusterAgeHours(cluster.createdAt)
echo "• ${cluster.name}"
echo " State: ${cluster.state}"
echo " Version: ${cluster.version}"
echo " Region: ${cluster.region}"
echo " Age: ${ageHours}h"
echo " Created: ${cluster.createdAt}"
echo ''
}
}
}
}
}
}

stage('Delete Clusters') {
when { expression { env.ACTION == 'DELETE_CLUSTERS' } }
steps {
withCredentials([
aws(credentialsId: 'pmm-staging-slave'),
string(credentialsId: 'REDHAT_OFFLINE_TOKEN', variable: 'ROSA_TOKEN')
]) {
script {
def clusterNames = params.CLUSTER_NAMES.split(',').collect { it.trim() }
echo "Deleting ${clusterNames.size()} cluster(s): ${clusterNames.join(', ')}"

clusterNames.each { clusterName ->
echo "Deleting cluster: ${clusterName}"

// Delete Route53 record first
try {
pmmHaRosa.deleteRoute53Record([
domain: "${clusterName}.${env.R53_ZONE_NAME}"
])
} catch (Exception e) {
echo "Warning: Could not delete Route53 record for ${clusterName}: ${e.message}"
}

// Delete the ROSA cluster
try {
pmmHaRosa.deleteCluster([
clusterName: clusterName
])
echo "Cluster ${clusterName} deletion initiated."
} catch (Exception e) {
echo "Error deleting ${clusterName}: ${e.message}"
}
}

echo 'All specified clusters deletion initiated.'
}
}
}
}

stage('Delete All Clusters') {
when { expression { env.ACTION == 'DELETE_ALL' } }
steps {
withCredentials([
aws(credentialsId: 'pmm-staging-slave'),
string(credentialsId: 'REDHAT_OFFLINE_TOKEN', variable: 'ROSA_TOKEN')
]) {
script {
def clusters = pmmHaRosa.listClusters([
region: env.REGION
])

if (clusters.isEmpty()) {
echo "No clusters found with prefix '${env.CLUSTER_PREFIX}'."
return
}

// listClusters already returns clusters sorted by createdAt (newest first)
echo 'Clusters (newest first):'
clusters.each { c -> echo " - ${c.name}: ${c.createdAt}" }

// Optionally skip newest cluster
if (params.SKIP_NEWEST && clusters.size() > 1) {
def skipped = clusters[0]
echo "Skipping newest cluster: ${skipped.name} (created: ${skipped.createdAt})"
clusters = clusters.drop(1)
}

if (clusters.isEmpty()) {
echo 'No clusters to delete after applying filters.'
return
}

echo "Deleting ${clusters.size()} cluster(s)..."

clusters.each { cluster ->
if (cluster.state == 'uninstalling') {
echo "Skipping ${cluster.name} - already uninstalling"
return
}

echo "Deleting: ${cluster.name}"

// Delete Route53 record
try {
pmmHaRosa.deleteRoute53Record([
domain: "${cluster.name}.${env.R53_ZONE_NAME}"
])
} catch (Exception e) {
echo "Warning: Could not delete Route53 record: ${e.message}"
}

// Delete the cluster
try {
pmmHaRosa.deleteCluster([
clusterName: cluster.name
])
echo "Deleted: ${cluster.name}"
} catch (Exception e) {
echo "Error deleting ${cluster.name}: ${e.message}"
}
}

echo 'All clusters deletion completed.'
}
}
}
}

stage('Delete Old Clusters (cron only)') {
when { expression { env.ACTION == 'DELETE_OLD' } }
steps {
withCredentials([
aws(credentialsId: 'pmm-staging-slave'),
string(credentialsId: 'REDHAT_OFFLINE_TOKEN', variable: 'ROSA_TOKEN')
]) {
script {
def clusters = pmmHaRosa.listClusters([
region: env.REGION
])

if (clusters.isEmpty()) {
echo "No clusters found with prefix '${env.CLUSTER_PREFIX}'."
return
}

def maxAgeHours = 24
def deletedCount = 0

echo "Checking ${clusters.size()} cluster(s) for age > ${maxAgeHours}h..."

clusters.each { cluster ->
if (cluster.state == 'uninstalling') {
echo "Skipping ${cluster.name} - already uninstalling"
return
}

def ageHours = pmmHaRosa.getClusterAgeHours(cluster.createdAt)

if (ageHours > maxAgeHours) {
echo "Deleting old cluster: ${cluster.name} (age: ${ageHours}h)"

// Delete Route53 record
try {
pmmHaRosa.deleteRoute53Record([
domain: "${cluster.name}.${env.R53_ZONE_NAME}"
])
} catch (Exception e) {
echo "Warning: Could not delete Route53 record: ${e.message}"
}

// Delete the cluster
try {
pmmHaRosa.deleteCluster([
clusterName: cluster.name
])
deletedCount++
echo "Deleted: ${cluster.name}"
} catch (Exception e) {
echo "Error deleting ${cluster.name}: ${e.message}"
}
} else {
echo "Skipping recent cluster: ${cluster.name} (age: ${ageHours}h < ${maxAgeHours}h)"
}
}

echo "Cleanup complete. Deleted ${deletedCount} cluster(s)."
}
}
}
}
}

post {
always {
script {
// Final cluster count
try {
withCredentials([
aws(credentialsId: 'pmm-staging-slave'),
string(credentialsId: 'REDHAT_OFFLINE_TOKEN', variable: 'ROSA_TOKEN')
]) {
pmmHaRosa.login([token: env.ROSA_TOKEN])
def remaining = pmmHaRosa.listClusters([region: env.REGION])
echo "Remaining PMM HA ROSA clusters: ${remaining.size()}"
}
} catch (Exception e) {
echo "Could not get final cluster count: ${e.message}"
}
}
}
}
}
Loading