From c333f9000d8578841a94aab97d8d2787c8eda411 Mon Sep 17 00:00:00 2001 From: Yuxiang Zhu Date: Aug 02 2019 04:07:37 +0000 Subject: Add C3I-as-a-Service job c3iaas-cleanup-projects to clean up expired projects --- diff --git a/c3iaas/jobs/c3iaas-cleanup-projects.env b/c3iaas/jobs/c3iaas-cleanup-projects.env new file mode 100644 index 0000000..b656574 --- /dev/null +++ b/c3iaas/jobs/c3iaas-cleanup-projects.env @@ -0,0 +1,2 @@ +NAME=c3iaas-cleanup-projects +CRON_EXPRESSION="H/10 * * * *" diff --git a/c3iaas/jobs/c3iaas-cleanup-projects.tmpl b/c3iaas/jobs/c3iaas-cleanup-projects.tmpl new file mode 120000 index 0000000..3dc7a32 --- /dev/null +++ b/c3iaas/jobs/c3iaas-cleanup-projects.tmpl @@ -0,0 +1 @@ +../templates/c3iaas-cleanup-projects-template.yaml \ No newline at end of file diff --git a/c3iaas/templates/c3iaas-cleanup-projects-template.yaml b/c3iaas/templates/c3iaas-cleanup-projects-template.yaml new file mode 100644 index 0000000..0a7c891 --- /dev/null +++ b/c3iaas/templates/c3iaas-cleanup-projects-template.yaml @@ -0,0 +1,62 @@ +apiVersion: v1 +kind: Template +metadata: + name: c3iaas-cleanup-projects +parameters: +- name: NAME + displayName: Short unique identifier for the templated instances + required: true + value: c3iaas-cleanup-projects +- name: GIT_REPO + displayName: Pipeline Git repository + required: true + value: "https://pagure.io/c3i-library.git" +- name: GIT_REF + displayName: Pipeline Git ref + required: true + value: master +- name: CRON_EXPRESSION + displayName: A string in cron syntax that defines the frequency to run this job + required: true + value: "H/10 * * * *" +- name: JENKINS_AGENT_IMAGE + displayName: Jenkins agent container image + required: true + value: docker-registry.engineering.redhat.com/factory2/factory2-integration-test-jenkins-slave:latest +- name: JENKINS_AGENT_SERVICE_ACCOUNT + displayName: service account for a Jenkins agent to interact with OpenShift API server + required: true + value: jenkins +- name: JENKINS_AGENT_CLOUD + displayName: OpenShift cloud to run a Jenkins agent + required: true + value: openshift +- name: JENKINS_AGENT_NAMESPACE + displayName: OpenShift namespace to run a Jenkins agent +labels: + template: c3iaas-cleanup-projects + app: c3iaas + component: project-manager +objects: +- kind: "BuildConfig" + apiVersion: "v1" + metadata: + name: "${NAME}" + spec: + runPolicy: "Parallel" + completionDeadlineSeconds: 600 + strategy: + type: JenkinsPipeline + jenkinsPipelineStrategy: + jenkinsfile: |- + library identifier: 'c3i@${GIT_REF}', changelog: false, + retriever: modernSCM([$class: 'GitSCMSource', remote: '${GIT_REPO}']) + c3iaasCleanupProjectsJob( + agent: [ + cloud: '${JENKINS_AGENT_CLOUD}', + namespace: '${JENKINS_AGENT_NAMESPACE}', + serviceAccount: '${JENKINS_AGENT_SERVICE_ACCOUNT}', + image: '${JENKINS_AGENT_IMAGE}', + ], + cronExpression: '${CRON_EXPRESSION}', + ) diff --git a/vars/c3iaasCleanupProjectsJob.groovy b/vars/c3iaasCleanupProjectsJob.groovy new file mode 100644 index 0000000..8f9a5d2 --- /dev/null +++ b/vars/c3iaasCleanupProjectsJob.groovy @@ -0,0 +1,108 @@ +import groovy.transform.Field +import groovy.json.JsonOutput +import java.time.Instant +import java.time.temporal.ChronoUnit + +@Field final PROJECT_NAME_PATTERN = /c3i\-[a-z0-9\-]{0,48}[a-z0-9]/ +@Field final USER_NAME_PATTERN = /[\w:\-]+/ + +def call(Map args=[:]) { + pipeline { + triggers{ cron(args.cronExpression) } + options { + timestamps() + buildDiscarder(logRotator(numToKeepStr: '10')) + } + agent { + kubernetes { + cloud args.agent.cloud + label "jenkins-slave-${UUID.randomUUID().toString()}" + namespace args.agent.namespace + serviceAccount args.agent.serviceAccount + defaultContainer 'jnlp' + yaml """ + apiVersion: v1 + kind: Pod + metadata: + labels: + app: c3iaas + component: project-manager + pipeline-job-name: "${env.JOB_BASE_NAME}" + pipeline-build-number: "${env.BUILD_NUMBER}" + spec: + containers: + - name: jnlp + image: ${args.agent.image} + imagePullPolicy: Always + tty: true + resources: + requests: + memory: 256Mi + cpu: 200m + limits: + memory: 512Mi + cpu: 356m + """ + } + } + stages { + stage("Clean up") { + steps { + script { + openshift.withCluster() { + def projects = openshift.selector('project') + projects.withEach { projectSelector -> + def projectName = projectSelector.name() + if (!projectName.startsWith('project/c3i-')) { + // Skip because C3IaaS created projects are always prefixed with 'c3i-' + return + } + def project = projectSelector.object() + echo "Checking if project $projectName is managed by C3IaaS..." + if (!isProjectManagedByC3IaaS(project)) { + echo "Project $projectName is NOT managed by C3IaaS" + return + } + try { + deleteProjectIfExpired(project) + } catch(e) { + echo "Couldn't delete project $projectName: $e" + } + } + } + } + } + } + } + } +} + +def isProjectManagedByC3IaaS(project) { + def projectName = project.metadata.name + def projectMetadataString = project.metadata.annotations["openshift.io/description"] + try { + projectMetadata = readJSON text: projectMetadataString + if (projectMetadata["c3iaas.redhat.com/managed_project"] != "true") { + echo 'c3iaas.redhat.com/managed_project is not "true"' + return false + } + } catch (e) { + echo("Couldn't parse C3IaaS metadata: $e") + return false + } + return true +} + +def deleteProjectIfExpired(project) { + def projectName = project.metadata.name + def projectMetadataString = project.metadata.annotations["openshift.io/description"] + Instant expirationInstant = Instant.parse(projectMetadata['c3iaas.redhat.com/expiration_datetime']) + if (Instant.now().compareTo(expirationInstant) <= 0) { + echo "Project $projectName will not be deleted until $expirationInstant" + return false + } + echo "Deleting project $projectName..." + openshift.selector('project', projectName).delete() + echo "Project $projectName deleted" + return true +}