From c333f9000d8578841a94aab97d8d2787c8eda411 Mon Sep 17 00:00:00 2001
From: Yuxiang Zhu <vfreex@gmail.com>
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
+}

