From c15c4a83fcc76ee97899a46218d5fa86911057f5 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Fri, 16 Feb 2018 14:41:02 -0500 Subject: [PATCH 01/45] Initial commit of multi-cluster pipeline so I can start testing --- multi-cluster-sprint-boot/Jenkinsfile | 120 ++++++++++++++ multi-cluster-sprint-boot/README.md | 154 ++++++++++++++++++ .../inventory/group_vars/seed-hosts.yml | 22 +++ .../applier/inventory/hosts | 2 + .../applier/params/build-dev | 2 + .../applier/params/deployment-dev | 5 + .../applier/params/deployment-prod | 5 + .../applier/params/deployment-stage | 6 + .../applier/projects/projects.yml | 21 +++ .../applier/templates/build.yml | 111 +++++++++++++ .../applier/templates/deployment.yml | 145 +++++++++++++++++ 11 files changed, 593 insertions(+) create mode 100644 multi-cluster-sprint-boot/Jenkinsfile create mode 100644 multi-cluster-sprint-boot/README.md create mode 100644 multi-cluster-sprint-boot/applier/inventory/group_vars/seed-hosts.yml create mode 100644 multi-cluster-sprint-boot/applier/inventory/hosts create mode 100644 multi-cluster-sprint-boot/applier/params/build-dev create mode 100644 multi-cluster-sprint-boot/applier/params/deployment-dev create mode 100644 multi-cluster-sprint-boot/applier/params/deployment-prod create mode 100644 multi-cluster-sprint-boot/applier/params/deployment-stage create mode 100644 multi-cluster-sprint-boot/applier/projects/projects.yml create mode 100644 multi-cluster-sprint-boot/applier/templates/build.yml create mode 100644 multi-cluster-sprint-boot/applier/templates/deployment.yml diff --git a/multi-cluster-sprint-boot/Jenkinsfile b/multi-cluster-sprint-boot/Jenkinsfile new file mode 100644 index 00000000..9e94b1f9 --- /dev/null +++ b/multi-cluster-sprint-boot/Jenkinsfile @@ -0,0 +1,120 @@ +#!/usr/bin/groovy + +//// +// This pipeline requires the following plugins: +// Kubernetes Plugin 0.10 +//// + +String ocpApiServer = env.OCP_API_SERVER ? "${env.OCP_API_SERVER}" : "https://openshift.default.svc.cluster.local" + +node('master') { + + env.NAMESPACE = readFile('/var/run/secrets/kubernetes.io/serviceaccount/namespace').trim() + env.TOKEN = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() + env.OC_CMD = "oc --token=${env.TOKEN} --server=${ocpApiServer} --certificate-authority=/run/secrets/kubernetes.io/serviceaccount/ca.crt --namespace=${env.NAMESPACE}" + + env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '') + def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') + env.STAGE1 = "${projectBase}-dev" + env.STAGE2 = "${projectBase}-stage" + env.STAGE3 = "${projectBase}-prod" + +} + +node('maven') { + def mvnCmd = 'mvn' + String pomFileLocation = env.BUILD_CONTEXT_DIR ? "${env.BUILD_CONTEXT_DIR}/pom.xml" : "pom.xml" + + stage('SCM Checkout') { + checkout scm + } + + stage('Build') { + + sh "${mvnCmd} clean install -DskipTests=true -f ${pomFileLocation}" + + } + + stage('Unit Test') { + + sh "${mvnCmd} test -f ${pomFileLocation}" + + } + + + stage('Build Image') { + + sh """ + rm -rf oc-build && mkdir -p oc-build/deployments + for t in \$(echo "jar;war;ear" | tr ";" "\\n"); do + cp -rfv ./target/*.\$t oc-build/deployments/ 2> /dev/null || echo "No \$t files" + done + ${env.OC_CMD} start-build ${env.APP_NAME} --from-dir=oc-build --wait=true --follow=true || exit 1 + """ + } + + stage("Verify Deployment to ${env.STAGE1}") { + + openshiftVerifyDeployment(deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE1}", verifyReplicaCount: true) + + input "Promote Application to Stage?" + } + + stage("Promote To ${env.STAGE2}") { + sh """ + ${env.OC_CMD} tag ${env.STAGE1}/${env.APP_NAME}:latest ${env.STAGE2}/${env.APP_NAME}:latest + """ + } + + stage("Verify Deployment to ${env.STAGE2}") { + + openshiftVerifyDeployment(deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE2}", verifyReplicaCount: true) + + input "Promote Application to Prod?" + } + +} + +podTemplate(label: 'promotion-slave', cloud: 'openshift', serviceAccount: "jenkins", containers: [ + containerTemplate(name: 'jenkins-slave-image-mgmt', image: "${env.SKOPEO_SLAVE_IMAGE}", ttyEnabled: true, command: 'cat'), + containerTemplate(name: 'jnlp', image: 'jenkinsci/jnlp-slave:2.62-alpine', args: '${computer.jnlpmac} ${computer.name}') +]) { + + node('promotion-slave') { + + sh""" + oc version + oc get is jenkins-slave-image-mgmt -o jsonpath='{ .status.dockerImageRepository }' > /tmp/jenkins-slave-image-mgmt.out; + oc get secret prod-credentials -o jsonpath='{ .data.api }' | base64 --decode > /tmp/prod_api; + oc get secret prod-credentials -o jsonpath='{ .data.registry }' | base64 --decode > /tmp/prod_registry + oc get secret prod-credentials -o jsonpath='{ .data.token }' | base64 --decode > /tmp/prod_token + """ + env.SKOPEO_SLAVE_IMAGE = readFile('/tmp/jenkins-slave-image-mgmt.out').trim() + env.PROD_API= readFile('/tmp/prod_api').trim() + env.PROD_REGISTRY = readFile('/tmp/prod_registry').trim() + env.PROD_TOKEN = readFile('/tmp/prod_token').trim() + + stage("Promote To ${env.STAGE3}") { + + container('jenkins-slave-image-mgmt') { + sh """ + + imageRegistry=\$(oc get is ${env.APP_NAME} --template='{{ .status.dockerImageRepository }}' -n ${env.STAGE2} | cut -d/ -f1) + + strippedNamespace=\$(echo ${env.NAMESPACE} | cut -d/ -f1) + + echo "Promoting \${imageRegistry}/${env.STAGE2}/${env.APP_NAME} -> \${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME}" + skopeo --tls-verify=false copy --remove-signatures --src-creds openshift:${env.TOKEN} --dest-creds openshift:${env.PROD_TOKEN} docker://\${imageRegistry}/${env.STAGE2}/${env.APP_NAME} docker://${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME} + """ + } + } + + stage("Verify Deployment to ${env.STAGE3}") { + + openshiftVerifyDeployment(apiURL: "${PROD_API}", authToken: "${PROD_TOKEN}" deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE3}", verifyReplicaCount: true) + + } + } +} + +println "Application ${env.APP_NAME} is now in Production!" diff --git a/multi-cluster-sprint-boot/README.md b/multi-cluster-sprint-boot/README.md new file mode 100644 index 00000000..ab9b272f --- /dev/null +++ b/multi-cluster-sprint-boot/README.md @@ -0,0 +1,154 @@ +# A Sample OpenShift Pipeline for a Spring Boot Application + +This example demonstrates how to implement a full end-to-end Jenkins Pipeline for a Java application in OpenShift Container Platform. This sample demonstrates the following capabilities: + +* Deploying an integrated Jenkins server inside of OpenShift +* Running both custom and oob Jenkins slaves as pods in OpenShift +* "One Click" instantiation of a Jenkins Pipeline using OpenShift's Jenkins Pipeline Strategy feature +* Promotion of an application's container image within an OpenShift Cluster (using `oc tag`) +* Promotion of an application's container image to a separate OpenShift Cluster (using `skopeo`) - Coming Soon! +* Automated rollout using the [openshift-appler](https://github.com/redhat-cop/casl-ansible/tree/master/roles/openshift-applier) Ansible role. + +## Automated Quickstart + +This quickstart can be deployed quickly using Ansible. Here are the steps. + +1. Clone [this repo](https://github.com/redhat-cop/container-pipelines.git) and the [casl-ansible](https://github.com/redhat-cop/casl-ansible.git) repo. +2. Log into an OpenShift cluster, then run the following command. +``` +$ oc login +$ ansible-playbook -i ./applier/inventory/ /path/to/casl-ansible/playbooks/openshift-cluster-seed.yml +``` + +At this point you should have 3 projects deployed (`basic-spring-boot-dev`, `basic-spring-boot-stage`, and `basic-spring-boot-prod`) with our [Spring Rest](https://github.com/redhat-cop/spring-rest.git) demo application deployed to all 3. + +## Architecture + +The following breaks down the architecture of the pipeline deployed, as well as walks through the manual deployment steps + +### OpenShift Templates + +The components of this pipeline are divided into two templates. + +The first template, `applier/templates/build.yml` is what we are calling the "Build" template. It contains: + +* A `jenkinsPipelineStrategy` BuildConfig +* An `s2i` BuildConfig +* An ImageStream for the s2i build config to push to + +The build template contains a default source code repo for a java application compatible with this pipelines architecture (https://github.com/redhat-cop/spring-rest). + +The second template, `applier/templates/deployment.yml` is the "Deploy" template. It contains: + +* A tomcat8 DeploymentConfig +* A Service definition +* A Route + +The idea behind the split between the templates is that I can deploy the build template only once (to my dev project) and that the pipeline will promote my image through all of the various stages of my application's lifecycle. The deployment template gets deployed once to each of the stages of the application lifecycle (once per OpenShift project). + +### Pipeline Script + +This project includes a sample `Jenkinsfile` pipeline script that could be included with a Java project in order to implement a basic CI/CD pipeline for that project, under the following assumptions: + +* The project is built with Maven +* The `Jenkinsfile` script is placed in the same directory as the `pom.xml` file in the git source. +* The OpenShift projects that represent the Application's lifecycle stages are of the naming format: `-dev`, `-stage`, `-prod`. + +For convenience, this pipeline script is already included in the following git repository, based on our [Spring Boot Demo App](https://github.com/redhat-cop/spring-rest) app. + +https://github.com/redhat-cop/spring-rest + +## Bill of Materials + +* One or Two OpenShift Container Platform Clusters + * OpenShift 3.5+ is required. +* Access to GitHub + +## Manual Deployment Instructions + +### 1. Create Lifecycle Stages + +For the purposes of this demo, we are going to create three stages for our application to be promoted through. + +- `basic-spring-boot-dev` +- `basic-spring-boot-stage` +- `basic-spring-boot-prod` + +In the spirit of _Infrastructure as Code_ we have a YAML file that defines the `ProjectRequests` for us. This is as an alternative to running `oc new-project`, but will yeild the same result. + +``` +$ oc create -f applier/projects/projects.yml +projectrequest "basic-spring-boot-dev" created +projectrequest "basic-spring-boot-stage" created +projectrequest "basic-spring-boot-prod" created +``` + +### 2. Stand up Jenkins master in dev + +For this step, the OpenShift default template set provides exactly what we need to get jenkins up and running. + +``` +$ oc process openshift//jenkins-ephemeral | oc apply -f- -n basic-spring-boot-dev +route "jenkins" created +deploymentconfig "jenkins" created +serviceaccount "jenkins" created +rolebinding "jenkins_edit" created +service "jenkins-jnlp" created +service "jenkins" created +``` + +### 4. Instantiate Pipeline + +A _deploy template_ is provided at `applier/templates/deployment.yml` that defines all of the resources required to run our Tomcat application. It includes: + +* A `Service` +* A `Route` +* An `ImageStream` +* A `DeploymentConfig` +* A `RoleBinding` to allow Jenkins to deploy in each namespace. + +This template should be instantiated once in each of the namespaces that our app will be deployed to. For this purpose, we have created a param file to be fed to `oc process` to customize the template for each environment. + +Deploy the deployment template to all three projects. +``` +$ oc process -f applier/templates/deployment.yml --param-file=applier/params/deployment-dev | oc apply -f- +service "spring-rest" created +route "spring-rest" created +imagestream "spring-rest" created +deploymentconfig "spring-rest" created +rolebinding "jenkins_edit" configured +$ oc process -f applier/templates/deployment.yml --param-file=applier/params/deployments-stage | oc apply -f- +service "spring-rest" created +route "spring-rest" created +imagestream "spring-rest" created +deploymentconfig "spring-rest" created +rolebinding "jenkins_edit" created +$ oc process -f applier/templates/deployment.yml --param-file=applier/params/deployment-prod | oc apply -f- +service "spring-rest" created +route "spring-rest" created +imagestream "spring-rest" created +deploymentconfig "spring-rest" created +rolebinding "jenkins_edit" created +``` + +A _build template_ is provided at `applier/templates/build.yml` that defines all the resources required to build our java app. It includes: + +* A `BuildConfig` that defines a `JenkinsPipelineStrategy` build, which will be used to define out pipeline. +* A `BuildConfig` that defines a `Source` build with `Binary` input. This will build our image. + +Deploy the pipeline template in dev only. +``` +$ oc process -f applier/templates/build.yml --param-file applier/params/build-dev | oc apply -f- +buildconfig "spring-rest-pipeline" created +buildconfig "spring-rest" created +``` + +At this point you should be able to go to the Web Console and follow the pipeline by clicking in your `basic-spring-boot-dev` project, and going to *Builds* -> *Pipelines*. At several points you will be prompted for input on the pipeline. You can interact with it by clicking on the _input required_ link, which takes you to Jenkins, where you can click the *Proceed* button. By the time you get through the end of the pipeline you should be able to visit the Route for your app deployed to the `myapp-prod` project to confirm that your image has been promoted through all stages. + +## Cleanup + +Cleaning up this example is as simple as deleting the projects we created at the beginning. + +``` +oc delete project basic-spring-boot-dev basic-spring-boot-prod basic-spring-boot-stage +``` diff --git a/multi-cluster-sprint-boot/applier/inventory/group_vars/seed-hosts.yml b/multi-cluster-sprint-boot/applier/inventory/group_vars/seed-hosts.yml new file mode 100644 index 00000000..9d58b500 --- /dev/null +++ b/multi-cluster-sprint-boot/applier/inventory/group_vars/seed-hosts.yml @@ -0,0 +1,22 @@ +openshift_cluster_content: +- object: projects + content: + - name: "create environments" + file: "{{ inventory_dir }}/../projects/projects.yml" + file_action: create +- object: deployments + content: + - name: "deploy dev environment" + template: "{{ inventory_dir }}/../templates/deployment.yml" + params: "{{ inventory_dir }}/../params/deployment-dev" + - name: "deply stage environment" + template: "{{ inventory_dir }}/../templates/deployment.yml" + params: "{{ inventory_dir }}/../params/deployment-stage" + - name: "deply prod environment" + template: "{{ inventory_dir }}/../templates/deployment.yml" + params: "{{ inventory_dir }}/../params/deployment-prod" +- object: builds + content: + - name: "deploy build pipeline to dev" + template: "{{ inventory_dir }}/../templates/build.yml" + params: "{{ inventory_dir }}/../params/build-dev" diff --git a/multi-cluster-sprint-boot/applier/inventory/hosts b/multi-cluster-sprint-boot/applier/inventory/hosts new file mode 100644 index 00000000..7f325c4b --- /dev/null +++ b/multi-cluster-sprint-boot/applier/inventory/hosts @@ -0,0 +1,2 @@ +[seed-hosts] +localhost ansible_connection=local diff --git a/multi-cluster-sprint-boot/applier/params/build-dev b/multi-cluster-sprint-boot/applier/params/build-dev new file mode 100644 index 00000000..81346c55 --- /dev/null +++ b/multi-cluster-sprint-boot/applier/params/build-dev @@ -0,0 +1,2 @@ +APPLICATION_NAME=spring-rest +NAMESPACE=multicluster-spring-boot-dev diff --git a/multi-cluster-sprint-boot/applier/params/deployment-dev b/multi-cluster-sprint-boot/applier/params/deployment-dev new file mode 100644 index 00000000..c13fb0f4 --- /dev/null +++ b/multi-cluster-sprint-boot/applier/params/deployment-dev @@ -0,0 +1,5 @@ +APPLICATION_NAME=spring-rest +NAMESPACE=multicluster-spring-boot-dev +SA_NAMESPACE=multicluster-spring-boot-dev +READINESS_RESPONSE=status.:.UP +READINESS_PATH=/health diff --git a/multi-cluster-sprint-boot/applier/params/deployment-prod b/multi-cluster-sprint-boot/applier/params/deployment-prod new file mode 100644 index 00000000..1f4db956 --- /dev/null +++ b/multi-cluster-sprint-boot/applier/params/deployment-prod @@ -0,0 +1,5 @@ +APPLICATION_NAME=spring-rest +NAMESPACE=multicluster-spring-boot-prod +SA_NAMESPACE=multicluster-spring-boot-dev +READINESS_RESPONSE=status.:.UP +READINESS_PATH=/health diff --git a/multi-cluster-sprint-boot/applier/params/deployment-stage b/multi-cluster-sprint-boot/applier/params/deployment-stage new file mode 100644 index 00000000..326a8b1b --- /dev/null +++ b/multi-cluster-sprint-boot/applier/params/deployment-stage @@ -0,0 +1,6 @@ +APPLICATION_NAME=spring-rest +NAMESPACE=multicluster-spring-boot-stage +SA_NAME=jenkins +SA_NAMESPACE=multicluster-spring-boot-dev +READINESS_RESPONSE=status.:.UP +READINESS_PATH=/health diff --git a/multi-cluster-sprint-boot/applier/projects/projects.yml b/multi-cluster-sprint-boot/applier/projects/projects.yml new file mode 100644 index 00000000..7d5eef1a --- /dev/null +++ b/multi-cluster-sprint-boot/applier/projects/projects.yml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: List +items: +- kind: ProjectRequest + apiVersion: v1 + metadata: + name: multicluster-spring-boot-dev + creationTimestam: null + displayName: Dev - Spring Rest App +- kind: ProjectRequest + apiVersion: v1 + metadata: + name: multicluster-spring-boot-stage + creationTimestam: null + displayName: Staging - Spring Rest App +- kind: ProjectRequest + apiVersion: v1 + metadata: + name: multicluster-spring-boot-prod + creationTimestam: null + displayName: Prod - Spring Rest App diff --git a/multi-cluster-sprint-boot/applier/templates/build.yml b/multi-cluster-sprint-boot/applier/templates/build.yml new file mode 100644 index 00000000..c23c17a3 --- /dev/null +++ b/multi-cluster-sprint-boot/applier/templates/build.yml @@ -0,0 +1,111 @@ +apiVersion: v1 +kind: Template +labels: + template: generic-java-jenkins-pipeline +metadata: + annotations: + description: Application template for JWS applications built using a Jenkins Pipeline + iconClass: icon-tomcat + tags: tomcat,tomcat8,java,jboss,xpaas,jenkins-ci + version: 1.2.0 + name: generic-java-jenkins-pipeline +objects: +- kind: "BuildConfig" + apiVersion: "v1" + metadata: + labels: + application: ${APPLICATION_NAME} + name: "${APPLICATION_NAME}-pipeline" + namespace: "${NAMESPACE}" + spec: + source: + type: Git + git: + uri: ${PIPELINE_REPOSITORY_URL} + ref: ${PIPELINE_REPOSITORY_REF} + contextDir: ${PIPELINE_REPOSITORY_CONTEXT_DIR} + triggers: + - type: "GitHub" + github: + secret: ${GITHUB_WEBHOOK_SECRET} + - type: "ConfigChange" + strategy: + type: "JenkinsPipeline" + jenkinsPipelineStrategy: + jenkinsfilePath: ${PIPELINE_SCRIPT} + env: + - name: SOURCE_CODE_URL + value: ${SOURCE_CODE_URL} + - name: SOURCE_CODE_BRANCH + value: ${SOURCE_CODE_BRANCH} +- apiVersion: v1 + kind: BuildConfig + metadata: + labels: + application: ${APPLICATION_NAME} + name: ${APPLICATION_NAME} + namespace: "${NAMESPACE}" + spec: + output: + to: + kind: ImageStreamTag + name: ${APPLICATION_NAME}:latest + source: + binary: {} + type: Binary + strategy: + sourceStrategy: + from: + kind: ImageStreamTag + name: ${IMAGE_STREAM_TAG_NAME} + namespace: ${IMAGE_STREAM_NAMESPACE} + type: Source +parameters: +- description: The name for the application. + name: APPLICATION_NAME + required: true + value: basic-spring +- description: The namespace to deploy into + name: NAMESPACE + required: true +- description: Git source URI for application + name: PIPELINE_REPOSITORY_URL + required: true + value: https://github.com/redhat-cop/container-pipelines.git +- description: Git branch/tag reference + name: PIPELINE_REPOSITORY_REF + value: "master" +- description: Path within Git project to build; empty for root project directory. + name: PIPELINE_REPOSITORY_CONTEXT_DIR + value: +- description: Path within Git project pointing to the pipeline run script + name: PIPELINE_SCRIPT + value: Jenkinsfile +- description: Git source URI for application + name: SOURCE_CODE_URL + required: true + value: https://github.com/redhat-cop/spring-rest.git +- description: Git branch/tag reference + name: SOURCE_CODE_REF + value: "master" +- description: GitHub trigger secret + from: '[a-zA-Z0-9]{8}' + generate: expression + name: GITHUB_WEBHOOK_SECRET + required: true +- description: Generic build trigger secret + from: '[a-zA-Z0-9]{8}' + generate: expression + name: GENERIC_WEBHOOK_SECRET + required: true +- description: Namespace in which the ImageStreams for Red Hat Middleware images are + installed. These ImageStreams are normally installed in the openshift namespace. + You should only need to modify this if you've installed the ImageStreams in a + different namespace/project. + name: IMAGE_STREAM_NAMESPACE + required: true + value: openshift +- description: Image stream tag for the image you'd like to use to build the application + name: IMAGE_STREAM_TAG_NAME + required: true + value: redhat-openjdk18-openshift:1.1 diff --git a/multi-cluster-sprint-boot/applier/templates/deployment.yml b/multi-cluster-sprint-boot/applier/templates/deployment.yml new file mode 100644 index 00000000..b981ae5c --- /dev/null +++ b/multi-cluster-sprint-boot/applier/templates/deployment.yml @@ -0,0 +1,145 @@ +apiVersion: v1 +kind: Template +labels: + template: basic-spring-boot +metadata: + annotations: + description: Application template for JWS applications built using a Jenkins Pipeline + iconClass: icon-tomcat + tags: java,spring + version: 1.2.0 + name: basic-sprint-boot +objects: +- apiVersion: v1 + kind: Service + metadata: + annotations: + description: The web server's http port. + labels: + application: ${APPLICATION_NAME} + name: ${APPLICATION_NAME} + namespace: ${NAMESPACE} + spec: + ports: + - port: 8080 + targetPort: 8080 + selector: + deploymentConfig: ${APPLICATION_NAME} +- apiVersion: v1 + id: ${APPLICATION_NAME}-http + kind: Route + metadata: + annotations: + description: Route for application's http service. + labels: + application: ${APPLICATION_NAME} + name: ${APPLICATION_NAME} + namespace: ${NAMESPACE} + spec: + host: ${HOSTNAME_HTTP} + to: + name: ${APPLICATION_NAME} +- apiVersion: v1 + kind: ImageStream + metadata: + labels: + application: ${APPLICATION_NAME} + name: ${APPLICATION_NAME} + namespace: ${NAMESPACE} +- apiVersion: v1 + kind: DeploymentConfig + metadata: + labels: + application: ${APPLICATION_NAME} + name: ${APPLICATION_NAME} + namespace: ${NAMESPACE} + spec: + replicas: 1 + selector: + deploymentConfig: ${APPLICATION_NAME} + strategy: + type: Recreate + template: + metadata: + labels: + application: ${APPLICATION_NAME} + deploymentConfig: ${APPLICATION_NAME} + name: ${APPLICATION_NAME} + spec: + containers: + - env: + - name: JWS_ADMIN_USERNAME + value: ${JWS_ADMIN_USERNAME} + - name: JWS_ADMIN_PASSWORD + value: ${JWS_ADMIN_PASSWORD} + image: ${APPLICATION_NAME} + imagePullPolicy: Always + name: ${APPLICATION_NAME} + ports: + - containerPort: 8778 + name: jolokia + protocol: TCP + - containerPort: 8080 + name: http + protocol: TCP + readinessProbe: + exec: + command: + - /bin/bash + - -c + - curl -s 'http://localhost:8080${READINESS_PATH}' + |grep -iq '${READINESS_RESPONSE}' + terminationGracePeriodSeconds: 60 + triggers: + - imageChangeParams: + automatic: true + containerNames: + - ${APPLICATION_NAME} + from: + kind: ImageStreamTag + name: ${APPLICATION_NAME}:latest + type: ImageChange + - type: ConfigChange +- apiVersion: v1 + groupNames: null + kind: RoleBinding + metadata: + creationTimestamp: null + labels: + template: basic-tomcat-template + name: jenkins_edit + namespace: ${NAMESPACE} + roleRef: + name: edit + subjects: + - kind: ServiceAccount + name: ${SA_NAME} + namespace: ${SA_NAMESPACE} + userNames: + - system:serviceaccount:${SA_NAMESPACE}:${SA_NAME} +parameters: +- description: The name for the application. + name: APPLICATION_NAME + required: true + value: jws-app +- description: The namespace to deploy into + name: NAMESPACE + required: true +- description: Name of a service account that can deploy to this project + name: SA_NAME + required: true + value: jenkins +- description: Namespace of service account that can deploy to this project + name: SA_NAMESPACE + required: true +- description: 'Custom hostname for http service route. Leave blank for default hostname, + e.g.: -.' + name: HOSTNAME_HTTP +- description: 'URI to check for app health' + name: READINESS_PATH + required: true + value: '/' +- description: 'String value expected back from readiness check' + name: READINESS_RESPONSE + required: true + value: 'Hello World!' From 2618826a01e48a3de8a93f682590bec18ec15312 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Fri, 16 Feb 2018 14:44:42 -0500 Subject: [PATCH 02/45] Renaming directory --- .../Jenkinsfile | 0 .../README.md | 0 .../applier/inventory/group_vars/seed-hosts.yml | 0 .../applier/inventory/hosts | 0 multi-cluster-spring-boot/applier/params/build-dev | 5 +++++ .../applier/params/deployment-dev | 0 .../applier/params/deployment-prod | 0 .../applier/params/deployment-stage | 0 .../applier/projects/projects.yml | 0 .../applier/templates/build.yml | 0 .../applier/templates/deployment.yml | 0 multi-cluster-sprint-boot/applier/params/build-dev | 2 -- 12 files changed, 5 insertions(+), 2 deletions(-) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/Jenkinsfile (100%) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/README.md (100%) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/applier/inventory/group_vars/seed-hosts.yml (100%) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/applier/inventory/hosts (100%) create mode 100644 multi-cluster-spring-boot/applier/params/build-dev rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/applier/params/deployment-dev (100%) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/applier/params/deployment-prod (100%) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/applier/params/deployment-stage (100%) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/applier/projects/projects.yml (100%) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/applier/templates/build.yml (100%) rename {multi-cluster-sprint-boot => multi-cluster-spring-boot}/applier/templates/deployment.yml (100%) delete mode 100644 multi-cluster-sprint-boot/applier/params/build-dev diff --git a/multi-cluster-sprint-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile similarity index 100% rename from multi-cluster-sprint-boot/Jenkinsfile rename to multi-cluster-spring-boot/Jenkinsfile diff --git a/multi-cluster-sprint-boot/README.md b/multi-cluster-spring-boot/README.md similarity index 100% rename from multi-cluster-sprint-boot/README.md rename to multi-cluster-spring-boot/README.md diff --git a/multi-cluster-sprint-boot/applier/inventory/group_vars/seed-hosts.yml b/multi-cluster-spring-boot/applier/inventory/group_vars/seed-hosts.yml similarity index 100% rename from multi-cluster-sprint-boot/applier/inventory/group_vars/seed-hosts.yml rename to multi-cluster-spring-boot/applier/inventory/group_vars/seed-hosts.yml diff --git a/multi-cluster-sprint-boot/applier/inventory/hosts b/multi-cluster-spring-boot/applier/inventory/hosts similarity index 100% rename from multi-cluster-sprint-boot/applier/inventory/hosts rename to multi-cluster-spring-boot/applier/inventory/hosts diff --git a/multi-cluster-spring-boot/applier/params/build-dev b/multi-cluster-spring-boot/applier/params/build-dev new file mode 100644 index 00000000..e1eaf44a --- /dev/null +++ b/multi-cluster-spring-boot/applier/params/build-dev @@ -0,0 +1,5 @@ +APPLICATION_NAME=spring-rest +NAMESPACE=multicluster-spring-boot-dev +PIPELINE_REPOSITORY_URL=https://github.com/etsauer/container-pipelines.git +PIPELINE_REPOSITORY_REF=multi-cluster-promotion +PIPELINE_REPOSITORY_CONTEXT_DIR=multicluster-spring-boot diff --git a/multi-cluster-sprint-boot/applier/params/deployment-dev b/multi-cluster-spring-boot/applier/params/deployment-dev similarity index 100% rename from multi-cluster-sprint-boot/applier/params/deployment-dev rename to multi-cluster-spring-boot/applier/params/deployment-dev diff --git a/multi-cluster-sprint-boot/applier/params/deployment-prod b/multi-cluster-spring-boot/applier/params/deployment-prod similarity index 100% rename from multi-cluster-sprint-boot/applier/params/deployment-prod rename to multi-cluster-spring-boot/applier/params/deployment-prod diff --git a/multi-cluster-sprint-boot/applier/params/deployment-stage b/multi-cluster-spring-boot/applier/params/deployment-stage similarity index 100% rename from multi-cluster-sprint-boot/applier/params/deployment-stage rename to multi-cluster-spring-boot/applier/params/deployment-stage diff --git a/multi-cluster-sprint-boot/applier/projects/projects.yml b/multi-cluster-spring-boot/applier/projects/projects.yml similarity index 100% rename from multi-cluster-sprint-boot/applier/projects/projects.yml rename to multi-cluster-spring-boot/applier/projects/projects.yml diff --git a/multi-cluster-sprint-boot/applier/templates/build.yml b/multi-cluster-spring-boot/applier/templates/build.yml similarity index 100% rename from multi-cluster-sprint-boot/applier/templates/build.yml rename to multi-cluster-spring-boot/applier/templates/build.yml diff --git a/multi-cluster-sprint-boot/applier/templates/deployment.yml b/multi-cluster-spring-boot/applier/templates/deployment.yml similarity index 100% rename from multi-cluster-sprint-boot/applier/templates/deployment.yml rename to multi-cluster-spring-boot/applier/templates/deployment.yml diff --git a/multi-cluster-sprint-boot/applier/params/build-dev b/multi-cluster-sprint-boot/applier/params/build-dev deleted file mode 100644 index 81346c55..00000000 --- a/multi-cluster-sprint-boot/applier/params/build-dev +++ /dev/null @@ -1,2 +0,0 @@ -APPLICATION_NAME=spring-rest -NAMESPACE=multicluster-spring-boot-dev From 479652878c0424030411799f4d538cd53b7fd6b1 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Fri, 16 Feb 2018 14:50:51 -0500 Subject: [PATCH 03/45] Missing , in jenkinsfile --- multi-cluster-spring-boot/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 9e94b1f9..49bf96dc 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -111,7 +111,7 @@ podTemplate(label: 'promotion-slave', cloud: 'openshift', serviceAccount: "jenki stage("Verify Deployment to ${env.STAGE3}") { - openshiftVerifyDeployment(apiURL: "${PROD_API}", authToken: "${PROD_TOKEN}" deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE3}", verifyReplicaCount: true) + openshiftVerifyDeployment(apiURL: "${PROD_API}", authToken: "${PROD_TOKEN}", deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE3}", verifyReplicaCount: true) } } From 3ed0a397e3a883f7e114387dcfb260b1acebe524 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Fri, 16 Feb 2018 14:54:27 -0500 Subject: [PATCH 04/45] Check out scm from parameter --- multi-cluster-spring-boot/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 49bf96dc..efbc5a94 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -26,7 +26,7 @@ node('maven') { String pomFileLocation = env.BUILD_CONTEXT_DIR ? "${env.BUILD_CONTEXT_DIR}/pom.xml" : "pom.xml" stage('SCM Checkout') { - checkout scm + git url: "${SOURCE_CODE_URL}" } stage('Build') { From f13d3106a7f36aacb7440059c7adbb20d25c1e84 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Fri, 16 Feb 2018 15:51:20 -0500 Subject: [PATCH 05/45] Moving secret grab out of skopeo image --- multi-cluster-spring-boot/Jenkinsfile | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index efbc5a94..b27e7a90 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -19,6 +19,17 @@ node('master') { env.STAGE2 = "${projectBase}-stage" env.STAGE3 = "${projectBase}-prod" + sh""" + oc version + oc get is jenkins-slave-image-mgmt -o jsonpath='{ .status.dockerImageRepository }' > /tmp/jenkins-slave-image-mgmt.out; + oc get secret prod-credentials -o jsonpath='{ .data.api }' | base64 --decode > /tmp/prod_api; + oc get secret prod-credentials -o jsonpath='{ .data.registry }' | base64 --decode > /tmp/prod_registry + oc get secret prod-credentials -o jsonpath='{ .data.token }' | base64 --decode > /tmp/prod_token + """ + env.SKOPEO_SLAVE_IMAGE = readFile('/tmp/jenkins-slave-image-mgmt.out').trim() + env.PROD_API= readFile('/tmp/prod_api').trim() + env.PROD_REGISTRY = readFile('/tmp/prod_registry').trim() + env.PROD_TOKEN = readFile('/tmp/prod_token').trim() } node('maven') { @@ -82,18 +93,6 @@ podTemplate(label: 'promotion-slave', cloud: 'openshift', serviceAccount: "jenki node('promotion-slave') { - sh""" - oc version - oc get is jenkins-slave-image-mgmt -o jsonpath='{ .status.dockerImageRepository }' > /tmp/jenkins-slave-image-mgmt.out; - oc get secret prod-credentials -o jsonpath='{ .data.api }' | base64 --decode > /tmp/prod_api; - oc get secret prod-credentials -o jsonpath='{ .data.registry }' | base64 --decode > /tmp/prod_registry - oc get secret prod-credentials -o jsonpath='{ .data.token }' | base64 --decode > /tmp/prod_token - """ - env.SKOPEO_SLAVE_IMAGE = readFile('/tmp/jenkins-slave-image-mgmt.out').trim() - env.PROD_API= readFile('/tmp/prod_api').trim() - env.PROD_REGISTRY = readFile('/tmp/prod_registry').trim() - env.PROD_TOKEN = readFile('/tmp/prod_token').trim() - stage("Promote To ${env.STAGE3}") { container('jenkins-slave-image-mgmt') { From 26466e9a3341c337f89ba446deb204e96a022f9d Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Fri, 16 Feb 2018 15:52:09 -0500 Subject: [PATCH 06/45] Removing input fields --- multi-cluster-spring-boot/Jenkinsfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index b27e7a90..d6ccdf56 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -68,7 +68,6 @@ node('maven') { openshiftVerifyDeployment(deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE1}", verifyReplicaCount: true) - input "Promote Application to Stage?" } stage("Promote To ${env.STAGE2}") { @@ -81,7 +80,6 @@ node('maven') { openshiftVerifyDeployment(deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE2}", verifyReplicaCount: true) - input "Promote Application to Prod?" } } From d0ddbf8c785de14e37322736d87b724f573ff775 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Fri, 16 Feb 2018 16:07:42 -0500 Subject: [PATCH 07/45] Unescaping registry var --- multi-cluster-spring-boot/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index d6ccdf56..b6a4cbe8 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -100,7 +100,7 @@ podTemplate(label: 'promotion-slave', cloud: 'openshift', serviceAccount: "jenki strippedNamespace=\$(echo ${env.NAMESPACE} | cut -d/ -f1) - echo "Promoting \${imageRegistry}/${env.STAGE2}/${env.APP_NAME} -> \${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME}" + echo "Promoting \${imageRegistry}/${env.STAGE2}/${env.APP_NAME} -> ${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME}" skopeo --tls-verify=false copy --remove-signatures --src-creds openshift:${env.TOKEN} --dest-creds openshift:${env.PROD_TOKEN} docker://\${imageRegistry}/${env.STAGE2}/${env.APP_NAME} docker://${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME} """ } From ff5147ca5df786775ee210954435c375444409ee Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Mon, 19 Feb 2018 15:44:44 -0500 Subject: [PATCH 08/45] Working pipeline --- multi-cluster-spring-boot/Jenkinsfile | 2 +- multi-cluster-spring-boot/README.md | 66 ++++++++++++------- .../group_vars/seed-hosts.yml | 15 ++++- .../{inventory => inventory-dev}/hosts | 0 .../inventory-prod/group_vars/seed-hosts.yml | 13 ++++ .../applier/inventory-prod/hosts | 2 + .../applier/params/build-dev | 2 +- .../applier/params/build-slave-dev | 0 .../applier/params/jenkins | 1 + .../applier/projects/projects-prod.yml | 9 +++ .../applier/projects/projects.yml | 10 +-- .../applier/projects/promoter-sa.yml | 24 +++++++ .../applier/templates/build.yml | 12 ++-- .../applier/templates/cluster-secret.yml | 35 ++++++++++ 14 files changed, 150 insertions(+), 41 deletions(-) rename multi-cluster-spring-boot/applier/{inventory => inventory-dev}/group_vars/seed-hosts.yml (51%) rename multi-cluster-spring-boot/applier/{inventory => inventory-dev}/hosts (100%) create mode 100644 multi-cluster-spring-boot/applier/inventory-prod/group_vars/seed-hosts.yml create mode 100644 multi-cluster-spring-boot/applier/inventory-prod/hosts create mode 100644 multi-cluster-spring-boot/applier/params/build-slave-dev create mode 100644 multi-cluster-spring-boot/applier/params/jenkins create mode 100644 multi-cluster-spring-boot/applier/projects/projects-prod.yml create mode 100644 multi-cluster-spring-boot/applier/projects/promoter-sa.yml create mode 100644 multi-cluster-spring-boot/applier/templates/cluster-secret.yml diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index b6a4cbe8..f63b7c76 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -108,7 +108,7 @@ podTemplate(label: 'promotion-slave', cloud: 'openshift', serviceAccount: "jenki stage("Verify Deployment to ${env.STAGE3}") { - openshiftVerifyDeployment(apiURL: "${PROD_API}", authToken: "${PROD_TOKEN}", deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE3}", verifyReplicaCount: true) + openshiftVerifyDeployment(apiURL: "${PROD_API}", authToken: "${PROD_TOKEN}", deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE3}", verifyReplicaCount: true ) } } diff --git a/multi-cluster-spring-boot/README.md b/multi-cluster-spring-boot/README.md index ab9b272f..858edf19 100644 --- a/multi-cluster-spring-boot/README.md +++ b/multi-cluster-spring-boot/README.md @@ -6,21 +6,47 @@ This example demonstrates how to implement a full end-to-end Jenkins Pipeline fo * Running both custom and oob Jenkins slaves as pods in OpenShift * "One Click" instantiation of a Jenkins Pipeline using OpenShift's Jenkins Pipeline Strategy feature * Promotion of an application's container image within an OpenShift Cluster (using `oc tag`) -* Promotion of an application's container image to a separate OpenShift Cluster (using `skopeo`) - Coming Soon! +* Promotion of an application's container image to a separate OpenShift Cluster (using `skopeo`) * Automated rollout using the [openshift-appler](https://github.com/redhat-cop/casl-ansible/tree/master/roles/openshift-applier) Ansible role. +## Prerequisites + +In order to run this pipeline, you will need: + +* Two (2) OpenShift clusters, version 3.5 or greater + * In this document, we will refer to the first cluster as *_Dev_* and the second as *_Prod_*. +* Ansible installed on your machine + ## Automated Quickstart This quickstart can be deployed quickly using Ansible. Here are the steps. 1. Clone [this repo](https://github.com/redhat-cop/container-pipelines.git) and the [casl-ansible](https://github.com/redhat-cop/casl-ansible.git) repo. -2. Log into an OpenShift cluster, then run the following command. -``` -$ oc login -$ ansible-playbook -i ./applier/inventory/ /path/to/casl-ansible/playbooks/openshift-cluster-seed.yml -``` - -At this point you should have 3 projects deployed (`basic-spring-boot-dev`, `basic-spring-boot-stage`, and `basic-spring-boot-prod`) with our [Spring Rest](https://github.com/redhat-cop/spring-rest.git) demo application deployed to all 3. +2. Log into your _Prod_ OpenShift cluster, and run the following command. + ``` + $ oc login + ... + $ ansible-playbook -i ./applier/inventory-prod/ /path/to/casl-ansible/playbooks/openshift-cluster-seed.yml + ``` +3. One of the things that was created by ansible is a `ServiceAccount` that will be used for promoting your app from _Dev_ to _Prod_. We'll need to extract its credentials so that our pipeline can use that account. + ``` + TOKEN=$(oc serviceaccounts get-token promoter -n field-guides-prod) + ``` +The Ansible automation for your _Dev_ cluster will expect a parameters file to be created at `./applier/params/prod-credentials`. It should look something like this: + ``` + $ echo "TOKEN=${TOKEN} + API_URL=https://master.example.com + REGISTRY_URL=docker-registry-default.apps.example.com + " > ./applier/params/prod-credentials + ``` +4. Now, Log into your _Dev_ cluster, and instantiate the pipeline. + ``` + $ oc login + ... + $ ansible-playbook -i ./applier/inventory-dev/ /path/to/casl-ansible/playbooks/openshift-cluster-seed.yml + ``` + +At this point you should have 3 projects deployed (`multicluster-spring-boot-dev`, `multicluster-spring-boot-stage`, and `multicluster-spring-boot-prod`) with our [Spring Rest](https://github.com/redhat-cop/spring-rest.git) demo application deployed to all 3. ## Architecture @@ -58,29 +84,23 @@ For convenience, this pipeline script is already included in the following git r https://github.com/redhat-cop/spring-rest -## Bill of Materials - -* One or Two OpenShift Container Platform Clusters - * OpenShift 3.5+ is required. -* Access to GitHub - ## Manual Deployment Instructions ### 1. Create Lifecycle Stages For the purposes of this demo, we are going to create three stages for our application to be promoted through. -- `basic-spring-boot-dev` -- `basic-spring-boot-stage` -- `basic-spring-boot-prod` +- `multicluster-spring-boot-dev` +- `multicluster-spring-boot-stage` +- `multicluster-spring-boot-prod` In the spirit of _Infrastructure as Code_ we have a YAML file that defines the `ProjectRequests` for us. This is as an alternative to running `oc new-project`, but will yeild the same result. ``` $ oc create -f applier/projects/projects.yml -projectrequest "basic-spring-boot-dev" created -projectrequest "basic-spring-boot-stage" created -projectrequest "basic-spring-boot-prod" created +projectrequest "multicluster-spring-boot-dev" created +projectrequest "multicluster-spring-boot-stage" created +projectrequest "multicluster-spring-boot-prod" created ``` ### 2. Stand up Jenkins master in dev @@ -88,7 +108,7 @@ projectrequest "basic-spring-boot-prod" created For this step, the OpenShift default template set provides exactly what we need to get jenkins up and running. ``` -$ oc process openshift//jenkins-ephemeral | oc apply -f- -n basic-spring-boot-dev +$ oc process openshift//jenkins-ephemeral | oc apply -f- -n multicluster-spring-boot-dev route "jenkins" created deploymentconfig "jenkins" created serviceaccount "jenkins" created @@ -143,12 +163,12 @@ buildconfig "spring-rest-pipeline" created buildconfig "spring-rest" created ``` -At this point you should be able to go to the Web Console and follow the pipeline by clicking in your `basic-spring-boot-dev` project, and going to *Builds* -> *Pipelines*. At several points you will be prompted for input on the pipeline. You can interact with it by clicking on the _input required_ link, which takes you to Jenkins, where you can click the *Proceed* button. By the time you get through the end of the pipeline you should be able to visit the Route for your app deployed to the `myapp-prod` project to confirm that your image has been promoted through all stages. +At this point you should be able to go to the Web Console and follow the pipeline by clicking in your `multicluster-spring-boot-dev` project, and going to *Builds* -> *Pipelines*. At several points you will be prompted for input on the pipeline. You can interact with it by clicking on the _input required_ link, which takes you to Jenkins, where you can click the *Proceed* button. By the time you get through the end of the pipeline you should be able to visit the Route for your app deployed to the `myapp-prod` project to confirm that your image has been promoted through all stages. ## Cleanup Cleaning up this example is as simple as deleting the projects we created at the beginning. ``` -oc delete project basic-spring-boot-dev basic-spring-boot-prod basic-spring-boot-stage +oc delete project multicluster-spring-boot-dev multicluster-spring-boot-prod multicluster-spring-boot-stage ``` diff --git a/multi-cluster-spring-boot/applier/inventory/group_vars/seed-hosts.yml b/multi-cluster-spring-boot/applier/inventory-dev/group_vars/seed-hosts.yml similarity index 51% rename from multi-cluster-spring-boot/applier/inventory/group_vars/seed-hosts.yml rename to multi-cluster-spring-boot/applier/inventory-dev/group_vars/seed-hosts.yml index 9d58b500..c56deb65 100644 --- a/multi-cluster-spring-boot/applier/inventory/group_vars/seed-hosts.yml +++ b/multi-cluster-spring-boot/applier/inventory-dev/group_vars/seed-hosts.yml @@ -6,17 +6,26 @@ openshift_cluster_content: file_action: create - object: deployments content: + - name: "deploy jenkins" + template: "openshift//jenkins-ephemeral" + params: "{{ inventory_dir }}/../params/jenkins" + namespace: multicluster-spring-boot-dev + - name: "create prod cluster credential" + template: "{{ inventory_dir }}/../templates/cluster-secret.yml" + params: "{{ inventory_dir }}/../params/prod-credentials" + namespace: multicluster-spring-boot-dev - name: "deploy dev environment" template: "{{ inventory_dir }}/../templates/deployment.yml" params: "{{ inventory_dir }}/../params/deployment-dev" - name: "deply stage environment" template: "{{ inventory_dir }}/../templates/deployment.yml" params: "{{ inventory_dir }}/../params/deployment-stage" - - name: "deply prod environment" - template: "{{ inventory_dir }}/../templates/deployment.yml" - params: "{{ inventory_dir }}/../params/deployment-prod" - object: builds content: + - name: Apply Image Build" + namespace: "multicluster-spring-boot-dev" + template: "https://raw.githubusercontent.com/redhat-cop/containers-quickstarts/master/jenkins-slaves/templates/jenkins-slave-image-mgmt-template.json" + params: "{{ inventory_dir }}/../params/build-slave-dev" - name: "deploy build pipeline to dev" template: "{{ inventory_dir }}/../templates/build.yml" params: "{{ inventory_dir }}/../params/build-dev" diff --git a/multi-cluster-spring-boot/applier/inventory/hosts b/multi-cluster-spring-boot/applier/inventory-dev/hosts similarity index 100% rename from multi-cluster-spring-boot/applier/inventory/hosts rename to multi-cluster-spring-boot/applier/inventory-dev/hosts diff --git a/multi-cluster-spring-boot/applier/inventory-prod/group_vars/seed-hosts.yml b/multi-cluster-spring-boot/applier/inventory-prod/group_vars/seed-hosts.yml new file mode 100644 index 00000000..87240fd3 --- /dev/null +++ b/multi-cluster-spring-boot/applier/inventory-prod/group_vars/seed-hosts.yml @@ -0,0 +1,13 @@ +openshift_cluster_content: +- object: projects + content: + - name: "create environments" + file: "{{ inventory_dir }}/../projects/projects-prod.yml" + file_action: create + - name: "create promoter account" + file: "{{ inventory_dir }}/../projects/promoter-sa.yml" +- object: deployments + content: + - name: "deply prod environment" + template: "{{ inventory_dir }}/../templates/deployment.yml" + params: "{{ inventory_dir }}/../params/deployment-prod" diff --git a/multi-cluster-spring-boot/applier/inventory-prod/hosts b/multi-cluster-spring-boot/applier/inventory-prod/hosts new file mode 100644 index 00000000..7f325c4b --- /dev/null +++ b/multi-cluster-spring-boot/applier/inventory-prod/hosts @@ -0,0 +1,2 @@ +[seed-hosts] +localhost ansible_connection=local diff --git a/multi-cluster-spring-boot/applier/params/build-dev b/multi-cluster-spring-boot/applier/params/build-dev index e1eaf44a..de786644 100644 --- a/multi-cluster-spring-boot/applier/params/build-dev +++ b/multi-cluster-spring-boot/applier/params/build-dev @@ -2,4 +2,4 @@ APPLICATION_NAME=spring-rest NAMESPACE=multicluster-spring-boot-dev PIPELINE_REPOSITORY_URL=https://github.com/etsauer/container-pipelines.git PIPELINE_REPOSITORY_REF=multi-cluster-promotion -PIPELINE_REPOSITORY_CONTEXT_DIR=multicluster-spring-boot +PIPELINE_REPOSITORY_CONTEXT_DIR=multi-cluster-spring-boot diff --git a/multi-cluster-spring-boot/applier/params/build-slave-dev b/multi-cluster-spring-boot/applier/params/build-slave-dev new file mode 100644 index 00000000..e69de29b diff --git a/multi-cluster-spring-boot/applier/params/jenkins b/multi-cluster-spring-boot/applier/params/jenkins new file mode 100644 index 00000000..e009ad5f --- /dev/null +++ b/multi-cluster-spring-boot/applier/params/jenkins @@ -0,0 +1 @@ +MEMORY_LIMIT=768Mi diff --git a/multi-cluster-spring-boot/applier/projects/projects-prod.yml b/multi-cluster-spring-boot/applier/projects/projects-prod.yml new file mode 100644 index 00000000..2cd0a889 --- /dev/null +++ b/multi-cluster-spring-boot/applier/projects/projects-prod.yml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: List +items: +- kind: ProjectRequest + apiVersion: v1 + metadata: + name: multicluster-spring-boot-prod + creationTimestam: null + displayName: MultiCluster Pipeline - Prod diff --git a/multi-cluster-spring-boot/applier/projects/projects.yml b/multi-cluster-spring-boot/applier/projects/projects.yml index 7d5eef1a..9c3b81e8 100644 --- a/multi-cluster-spring-boot/applier/projects/projects.yml +++ b/multi-cluster-spring-boot/applier/projects/projects.yml @@ -6,16 +6,10 @@ items: metadata: name: multicluster-spring-boot-dev creationTimestam: null - displayName: Dev - Spring Rest App + displayName: MultiCluster Pipeline - Dev - kind: ProjectRequest apiVersion: v1 metadata: name: multicluster-spring-boot-stage creationTimestam: null - displayName: Staging - Spring Rest App -- kind: ProjectRequest - apiVersion: v1 - metadata: - name: multicluster-spring-boot-prod - creationTimestam: null - displayName: Prod - Spring Rest App + displayName: MultiCluster Pipeline - Stage diff --git a/multi-cluster-spring-boot/applier/projects/promoter-sa.yml b/multi-cluster-spring-boot/applier/projects/promoter-sa.yml new file mode 100644 index 00000000..76616d0a --- /dev/null +++ b/multi-cluster-spring-boot/applier/projects/promoter-sa.yml @@ -0,0 +1,24 @@ +--- +apiVersion: v1 +kind: List +items: +- apiVersion: v1 + kind: ServiceAccount + metadata: + name: promoter + namespace: multicluster-spring-boot-prod +- apiVersion: v1 + groupNames: null + kind: RoleBinding + metadata: + creationTimestamp: null + name: edit + namespace: multicluster-spring-boot-prod + roleRef: + name: edit + subjects: + - kind: ServiceAccount + name: promoter + namespace: multicluster-spring-boot-prod + userNames: + - system:serviceaccount:multicluster-spring-boot-prod:promoter diff --git a/multi-cluster-spring-boot/applier/templates/build.yml b/multi-cluster-spring-boot/applier/templates/build.yml index c23c17a3..b1667b5a 100644 --- a/multi-cluster-spring-boot/applier/templates/build.yml +++ b/multi-cluster-spring-boot/applier/templates/build.yml @@ -33,11 +33,13 @@ objects: type: "JenkinsPipeline" jenkinsPipelineStrategy: jenkinsfilePath: ${PIPELINE_SCRIPT} - env: - - name: SOURCE_CODE_URL - value: ${SOURCE_CODE_URL} - - name: SOURCE_CODE_BRANCH - value: ${SOURCE_CODE_BRANCH} + env: + - name: SOURCE_CODE_URL + value: ${SOURCE_CODE_URL} + - name: SOURCE_CODE_BRANCH + value: ${SOURCE_CODE_BRANCH} + - name: SKIP_TLS + value: "true" - apiVersion: v1 kind: BuildConfig metadata: diff --git a/multi-cluster-spring-boot/applier/templates/cluster-secret.yml b/multi-cluster-spring-boot/applier/templates/cluster-secret.yml new file mode 100644 index 00000000..ed9ddbdc --- /dev/null +++ b/multi-cluster-spring-boot/applier/templates/cluster-secret.yml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: Template +labels: + template: cluster-credentials-secret +metadata: + annotations: + description: Cluster Credential Secret + tags: secret + version: 1.0.0 + name: cluster-credentials-secret +objects: +- apiVersion: v1 + stringData: + api: ${API_URL} + registry: ${REGISTRY_URL} + token: "${TOKEN}" + data: + kind: Secret + metadata: + name: ${SECRET_NAME} + type: Opaque +parameters: +- description: The name for the application. + name: SECRET_NAME + required: true + value: prod-credentials +- description: The API URL + name: API_URL + required: true +- description: The Registry Route URL + name: REGISTRY_URL + required: true +- description: Service Account Token + name: TOKEN + required: true From ec39777a0081598210f8f4aacd92c699e4727022 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Mon, 19 Feb 2018 16:55:29 -0500 Subject: [PATCH 09/45] Fix formatting issue in readme --- multi-cluster-spring-boot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/README.md b/multi-cluster-spring-boot/README.md index 858edf19..bcd04918 100644 --- a/multi-cluster-spring-boot/README.md +++ b/multi-cluster-spring-boot/README.md @@ -32,7 +32,7 @@ This quickstart can be deployed quickly using Ansible. Here are the steps. ``` TOKEN=$(oc serviceaccounts get-token promoter -n field-guides-prod) ``` -The Ansible automation for your _Dev_ cluster will expect a parameters file to be created at `./applier/params/prod-credentials`. It should look something like this: + The Ansible automation for your _Dev_ cluster will expect a parameters file to be created at `./applier/params/prod-credentials`. It should look something like this: ``` $ echo "TOKEN=${TOKEN} API_URL=https://master.example.com From 28c9f8a1baa9b9e5b009c7e958d9e85036a15364 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 15:17:44 -0500 Subject: [PATCH 10/45] Initial conversion to declarative and openshift/jenkins-client-plugin; needs more work --- multi-cluster-spring-boot/Jenkinsfile | 174 +++++++++++--------------- 1 file changed, 74 insertions(+), 100 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index f63b7c76..a89a7edf 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -1,99 +1,81 @@ -#!/usr/bin/groovy - -//// -// This pipeline requires the following plugins: -// Kubernetes Plugin 0.10 -//// - -String ocpApiServer = env.OCP_API_SERVER ? "${env.OCP_API_SERVER}" : "https://openshift.default.svc.cluster.local" - -node('master') { - - env.NAMESPACE = readFile('/var/run/secrets/kubernetes.io/serviceaccount/namespace').trim() - env.TOKEN = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() - env.OC_CMD = "oc --token=${env.TOKEN} --server=${ocpApiServer} --certificate-authority=/run/secrets/kubernetes.io/serviceaccount/ca.crt --namespace=${env.NAMESPACE}" - - env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '') - def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') - env.STAGE1 = "${projectBase}-dev" - env.STAGE2 = "${projectBase}-stage" - env.STAGE3 = "${projectBase}-prod" - - sh""" - oc version - oc get is jenkins-slave-image-mgmt -o jsonpath='{ .status.dockerImageRepository }' > /tmp/jenkins-slave-image-mgmt.out; - oc get secret prod-credentials -o jsonpath='{ .data.api }' | base64 --decode > /tmp/prod_api; - oc get secret prod-credentials -o jsonpath='{ .data.registry }' | base64 --decode > /tmp/prod_registry - oc get secret prod-credentials -o jsonpath='{ .data.token }' | base64 --decode > /tmp/prod_token - """ - env.SKOPEO_SLAVE_IMAGE = readFile('/tmp/jenkins-slave-image-mgmt.out').trim() - env.PROD_API= readFile('/tmp/prod_api').trim() - env.PROD_REGISTRY = readFile('/tmp/prod_registry').trim() - env.PROD_TOKEN = readFile('/tmp/prod_token').trim() -} - -node('maven') { - def mvnCmd = 'mvn' - String pomFileLocation = env.BUILD_CONTEXT_DIR ? "${env.BUILD_CONTEXT_DIR}/pom.xml" : "pom.xml" - - stage('SCM Checkout') { - git url: "${SOURCE_CODE_URL}" - } - - stage('Build') { - - sh "${mvnCmd} clean install -DskipTests=true -f ${pomFileLocation}" - - } - - stage('Unit Test') { - - sh "${mvnCmd} test -f ${pomFileLocation}" - - } - - - stage('Build Image') { - - sh """ - rm -rf oc-build && mkdir -p oc-build/deployments - for t in \$(echo "jar;war;ear" | tr ";" "\\n"); do - cp -rfv ./target/*.\$t oc-build/deployments/ 2> /dev/null || echo "No \$t files" - done - ${env.OC_CMD} start-build ${env.APP_NAME} --from-dir=oc-build --wait=true --follow=true || exit 1 - """ - } - - stage("Verify Deployment to ${env.STAGE1}") { - - openshiftVerifyDeployment(deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE1}", verifyReplicaCount: true) - - } - - stage("Promote To ${env.STAGE2}") { - sh """ - ${env.OC_CMD} tag ${env.STAGE1}/${env.APP_NAME}:latest ${env.STAGE2}/${env.APP_NAME}:latest - """ - } - - stage("Verify Deployment to ${env.STAGE2}") { - - openshiftVerifyDeployment(deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE2}", verifyReplicaCount: true) - - } - +openshift.withCluster() { + env.SKOPEO_IMAGE = openshift.selector( 'is/jenkins-slave-image-mgmt' ).object().status.dockerImageRepository } +env.SOURCE_CODE_URL = 'https://github.com/redhat-cop/spring-rest.git' + +//env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '') +env.APP_NAME = 'spring-rest' +def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') +env.STAGE1 = "${projectBase}-dev" +env.STAGE2 = "${projectBase}-stage" +env.STAGE3 = "${projectBase}-prod" + + +pipeline { + agent { label 'maven' } + stages { + stage('Code Build') { + steps { + git url: "${SOURCE_CODE_URL}" + sh "mvn clean install" + } + } + stage('Image Build') { + steps { + sh """ + rm -rf oc-build && mkdir -p oc-build/deployments + for t in \$(echo "jar;war;ear" | tr ";" "\\n"); do + cp -rfv ./target/*.\$t oc-build/deployments/ 2> /dev/null || echo "No \$t files" + done + """ + script { + openshift.withCluster() { + openshift.startBuild("${APP_NAME}", "--from-dir=oc-build", "--wait", "--follow") + } + } + } + } -podTemplate(label: 'promotion-slave', cloud: 'openshift', serviceAccount: "jenkins", containers: [ - containerTemplate(name: 'jenkins-slave-image-mgmt', image: "${env.SKOPEO_SLAVE_IMAGE}", ttyEnabled: true, command: 'cat'), - containerTemplate(name: 'jnlp', image: 'jenkinsci/jnlp-slave:2.62-alpine', args: '${computer.jnlpmac} ${computer.name}') -]) { - - node('promotion-slave') { + stage ('Verify Deployment to ${STAGE1}') { + steps { + script { + openshift.withCluster() { + def dcObj = openshift.selector('dc', env.APP_NAME).object() + def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"]) + podSelector.untilEach { + echo "pod: ${it.name()}" + return it.object().status.containerStatuses[0].ready + } + } + } + } + } - stage("Promote To ${env.STAGE3}") { + stage('Promote to Stage') { + steps { + script { + openshift.withCluster() { + openshift.tag("${env.STAGE1}/${env.APP_NAME}:latest", "${env.STAGE2}/${env.APP_NAME}:latest") + } + } + } + } - container('jenkins-slave-image-mgmt') { + stage('Promote to Prod') { + agent { + kubernetes { + label 'promotion-slave' + cloud 'openshift' + serviceAccount 'jenkins' + containerTemplate { + name 'jenkins-slave-image-mgmt' + image "${SKOPEO_IMAGE}" + ttyEnabled true + command 'cat' + } + } + } + steps { sh """ imageRegistry=\$(oc get is ${env.APP_NAME} --template='{{ .status.dockerImageRepository }}' -n ${env.STAGE2} | cut -d/ -f1) @@ -105,13 +87,5 @@ podTemplate(label: 'promotion-slave', cloud: 'openshift', serviceAccount: "jenki """ } } - - stage("Verify Deployment to ${env.STAGE3}") { - - openshiftVerifyDeployment(apiURL: "${PROD_API}", authToken: "${PROD_TOKEN}", deploymentConfig: "${env.APP_NAME}", namespace: "${STAGE3}", verifyReplicaCount: true ) - - } } } - -println "Application ${env.APP_NAME} is now in Production!" From 9acfa4c8e89152f7a9e6afc46fc326bcc753b070 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 15:20:21 -0500 Subject: [PATCH 11/45] Remove some hard coded values --- multi-cluster-spring-boot/Jenkinsfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index a89a7edf..5a0a4671 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -1,10 +1,7 @@ openshift.withCluster() { env.SKOPEO_IMAGE = openshift.selector( 'is/jenkins-slave-image-mgmt' ).object().status.dockerImageRepository } -env.SOURCE_CODE_URL = 'https://github.com/redhat-cop/spring-rest.git' - -//env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '') -env.APP_NAME = 'spring-rest' +env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '') def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') env.STAGE1 = "${projectBase}-dev" env.STAGE2 = "${projectBase}-stage" From 6d8ad3c75c5d2e63892fe8eec3f2bb2390475307 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 15:54:10 -0500 Subject: [PATCH 12/45] Echo app name --- multi-cluster-spring-boot/Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 5a0a4671..ae667eb5 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -2,6 +2,7 @@ openshift.withCluster() { env.SKOPEO_IMAGE = openshift.selector( 'is/jenkins-slave-image-mgmt' ).object().status.dockerImageRepository } env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '') +echo "Starting Pipeline for ${APP_NAME}..." def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') env.STAGE1 = "${projectBase}-dev" env.STAGE2 = "${projectBase}-stage" @@ -33,7 +34,7 @@ pipeline { } } - stage ('Verify Deployment to ${STAGE1}') { + stage ('Verify Deployment to Dev') { steps { script { openshift.withCluster() { From 80a22a5c339ae488c026e5a5ce361de09c7aabb8 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 15:58:55 -0500 Subject: [PATCH 13/45] Switching to .project() --- multi-cluster-spring-boot/Jenkinsfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index ae667eb5..0eb6d63d 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -1,12 +1,12 @@ openshift.withCluster() { env.SKOPEO_IMAGE = openshift.selector( 'is/jenkins-slave-image-mgmt' ).object().status.dockerImageRepository + env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${openshift.project()}-?/, '') + echo "Starting Pipeline for ${APP_NAME}..." + def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') + env.STAGE1 = "${projectBase}-dev" + env.STAGE2 = "${projectBase}-stage" + env.STAGE3 = "${projectBase}-prod" } -env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '') -echo "Starting Pipeline for ${APP_NAME}..." -def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') -env.STAGE1 = "${projectBase}-dev" -env.STAGE2 = "${projectBase}-stage" -env.STAGE3 = "${projectBase}-prod" pipeline { From 3b198813a16bdf4f10a4f8a11b1841db50748126 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 15:59:46 -0500 Subject: [PATCH 14/45] Switching to .project() --- multi-cluster-spring-boot/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 0eb6d63d..ebfcdb00 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -2,7 +2,7 @@ openshift.withCluster() { env.SKOPEO_IMAGE = openshift.selector( 'is/jenkins-slave-image-mgmt' ).object().status.dockerImageRepository env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${openshift.project()}-?/, '') echo "Starting Pipeline for ${APP_NAME}..." - def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') + def projectBase = "${env.openshift.project()}".replaceAll(/-dev/, '') env.STAGE1 = "${projectBase}-dev" env.STAGE2 = "${projectBase}-stage" env.STAGE3 = "${projectBase}-prod" @@ -78,7 +78,7 @@ pipeline { imageRegistry=\$(oc get is ${env.APP_NAME} --template='{{ .status.dockerImageRepository }}' -n ${env.STAGE2} | cut -d/ -f1) - strippedNamespace=\$(echo ${env.NAMESPACE} | cut -d/ -f1) + strippedNamespace=\$(echo ${env.openshift.project()} | cut -d/ -f1) echo "Promoting \${imageRegistry}/${env.STAGE2}/${env.APP_NAME} -> ${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME}" skopeo --tls-verify=false copy --remove-signatures --src-creds openshift:${env.TOKEN} --dest-creds openshift:${env.PROD_TOKEN} docker://\${imageRegistry}/${env.STAGE2}/${env.APP_NAME} docker://${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME} From 42bbc79a510c4079dfe50fed93993a995a56baff Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 16:01:38 -0500 Subject: [PATCH 15/45] Switching back to NAMESPACE --- multi-cluster-spring-boot/Jenkinsfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index ebfcdb00..4036c51e 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -1,8 +1,9 @@ openshift.withCluster() { + env.NAMESPACE = openshift.project() env.SKOPEO_IMAGE = openshift.selector( 'is/jenkins-slave-image-mgmt' ).object().status.dockerImageRepository - env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${openshift.project()}-?/, '') + env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '') echo "Starting Pipeline for ${APP_NAME}..." - def projectBase = "${env.openshift.project()}".replaceAll(/-dev/, '') + def projectBase = "${env.NAMESPACE}".replaceAll(/-dev/, '') env.STAGE1 = "${projectBase}-dev" env.STAGE2 = "${projectBase}-stage" env.STAGE3 = "${projectBase}-prod" @@ -78,7 +79,7 @@ pipeline { imageRegistry=\$(oc get is ${env.APP_NAME} --template='{{ .status.dockerImageRepository }}' -n ${env.STAGE2} | cut -d/ -f1) - strippedNamespace=\$(echo ${env.openshift.project()} | cut -d/ -f1) + strippedNamespace=\$(echo ${env.NAMESPACE} | cut -d/ -f1) echo "Promoting \${imageRegistry}/${env.STAGE2}/${env.APP_NAME} -> ${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME}" skopeo --tls-verify=false copy --remove-signatures --src-creds openshift:${env.TOKEN} --dest-creds openshift:${env.PROD_TOKEN} docker://\${imageRegistry}/${env.STAGE2}/${env.APP_NAME} docker://${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME} From 78eb98cff2b5289408add930f90a6be7d344e001 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 16:17:46 -0500 Subject: [PATCH 16/45] Refactor skopeo promotion --- multi-cluster-spring-boot/Jenkinsfile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 4036c51e..fc124bf2 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -75,15 +75,17 @@ pipeline { } } steps { - sh """ - - imageRegistry=\$(oc get is ${env.APP_NAME} --template='{{ .status.dockerImageRepository }}' -n ${env.STAGE2} | cut -d/ -f1) - - strippedNamespace=\$(echo ${env.NAMESPACE} | cut -d/ -f1) + script { + openshift.withCluster() { + def secretData = openshift.selector('secret/prod-credentials').object().data - echo "Promoting \${imageRegistry}/${env.STAGE2}/${env.APP_NAME} -> ${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME}" - skopeo --tls-verify=false copy --remove-signatures --src-creds openshift:${env.TOKEN} --dest-creds openshift:${env.PROD_TOKEN} docker://\${imageRegistry}/${env.STAGE2}/${env.APP_NAME} docker://${PROD_REGISTRY}/${env.STAGE3}/${env.APP_NAME} - """ + openshift.withProject("${STAGE2}") { + def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository + echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${secretData.registry}/${STAGE3}/${APP_NAME}" + sh "skopeo copy --tls-verify=false --remove-signatures --src-creds openshift:${TOKEN} --dest-creds openshift:${secretData.token} docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${secretData.registry}/${STAGE3}/${APP_NAME}" + } + } + } } } } From 24e1b383a29d315d15bb67aa640bcc7fecc82c3f Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 16:26:39 -0500 Subject: [PATCH 17/45] Test Jenkinsfile --- multi-cluster-spring-boot/Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index fc124bf2..f6f46170 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -78,12 +78,13 @@ pipeline { script { openshift.withCluster() { def secretData = openshift.selector('secret/prod-credentials').object().data - +/* openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${secretData.registry}/${STAGE3}/${APP_NAME}" sh "skopeo copy --tls-verify=false --remove-signatures --src-creds openshift:${TOKEN} --dest-creds openshift:${secretData.token} docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${secretData.registry}/${STAGE3}/${APP_NAME}" } +*/ } } } From de3db9d425342bf9085f36b949e29b13a8fb9608 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 16:31:22 -0500 Subject: [PATCH 18/45] Test Jenkinsfile --- multi-cluster-spring-boot/Jenkinsfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index f6f46170..3bbdcfd5 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -77,7 +77,9 @@ pipeline { steps { script { openshift.withCluster() { - def secretData = openshift.selector('secret/prod-credentials').object().data + echo "Looking for secret in ${openshift.project()}" + def secretData = openshift.selector('secret/prod-credentials') + secretData.describe() /* openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository From 379ed8d58f458e5b52adc5def5b7e57aeb0fd6ea Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 16:41:55 -0500 Subject: [PATCH 19/45] Test Jenkinsfile --- multi-cluster-spring-boot/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 3bbdcfd5..229f33b2 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -78,8 +78,8 @@ pipeline { script { openshift.withCluster() { echo "Looking for secret in ${openshift.project()}" - def secretData = openshift.selector('secret/prod-credentials') - secretData.describe() + def secretData = openshift.selector('secret', 'prod-credentials') + echo "${secretData.object()}" /* openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository From 3311869bf4292a4ef9899148f7e49f080ec21dc7 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 16:45:00 -0500 Subject: [PATCH 20/45] Test Jenkinsfile --- multi-cluster-spring-boot/Jenkinsfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 229f33b2..e5101406 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -78,8 +78,10 @@ pipeline { script { openshift.withCluster() { echo "Looking for secret in ${openshift.project()}" + openshift.verbose() def secretData = openshift.selector('secret', 'prod-credentials') echo "${secretData.object()}" + openshift.verbose(false) /* openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository From e7a8536342305faf99e22307375fa3c18f02b17a Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 17:01:38 -0500 Subject: [PATCH 21/45] Test Jenkinsfile --- multi-cluster-spring-boot/Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index e5101406..be493110 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -79,6 +79,7 @@ pipeline { openshift.withCluster() { echo "Looking for secret in ${openshift.project()}" openshift.verbose() + openshift.raw('whoami') def secretData = openshift.selector('secret', 'prod-credentials') echo "${secretData.object()}" openshift.verbose(false) From f0777b9cd75b450bc8471dcb4c89565fc9580780 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Tue, 20 Feb 2018 17:08:01 -0500 Subject: [PATCH 22/45] Test Jenkinsfile --- multi-cluster-spring-boot/Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index be493110..ab51016e 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -79,6 +79,9 @@ pipeline { openshift.withCluster() { echo "Looking for secret in ${openshift.project()}" openshift.verbose() + testImage = openshift.selector( 'is/jenkins-slave-image-mgmt' ).object().status.dockerImageRepository + echo "Test Image: ${testImage}" + openshift.raw('whoami') def secretData = openshift.selector('secret', 'prod-credentials') echo "${secretData.object()}" From 43a150799c2d8ddc9d467f726bda8cba033e9531 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 20:37:30 -0500 Subject: [PATCH 23/45] Test Jenkinsfile --- multi-cluster-spring-boot/Jenkinsfile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index ab51016e..dc447517 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -67,10 +67,12 @@ pipeline { cloud 'openshift' serviceAccount 'jenkins' containerTemplate { - name 'jenkins-slave-image-mgmt' - image "${SKOPEO_IMAGE}" - ttyEnabled true - command 'cat' + name 'jnlp' + image "docker-registry.default.svc:5000/${NAMESPACE}/jenkins-slave-image-mgmt" + alwaysPullImage true + workingDir '/tmp' + args '${computer.jnlpmac} ${computer.name}' + ttyEnabled false } } } From af799948802626116848541e00372ac409990a5d Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 21:02:48 -0500 Subject: [PATCH 24/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index dc447517..13e9a969 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -88,13 +88,13 @@ pipeline { def secretData = openshift.selector('secret', 'prod-credentials') echo "${secretData.object()}" openshift.verbose(false) -/* + openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${secretData.registry}/${STAGE3}/${APP_NAME}" sh "skopeo copy --tls-verify=false --remove-signatures --src-creds openshift:${TOKEN} --dest-creds openshift:${secretData.token} docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${secretData.registry}/${STAGE3}/${APP_NAME}" } -*/ + } } } From dcc0f69ae8e669dee60137e5e87de7226048ce78 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 21:13:21 -0500 Subject: [PATCH 25/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 13e9a969..de2a4484 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -79,20 +79,15 @@ pipeline { steps { script { openshift.withCluster() { - echo "Looking for secret in ${openshift.project()}" - openshift.verbose() - testImage = openshift.selector( 'is/jenkins-slave-image-mgmt' ).object().status.dockerImageRepository - echo "Test Image: ${testImage}" - openshift.raw('whoami') def secretData = openshift.selector('secret', 'prod-credentials') - echo "${secretData.object()}" - openshift.verbose(false) + def registry = secretData.registry + def token = secretData.token openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository - echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${secretData.registry}/${STAGE3}/${APP_NAME}" - sh "skopeo copy --tls-verify=false --remove-signatures --src-creds openshift:${TOKEN} --dest-creds openshift:${secretData.token} docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${secretData.registry}/${STAGE3}/${APP_NAME}" + echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${registry}/${STAGE3}/${APP_NAME}" + sh "skopeo copy --tls-verify=false --remove-signatures --src-creds openshift:${TOKEN} --dest-creds openshift:${token} docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${registry}/${STAGE3}/${APP_NAME}" } } From 29f0251ae5e3cee61225031fceadf376c796d3f5 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 21:17:22 -0500 Subject: [PATCH 26/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index de2a4484..2e866c4c 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -80,7 +80,7 @@ pipeline { script { openshift.withCluster() { - def secretData = openshift.selector('secret', 'prod-credentials') + def secretData = openshift.selector('secret', 'prod-credentials').object() def registry = secretData.registry def token = secretData.token From a5e85c357797cd913f28ec08787925abd992e4bf Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 21:23:01 -0500 Subject: [PATCH 27/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 2e866c4c..116918d8 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -80,6 +80,8 @@ pipeline { script { openshift.withCluster() { + def localToken = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() + def secretData = openshift.selector('secret', 'prod-credentials').object() def registry = secretData.registry def token = secretData.token @@ -87,7 +89,7 @@ pipeline { openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${registry}/${STAGE3}/${APP_NAME}" - sh "skopeo copy --tls-verify=false --remove-signatures --src-creds openshift:${TOKEN} --dest-creds openshift:${token} docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${registry}/${STAGE3}/${APP_NAME}" + sh "skopeo copy --tls-verify=false --remove-signatures --src-creds openshift:${localToken} --dest-creds openshift:${token} docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${registry}/${STAGE3}/${APP_NAME}" } } From d7ea6e9e6585a3157931d710de94912ed1df81a0 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 21:29:36 -0500 Subject: [PATCH 28/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 116918d8..2c6c9ac8 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -89,7 +89,12 @@ pipeline { openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${registry}/${STAGE3}/${APP_NAME}" - sh "skopeo copy --tls-verify=false --remove-signatures --src-creds openshift:${localToken} --dest-creds openshift:${token} docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${registry}/${STAGE3}/${APP_NAME}" + sh """ + skopeo copy --remove-signatures \ + --src-creds openshift:${localToken} --src-cert-dir /run/secrets/kubernetes.io/serviceaccount/ca.crt \ + --dest-creds openshift:${token} --dest-tls-verify false \ + docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${registry}/${STAGE3}/${APP_NAME} + """ } } From 31a11774ca9b1fdfa3fe9b783e1b927e138b3085 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 21:37:08 -0500 Subject: [PATCH 29/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 2c6c9ac8..4835b74d 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -82,7 +82,7 @@ pipeline { def localToken = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() - def secretData = openshift.selector('secret', 'prod-credentials').object() + def secretData = openshift.selector('secret/prod-credentials').object().data def registry = secretData.registry def token = secretData.token From a70004448fa355d76da36d54361e9f62652101de Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 21:54:30 -0500 Subject: [PATCH 30/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 4835b74d..09837b91 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -83,8 +83,8 @@ pipeline { def localToken = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() def secretData = openshift.selector('secret/prod-credentials').object().data - def registry = secretData.registry - def token = secretData.token + def registry = Base64.decodeBase64(secretData.registry) + def token = Base64.decodeBase64(secretData.token) openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository From ac7b48fe5eaa1eead47a3d4337a16162c7bd4ad4 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 21:58:26 -0500 Subject: [PATCH 31/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 09837b91..80fc3475 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -83,8 +83,8 @@ pipeline { def localToken = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() def secretData = openshift.selector('secret/prod-credentials').object().data - def registry = Base64.decodeBase64(secretData.registry) - def token = Base64.decodeBase64(secretData.token) + def registry = Base64.decodeBase64(secretData.registry.getByte()) + def token = Base64.decodeBase64(secretData.token.getByte()) openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository From 07a445f6ef53bee4dd62c4d07c680229bbae368f Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 22:04:25 -0500 Subject: [PATCH 32/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 80fc3475..59d248dc 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -83,8 +83,8 @@ pipeline { def localToken = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() def secretData = openshift.selector('secret/prod-credentials').object().data - def registry = Base64.decodeBase64(secretData.registry.getByte()) - def token = Base64.decodeBase64(secretData.token.getByte()) + def registry = Base64.decodeBase64(secretData.registry.getBytes()) + def token = Base64.decodeBase64(secretData.token.getBytes()) openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository From 2f958d71badac46266f84a9e01781a7a4f2d487c Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 22:15:00 -0500 Subject: [PATCH 33/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 59d248dc..231d8303 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -83,17 +83,18 @@ pipeline { def localToken = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() def secretData = openshift.selector('secret/prod-credentials').object().data - def registry = Base64.decodeBase64(secretData.registry.getBytes()) - def token = Base64.decodeBase64(secretData.token.getBytes()) + def registry = secretData.registry + def token = secretData.token openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${registry}/${STAGE3}/${APP_NAME}" sh """ + set +x skopeo copy --remove-signatures \ --src-creds openshift:${localToken} --src-cert-dir /run/secrets/kubernetes.io/serviceaccount/ca.crt \ - --dest-creds openshift:${token} --dest-tls-verify false \ - docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://${registry}/${STAGE3}/${APP_NAME} + --dest-creds openshift:\$(echo ${token} | base64 --decode) --dest-tls-verify false \ + docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://\$(echo ${registry} | base64 --decode)/${STAGE3}/${APP_NAME} """ } From 01542e606dc7227ceca12593bc5351e5f8525646 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 22:21:57 -0500 Subject: [PATCH 34/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 231d8303..225a2d47 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -83,18 +83,19 @@ pipeline { def localToken = readFile('/var/run/secrets/kubernetes.io/serviceaccount/token').trim() def secretData = openshift.selector('secret/prod-credentials').object().data - def registry = secretData.registry - def token = secretData.token + def encodedRegistry = secretData.registry + def encodedToken = secretData.token + def registry = sh(script:"set +x; echo ${encodedRegistry} | base64 --decode", returnStdout: true) + def token = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) openshift.withProject("${STAGE2}") { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository - echo "Promoting ${imageRegistry}/${STAGE2}/${APP_NAME} -> ${registry}/${STAGE3}/${APP_NAME}" + echo "Promoting ${imageRegistry} -> ${registry}/${STAGE3}/${APP_NAME}" sh """ - set +x skopeo copy --remove-signatures \ --src-creds openshift:${localToken} --src-cert-dir /run/secrets/kubernetes.io/serviceaccount/ca.crt \ - --dest-creds openshift:\$(echo ${token} | base64 --decode) --dest-tls-verify false \ - docker://${imageRegistry}/${STAGE2}/${APP_NAME} docker://\$(echo ${registry} | base64 --decode)/${STAGE3}/${APP_NAME} + --dest-creds openshift:${token} --dest-tls-verify false \ + docker://${imageRegistry} docker://${registry}/${STAGE3}/${APP_NAME} """ } From 7e84e15d73ce7205ded243da87402011f804ba2c Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 22:30:44 -0500 Subject: [PATCH 35/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 225a2d47..7e5966ce 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -93,8 +93,8 @@ pipeline { echo "Promoting ${imageRegistry} -> ${registry}/${STAGE3}/${APP_NAME}" sh """ skopeo copy --remove-signatures \ - --src-creds openshift:${localToken} --src-cert-dir /run/secrets/kubernetes.io/serviceaccount/ca.crt \ - --dest-creds openshift:${token} --dest-tls-verify false \ + --src-creds openshift:${localToken} --src-cert-dir=/run/secrets/kubernetes.io/serviceaccount/ \ + --dest-creds openshift:${token} --dest-tls-verify=false \ docker://${imageRegistry} docker://${registry}/${STAGE3}/${APP_NAME} """ } From c2de140e4cde415943334254a91001677554d4aa Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 22:39:08 -0500 Subject: [PATCH 36/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 7e5966ce..3fa7c033 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -60,6 +60,23 @@ pipeline { } } + stage ('Verify Deployment to Stage') { + steps { + script { + openshift.withCluster() { + openshift.withProject("${STAGE2}") { + def dcObj = openshift.selector('dc', env.APP_NAME).object() + def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"]) + podSelector.untilEach { + echo "pod: ${it.name()}" + return it.object().status.containerStatuses[0].ready + } + } + } + } + } + } + stage('Promote to Prod') { agent { kubernetes { @@ -102,6 +119,30 @@ pipeline { } } } + + stage ('Verify Deployment to Prod') { + steps { + script { + openshift.withCluster() { + def secretData = openshift.selector('secret/prod-credentials').object().data + def encodedAPI = secretData.api + def encodedToken = secretData.token + def api = sh(script:"set +x; echo ${encodedAPI} | base64 --decode", returnStdout: true) + def token = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) + } + openshift.withCluster( api, token ) { + openshift.withProject("${STAGE3}") { + def dcObj = openshift.selector('dc', env.APP_NAME).object() + def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"]) + podSelector.untilEach { + echo "pod: ${it.name()}" + return it.object().status.containerStatuses[0].ready + } + } + } + } + } + } } } } From c3ea77f26263bccfec7b6d07f1688032ebda10ad Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 22:49:05 -0500 Subject: [PATCH 37/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 41 +++++++++++++++------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 3fa7c033..dc214a9e 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -11,14 +11,18 @@ openshift.withCluster() { pipeline { + agent { label 'maven' } + stages { + stage('Code Build') { steps { git url: "${SOURCE_CODE_URL}" sh "mvn clean install" } } + stage('Image Build') { steps { sh """ @@ -119,30 +123,31 @@ pipeline { } } } + } - stage ('Verify Deployment to Prod') { - steps { - script { - openshift.withCluster() { - def secretData = openshift.selector('secret/prod-credentials').object().data - def encodedAPI = secretData.api - def encodedToken = secretData.token - def api = sh(script:"set +x; echo ${encodedAPI} | base64 --decode", returnStdout: true) - def token = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) - } - openshift.withCluster( api, token ) { - openshift.withProject("${STAGE3}") { - def dcObj = openshift.selector('dc', env.APP_NAME).object() - def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"]) - podSelector.untilEach { - echo "pod: ${it.name()}" - return it.object().status.containerStatuses[0].ready - } + stage ('Verify Deployment to Prod') { + steps { + script { + openshift.withCluster() { + def secretData = openshift.selector('secret/prod-credentials').object().data + def encodedAPI = secretData.api + def encodedToken = secretData.token + def api = sh(script:"set +x; echo ${encodedAPI} | base64 --decode", returnStdout: true) + def token = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) + } + openshift.withCluster( api, token ) { + openshift.withProject("${STAGE3}") { + def dcObj = openshift.selector('dc', env.APP_NAME).object() + def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"]) + podSelector.untilEach { + echo "pod: ${it.name()}" + return it.object().status.containerStatuses[0].ready } } } } } } + } } From 0ad07004c2ca28bae9ef05f5e869c7f186db30a4 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 22:57:47 -0500 Subject: [PATCH 38/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index dc214a9e..d7d366be 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -132,10 +132,10 @@ pipeline { def secretData = openshift.selector('secret/prod-credentials').object().data def encodedAPI = secretData.api def encodedToken = secretData.token - def api = sh(script:"set +x; echo ${encodedAPI} | base64 --decode", returnStdout: true) - def token = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) + env.API = sh(script:"set +x; echo ${encodedAPI} | base64 --decode", returnStdout: true) + end.TOKEN = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) } - openshift.withCluster( api, token ) { + openshift.withCluster( env.API, env.TOKEN ) { openshift.withProject("${STAGE3}") { def dcObj = openshift.selector('dc', env.APP_NAME).object() def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"]) From 3dbe6096ed19d354e5f9db8a6635f0e2ba21000d Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 23:01:47 -0500 Subject: [PATCH 39/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index d7d366be..001793f7 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -133,7 +133,7 @@ pipeline { def encodedAPI = secretData.api def encodedToken = secretData.token env.API = sh(script:"set +x; echo ${encodedAPI} | base64 --decode", returnStdout: true) - end.TOKEN = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) + env.TOKEN = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) } openshift.withCluster( env.API, env.TOKEN ) { openshift.withProject("${STAGE3}") { From c3ea68a787c647cd81d4d56e3e0e31b541da6c54 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 23:19:15 -0500 Subject: [PATCH 40/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 001793f7..94144a43 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -137,12 +137,14 @@ pipeline { } openshift.withCluster( env.API, env.TOKEN ) { openshift.withProject("${STAGE3}") { + openshift.skipTLSVerify(true) def dcObj = openshift.selector('dc', env.APP_NAME).object() def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"]) podSelector.untilEach { echo "pod: ${it.name()}" return it.object().status.containerStatuses[0].ready } + openshift.skipTLSVerify(false) } } } From 32425a0d0e41fa4557945f00b406f5957653bc80 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 23:28:19 -0500 Subject: [PATCH 41/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 94144a43..350783fc 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -132,19 +132,17 @@ pipeline { def secretData = openshift.selector('secret/prod-credentials').object().data def encodedAPI = secretData.api def encodedToken = secretData.token - env.API = sh(script:"set +x; echo ${encodedAPI} | base64 --decode", returnStdout: true) + env.API = sh(script:"set +x; echo ${encodedAPI} | base64 --decode", returnStdout: true).replaceAll(/https?/, 'insecure') env.TOKEN = sh(script:"set +x; echo ${encodedToken} | base64 --decode", returnStdout: true) } openshift.withCluster( env.API, env.TOKEN ) { openshift.withProject("${STAGE3}") { - openshift.skipTLSVerify(true) def dcObj = openshift.selector('dc', env.APP_NAME).object() def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"]) podSelector.untilEach { echo "pod: ${it.name()}" return it.object().status.containerStatuses[0].ready } - openshift.skipTLSVerify(false) } } } From 7ad965d2749558cb8c6cc1f15bd752f848995243 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 23:34:13 -0500 Subject: [PATCH 42/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 350783fc..0e7bca52 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -19,7 +19,7 @@ pipeline { stage('Code Build') { steps { git url: "${SOURCE_CODE_URL}" - sh "mvn clean install" + sh "mvn clean install -q" } } From cc538eef755118ec4c49ce9c23043a8ef491ee44 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 23:38:04 -0500 Subject: [PATCH 43/45] Enable promotion step --- multi-cluster-spring-boot/Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/multi-cluster-spring-boot/Jenkinsfile b/multi-cluster-spring-boot/Jenkinsfile index 0e7bca52..f3a0e106 100644 --- a/multi-cluster-spring-boot/Jenkinsfile +++ b/multi-cluster-spring-boot/Jenkinsfile @@ -25,7 +25,9 @@ pipeline { stage('Image Build') { steps { + echo 'Building Image from Jar File' sh """ + set +x rm -rf oc-build && mkdir -p oc-build/deployments for t in \$(echo "jar;war;ear" | tr ";" "\\n"); do cp -rfv ./target/*.\$t oc-build/deployments/ 2> /dev/null || echo "No \$t files" @@ -113,6 +115,7 @@ pipeline { def imageRegistry = openshift.selector( 'is', "${APP_NAME}").object().status.dockerImageRepository echo "Promoting ${imageRegistry} -> ${registry}/${STAGE3}/${APP_NAME}" sh """ + set +x skopeo copy --remove-signatures \ --src-creds openshift:${localToken} --src-cert-dir=/run/secrets/kubernetes.io/serviceaccount/ \ --dest-creds openshift:${token} --dest-tls-verify=false \ From 08be3c9c570039d73cac94bfaf310fda0d5f5294 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 21 Feb 2018 23:43:27 -0500 Subject: [PATCH 44/45] README fix and jenkins memory limit update --- multi-cluster-spring-boot/README.md | 2 +- multi-cluster-spring-boot/applier/params/jenkins | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-cluster-spring-boot/README.md b/multi-cluster-spring-boot/README.md index bcd04918..8ecd98f1 100644 --- a/multi-cluster-spring-boot/README.md +++ b/multi-cluster-spring-boot/README.md @@ -30,7 +30,7 @@ This quickstart can be deployed quickly using Ansible. Here are the steps. ``` 3. One of the things that was created by ansible is a `ServiceAccount` that will be used for promoting your app from _Dev_ to _Prod_. We'll need to extract its credentials so that our pipeline can use that account. ``` - TOKEN=$(oc serviceaccounts get-token promoter -n field-guides-prod) + TOKEN=$(oc serviceaccounts get-token promoter -n multicluster-spring-boot-prod) ``` The Ansible automation for your _Dev_ cluster will expect a parameters file to be created at `./applier/params/prod-credentials`. It should look something like this: ``` diff --git a/multi-cluster-spring-boot/applier/params/jenkins b/multi-cluster-spring-boot/applier/params/jenkins index e009ad5f..e29bc258 100644 --- a/multi-cluster-spring-boot/applier/params/jenkins +++ b/multi-cluster-spring-boot/applier/params/jenkins @@ -1 +1 @@ -MEMORY_LIMIT=768Mi +MEMORY_LIMIT=1.5Gi From 053787e4eae33976976a850c8aaea850ea6b6528 Mon Sep 17 00:00:00 2001 From: Eric Sauer Date: Wed, 28 Feb 2018 09:19:32 -0500 Subject: [PATCH 45/45] Ading ansible-galaxy for dependency management --- multi-cluster-spring-boot/README.md | 12 +++++++----- multi-cluster-spring-boot/requirements.yml | 8 ++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 multi-cluster-spring-boot/requirements.yml diff --git a/multi-cluster-spring-boot/README.md b/multi-cluster-spring-boot/README.md index 8ecd98f1..667e7b82 100644 --- a/multi-cluster-spring-boot/README.md +++ b/multi-cluster-spring-boot/README.md @@ -21,14 +21,16 @@ In order to run this pipeline, you will need: This quickstart can be deployed quickly using Ansible. Here are the steps. -1. Clone [this repo](https://github.com/redhat-cop/container-pipelines.git) and the [casl-ansible](https://github.com/redhat-cop/casl-ansible.git) repo. -2. Log into your _Prod_ OpenShift cluster, and run the following command. +1. Clone [this repo](https://github.com/redhat-cop/container-pipelines.git) +2. `cd container-pipelines/multi-cluster-spring-boot` +3. Run `ansible-galaxy install -r requirements.yml --roles-path=roles` +4. Log into your _Prod_ OpenShift cluster, and run the following command. ``` $ oc login ... - $ ansible-playbook -i ./applier/inventory-prod/ /path/to/casl-ansible/playbooks/openshift-cluster-seed.yml + $ ansible-playbook -i ./applier/inventory-prod/ roles/casl-ansible/playbooks/openshift-cluster-seed.yml ``` -3. One of the things that was created by ansible is a `ServiceAccount` that will be used for promoting your app from _Dev_ to _Prod_. We'll need to extract its credentials so that our pipeline can use that account. +5. One of the things that was created by ansible is a `ServiceAccount` that will be used for promoting your app from _Dev_ to _Prod_. We'll need to extract its credentials so that our pipeline can use that account. ``` TOKEN=$(oc serviceaccounts get-token promoter -n multicluster-spring-boot-prod) ``` @@ -39,7 +41,7 @@ This quickstart can be deployed quickly using Ansible. Here are the steps. REGISTRY_URL=docker-registry-default.apps.example.com " > ./applier/params/prod-credentials ``` -4. Now, Log into your _Dev_ cluster, and instantiate the pipeline. +6. Now, Log into your _Dev_ cluster, and instantiate the pipeline. ``` $ oc login ... diff --git a/multi-cluster-spring-boot/requirements.yml b/multi-cluster-spring-boot/requirements.yml new file mode 100644 index 00000000..26d57cb7 --- /dev/null +++ b/multi-cluster-spring-boot/requirements.yml @@ -0,0 +1,8 @@ +# This is the Ansible Galaxy requirements file to pull in the correct roles +# to support the operation of CASL provisioning/runs. + +# From 'casl-ansible' +- name: casl-ansible + scm: git + src: https://github.com/redhat-cop/casl-ansible + version: v3.7.1