diff --git a/multi-cluster-multi-branch-jee/.applier/.gitignore b/multi-cluster-multi-branch-jee/.applier/.gitignore new file mode 100644 index 00000000..82047485 --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/.gitignore @@ -0,0 +1,2 @@ +roles +*.retry \ No newline at end of file diff --git a/multi-cluster-multi-branch-jee/.applier/apply.yml b/multi-cluster-multi-branch-jee/.applier/apply.yml new file mode 100644 index 00000000..c36c4bf4 --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/apply.yml @@ -0,0 +1,6 @@ +--- +- name: Run OpenShift applier + hosts: "{{ app_env }}" + tasks: + - include_role: + name: openshift-applier/roles/openshift-applier diff --git a/multi-cluster-multi-branch-jee/.applier/inventory/group_vars/all.yml b/multi-cluster-multi-branch-jee/.applier/inventory/group_vars/all.yml new file mode 100644 index 00000000..8c0a8db6 --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/inventory/group_vars/all.yml @@ -0,0 +1,7 @@ +--- +# Globals +ansible_connection: local + +# NOTE: openshift_templates_raw MUST be accessible to OpenShift without authentication +openshift_templates_raw: 'https://raw.githubusercontent.com/redhat-cop/openshift-templates' +openshift_templates_raw_version_tag: 'v1.4.9' diff --git a/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-build.yml b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-build.yml new file mode 100644 index 00000000..e74946ca --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-build.yml @@ -0,0 +1,135 @@ +--- +create_project_params: + NAMESPACE: '{{ ci_cd_namespace }}' + NAMESPACE_DISPLAY_NAME: '{{ ci_cd_namespace }}' + +jenkins_rolebinding_params: + JENKINS_NAMESPACE: '{{ ci_cd_namespace }}' + +group_rolebinding_params: + ROLE: 'admin' + GROUP: "{{ app_owner_group_name }}" + +build_params: + NAME: '{{ app_name }}' + PUSH_SECRET: '{{ app_build_push_secret }}' + PULL_SECRET: '{{ app_build_pull_secret }}' + DESTINATION_IMAGE_NAME: '{{ app_build_destination_image_name }}' + DESTINATION_REPO_NAME: '{{ app_build_destination_repo_name }}' + DESTINATION_IMAGE_TAG: '{{ app_build_destination_image_tag }}' + DESTINATION_REPO_NAMESPACE: '{{ app_build_destination_repo_namespace }}' + BUILDER_IMAGE_NAME: '{{ app_build_builder_image_name }}' + MAVEN_MIRROR_URL: '{{ app_build_maven_mirror_url }}' + MAVEN_ARGS_APPEND: '{{ app_build_maven_args_append }}' + +cluster_credential_dev_secret_params: + NAME: 'cluster-credential-dev' + API: "TODO_CLUSTER_API_ADDRESS_EX_https://ocp.non-prod.example.com" + TOKEN: TODO_VAULTED_CLUSTER_API_TOKEN + +cluster_credential_test_secret_params: + NAME: 'cluster-credential-test' + API: "TODO_CLUSTER_API_ADDRESS_EX_https://ocp.non-prod.example.com" + TOKEN: TODO_VAULTED_CLUSTER_API_TOKEN + +cluster_credential_qa_secret_params: + NAME: 'cluster-credential-qa' + API: "TODO_CLUSTER_API_ADDRESS_EX_https://ocp.non-prod.example.com" + TOKEN: TODO_VAULTED_CLUSTER_API_TOKEN + +cluster_credential_prod_secret_params: + NAME: 'cluster-credential-prod' + API: "TODO_CLUSTER_API_ADDRESS_EX_https://ocp.prod.example.com" + TOKEN: TODO_VAULTED_CLUSTER_API_TOKEN + +image_pull_repo_credential_secret_params: + NAME: 'image-pull-repo-credential' + REPO_FQDN: 'registry.example.com' + REPO_AUTH: TODO_VAULTED_REPO_AUTH + +image_push_repo_credential_secret_params: + NAME: 'image-push-repo-credential' + REPO_FQDN: 'registry.example.com' + REPO_AUTH: TODO_VAULTED_REPO_AUTH + +openshift_cluster_content: + - object: deploy-cicd + content: + - name: '{{ app_name }}-{{ ci_cd_namespace }}-create' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/project-requests/create-project.yml' + action: create + params_from_vars: '{{ create_project_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - deploy + - cicd + - name: '{{ app_name }}-{{ ci_cd_namespace }}-jenkins-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/jenkins-rolebinding-template.yml' + params_from_vars: '{{ jenkins_rolebinding_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - deploy + - dev + - name: '{{ app_name }}-{{ ci_cd_namespace }}-owner-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/group-rolebinding-template.yml' + params_from_vars: '{{ group_rolebinding_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - deploy + - dev + - name: '{{ app_name }}-build' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/app-build/s2i-build-template-binary-to-dockerimage-with-pull-and-push-secrets.yaml' + params_from_vars: '{{ build_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - build + - deploy + - cicd + - name: '{{ app_name }}-secret-cluster-credential-dev' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/secrets/cluster-credential-secret.yml' + params_from_vars: '{{ cluster_credential_dev_secret_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - build + - deploy + - cicd + - name: '{{ app_name }}-secret-cluster-credential-test' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/secrets/cluster-credential-secret.yml' + params_from_vars: '{{ cluster_credential_test_secret_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - build + - deploy + - cicd + - name: '{{ app_name }}-secret-cluster-credential-qa' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/secrets/cluster-credential-secret.yml' + params_from_vars: '{{ cluster_credential_qa_secret_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - build + - deploy + - cicd + - name: '{{ app_name }}-secret-cluster-credential-prod' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/secrets/cluster-credential-secret.yml' + params_from_vars: '{{ cluster_credential_prod_secret_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - build + - deploy + - cicd + - name: '{{ app_name }}-secret-image-pull-repo-credential' + template: 'https://raw.github.epb.net/EASL/epb-openshift-templates/master/docker-config-json-secret.yml?token=AAAAYZCqr6sQPbRRGcezspQP-y5Ylwboks5dPwXKwA%3D%3D' + params_from_vars: '{{ image_pull_repo_credential_secret_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - build + - deploy + - cicd + - name: '{{ app_name }}-secret-image-push-repo-credential' + template: 'https://raw.github.epb.net/EASL/epb-openshift-templates/master/docker-config-json-secret.yml?token=AAAAYZCqr6sQPbRRGcezspQP-y5Ylwboks5dPwXKwA%3D%3D' + params_from_vars: '{{ image_push_repo_credential_secret_params }}' + namespace: '{{ ci_cd_namespace }}' + tags: + - build + - deploy + - cicd diff --git a/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-dev.yml b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-dev.yml new file mode 100644 index 00000000..beb7aabf --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-dev.yml @@ -0,0 +1,51 @@ +--- +create_project: + NAMESPACE: '{{ namespace }}' + NAMESPACE_DISPLAY_NAME: '{{ namespace }}' + +jenkins_rolebinding: + JENKINS_NAMESPACE: '{{ ci_cd_namespace }}' + +group_rolebinding: + ROLE: 'admin' + GROUP: "{{ app_owner_group_name }}" + +app_deploy: + NAME: '{{ app_name }}' + APP_TAG: '{{ app_image_tag }}' + NAMESPACE: '{{ app_image_namespace }}' + IMAGE_REPO: '{{ repo_name }}' + IMAGE_PULL_SECRET: '{{ image_pull_secret }}' + +openshift_cluster_content: +- object: deploy-dev + content: + - name: '{{ app_name }}-{{ namespace }}-create' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/project-requests/create-project.yml' + action: create + params_from_vars: '{{ create_project }}' + namespace: '{{ namespace }}' + tags: + - deploy + - dev + - name: '{{ app_name }}-{{ namespace }}-jenkins-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/jenkins-rolebinding-template.yml' + params_from_vars: '{{ jenkins_rolebinding }}' + namespace: '{{ namespace }}' + tags: + - deploy + - dev + - name: '{{ app_name }}-{{ namespace }}-owner-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/group-rolebinding-template.yml' + params_from_vars: '{{ group_rolebinding }}' + namespace: '{{ namespace }}' + tags: + - deploy + - dev + - name: '{{ app_name }}-{{ namespace }}-deploy' + template: '{{ inventory_dir }}/../.openshift/app-deploy-jboss-eap.yml' + params_from_vars: '{{ app_deploy }}' + namespace: '{{ namespace }}' + tags: + - deploy + - dev diff --git a/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-prod.yml b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-prod.yml new file mode 100644 index 00000000..ba8e090d --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-prod.yml @@ -0,0 +1,51 @@ +--- +create_project: + NAMESPACE: '{{ namespace }}' + NAMESPACE_DISPLAY_NAME: '{{ namespace }}' + +jenkins_rolebinding: + JENKINS_NAMESPACE: '{{ ci_cd_namespace }}' + +group_rolebinding: + ROLE: 'admin' + GROUP: "{{ app_owner_group_name }}" + +app_deploy: + NAME: '{{ app_name }}' + APP_TAG: '{{ app_image_tag }}' + NAMESPACE: '{{ app_image_namespace }}' + IMAGE_REPO: '{{ repo_name }}' + IMAGE_PULL_SECRET: '{{ image_pull_secret }}' + +openshift_cluster_content: +- object: deploy-prod + content: + - name: '{{ app_name }}-{{ namespace }}-create' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/project-requests/create-project.yml' + action: create + params_from_vars: '{{ create_project }}' + namespace: '{{ namespace }}' + tags: + - deploy + - prod + - name: '{{ app_name }}-{{ namespace }}-jenkins-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/jenkins-rolebinding-template.yml' + params_from_vars: '{{ jenkins_rolebinding }}' + namespace: '{{ namespace }}' + tags: + - deploy + - prod + - name: '{{ app_name }}-{{ namespace }}-owner-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/group-rolebinding-template.yml' + params_from_vars: '{{ group_rolebinding }}' + namespace: '{{ namespace }}' + tags: + - deploy + - prod + - name: '{{ app_name }}-{{ namespace }}-deploy' + template: '{{ inventory_dir }}/../.openshift/app-deploy-jboss-eap.yml' + params_from_vars: '{{ app_deploy }}' + namespace: '{{ namespace }}' + tags: + - deploy + - prod diff --git a/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-qa.yml b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-qa.yml new file mode 100644 index 00000000..5ff8fd5b --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-qa.yml @@ -0,0 +1,51 @@ +--- +create_project: + NAMESPACE: '{{ namespace }}' + NAMESPACE_DISPLAY_NAME: '{{ namespace }}' + +jenkins_rolebinding: + JENKINS_NAMESPACE: '{{ ci_cd_namespace }}' + +group_rolebinding: + ROLE: 'admin' + GROUP: "{{ app_owner_group_name }}" + +app_deploy: + NAME: '{{ app_name }}' + APP_TAG: '{{ app_image_tag }}' + NAMESPACE: '{{ app_image_namespace }}' + IMAGE_REPO: '{{ repo_name }}' + IMAGE_PULL_SECRET: '{{ image_pull_secret }}' + +openshift_cluster_content: +- object: deploy-qa + content: + - name: '{{ app_name }}-{{ namespace }}-create' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/project-requests/create-project.yml' + action: create + params_from_vars: '{{ create_project }}' + namespace: '{{ namespace }}' + tags: + - deploy + - qa + - name: '{{ app_name }}-{{ namespace }}-jenkins-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/jenkins-rolebinding-template.yml' + params_from_vars: '{{ jenkins_rolebinding }}' + namespace: '{{ namespace }}' + tags: + - deploy + - qa + - name: '{{ app_name }}-{{ namespace }}-owner-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/group-rolebinding-template.yml' + params_from_vars: '{{ group_rolebinding }}' + namespace: '{{ namespace }}' + tags: + - deploy + - qa + - name: '{{ app_name }}-{{ namespace }}-deploy' + template: '{{ inventory_dir }}/../.openshift/app-deploy-jboss-eap.yml' + params_from_vars: '{{ app_deploy }}' + namespace: '{{ namespace }}' + tags: + - deploy + - qa diff --git a/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-test.yml b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-test.yml new file mode 100644 index 00000000..6f89bec3 --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/inventory/host_vars/app-test.yml @@ -0,0 +1,51 @@ +--- +create_project: + NAMESPACE: '{{ namespace }}' + NAMESPACE_DISPLAY_NAME: '{{ namespace }}' + +jenkins_rolebinding: + JENKINS_NAMESPACE: '{{ ci_cd_namespace }}' + +group_rolebinding: + ROLE: 'admin' + GROUP: "{{ app_owner_group_name }}" + +app_deploy: + NAME: '{{ app_name }}' + APP_TAG: '{{ app_image_tag }}' + NAMESPACE: '{{ app_image_namespace }}' + IMAGE_REPO: '{{ repo_name }}' + IMAGE_PULL_SECRET: '{{ image_pull_secret }}' + +openshift_cluster_content: +- object: deploy-test + content: + - name: '{{ app_name }}-{{ namespace }}-create' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/project-requests/create-project.yml' + action: create + params_from_vars: '{{ create_project }}' + namespace: '{{ namespace }}' + tags: + - deploy + - test + - name: '{{ app_name }}-{{ namespace }}-jenkins-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/jenkins-rolebinding-template.yml' + params_from_vars: '{{ jenkins_rolebinding }}' + namespace: '{{ namespace }}' + tags: + - deploy + - test + - name: '{{ app_name }}-{{ namespace }}-owner-role-binding' + template: '{{ openshift_templates_raw }}/{{ openshift_templates_raw_version_tag }}/role-bindings/group-rolebinding-template.yml' + params_from_vars: '{{ group_rolebinding }}' + namespace: '{{ namespace }}' + tags: + - deploy + - test + - name: '{{ app_name }}-{{ namespace }}-deploy' + template: '{{ inventory_dir }}/../.openshift/app-deploy-jboss-eap.yml' + params_from_vars: '{{ app_deploy }}' + namespace: '{{ namespace }}' + tags: + - deploy + - test diff --git a/multi-cluster-multi-branch-jee/.applier/inventory/hosts b/multi-cluster-multi-branch-jee/.applier/inventory/hosts new file mode 100644 index 00000000..ada3e45d --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/inventory/hosts @@ -0,0 +1,8 @@ +[app-build] +app-build + +[app-deploy] +app-dev +app-test +app-qa +app-prod diff --git a/multi-cluster-multi-branch-jee/.applier/requirements.yml b/multi-cluster-multi-branch-jee/.applier/requirements.yml new file mode 100644 index 00000000..c62f04f2 --- /dev/null +++ b/multi-cluster-multi-branch-jee/.applier/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 'openshift-applier' +- src: https://github.com/redhat-cop/openshift-applier + scm: git + version: v2.1.1 + name: openshift-applier diff --git a/multi-cluster-multi-branch-jee/.gitignore b/multi-cluster-multi-branch-jee/.gitignore new file mode 100644 index 00000000..1377554e --- /dev/null +++ b/multi-cluster-multi-branch-jee/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/multi-cluster-multi-branch-jee/.openshift/app-deploy-jboss-eap.yml b/multi-cluster-multi-branch-jee/.openshift/app-deploy-jboss-eap.yml new file mode 100644 index 00000000..655bfb71 --- /dev/null +++ b/multi-cluster-multi-branch-jee/.openshift/app-deploy-jboss-eap.yml @@ -0,0 +1,164 @@ +--- +kind: Template +apiVersion: v1 +metadata: + name: ${NAME}-deploy-jboss-eap + annotations: + openshift.io/display-name: Sample JBoss EAP JEE template + description: A sample template to deploy your JBoss EAP JEE App with a HTTPS route + iconClass: icon-cube + tags: jee,eap +labels: + template: ${NAME}-deploy-template +objects: +- apiVersion: v1 + kind: DeploymentConfig + metadata: + name: "${NAME}" + labels: + app: "${NAME}" + spec: + replicas: 1 + revisionHistoryLimit: 10 + selector: + name: "${NAME}" + strategy: + activeDeadlineSeconds: 21600 + recreateParams: + timeoutSeconds: 600 + resources: {} + type: Rolling + template: + metadata: + creationTimestamp: null + labels: + name: "${NAME}" + spec: + containers: + - image: "${IMAGE_REPO}/${NAMESPACE}/${NAME}:${APP_TAG}" + imagePullPolicy: Always + name: "${NAME}" + - name: JAVA_OPTS_APPEND + value: "${JAVA_OPTS_APPEND}" + livenessProbe: + exec: + command: + - /bin/bash + - '-c' + - /opt/eap/bin/livenessProbe.sh + failureThreshold: 3 + initialDelaySeconds: 60 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: eap-app + ports: + - containerPort: 8778 + name: jolokia + protocol: TCP + - containerPort: 8080 + name: http + protocol: TCP + - containerPort: 8888 + name: ping + protocol: TCP + readinessProbe: + exec: + command: + - /bin/bash + - '-c' + - /opt/eap/bin/readinessProbe.sh + failureThreshold: 3 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + memory: 1Gi + requests: + memory: 256Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 75 + test: false + triggers: [] +- apiVersion: v1 + kind: Service + metadata: + labels: + name: "${NAME}" + name: "${NAME}" + spec: + ports: + - port: 8080 + protocol: TCP + targetPort: 8080 + selector: + name: "${NAME}" + sessionAffinity: None + type: ClusterIP +- apiVersion: v1 + kind: Service + metadata: + labels: + name: "${NAME}-ping" + name: "${NAME}-ping" + spec: + ports: + - name: ping + port: 8888 + protocol: TCP + targetPort: 8888 + publishNotReadyAddresses: true + selector: + name: "${NAME}" + sessionAffinity: None + type: ClusterIP +- apiVersion: v1 + kind: Route + metadata: + labels: + name: "${NAME}" + name: "${NAME}" + spec: + port: + targetPort: 8080 + path: / + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: "${NAME}" + weight: 100 + wildcardPolicy: None +parameters: +- name: NAME + displayName: Name + description: The name assigned to all objects and the related imagestream. + required: true +- name: APP_TAG + displayName: App Tag + description: The tag of the image to use eg latest. + value: latest + required: true +- name: NAMESPACE + displayName: Docker image namespace + description: The namespace of the image to use eg js-apps. + required: true +- name: IMAGE_REPO + displayName: Docker image repository + description: The docker repository containing the image to use + required: true + value: docker-registry.default.svc:5000 +- name: IMAGE_PULL_SECRET + displayName: Docker image repository pull secret + description: The secret used to pull images from the docker repository + required: true +- name: JAVA_OPTS_APPEND + displayName: Java Options Append + description: Additional Java options to append to the JEE runtime diff --git a/multi-cluster-multi-branch-jee/Jenkinsfile.example b/multi-cluster-multi-branch-jee/Jenkinsfile.example new file mode 100644 index 00000000..7aed2971 --- /dev/null +++ b/multi-cluster-multi-branch-jee/Jenkinsfile.example @@ -0,0 +1,23 @@ +library( + identifier: 'my-orgs-pipeline-library@master', + retriever: modernSCM([ + $class: 'GitSCMSource', + remote: 'TODO', + credentialsId: 'TODO_IF_NOT_PUBLIC_REPO' + ]) +) + +pipelineJEE8( + 'my-app-foo', // applicationName + 'my-service-hello', // serviceName + 'my-ocp-user-group', // ownerGroupName + 'https://my-image-push-registry.example.xyz', // imagePushRegistry + 'https://my-image-pull-registry.example.xyz', // imagePullRegistry + 'my-jenkins-credential-with-my-ansible-vault-password' // ansibleVaultJenkinsCredentialName + 'image-push-repo-credential', // immagePushSecret - default value + 'image-pull-repo-credential', // immagePullSecret - default value + 'jboss-eap-7/eap72-openjdk11-openshift-rhel8:1.0', // builderImage - default value + 'https://my-maven-mirror.example.xyz', // mavenMirrorUrl - optional + '-Dcom.redhat.xpaas.repo.jbossorg', // mvnAdditionalArgs - optional. default value + 'sonarQubeEnv' // sonarQubeEnv - optional +) diff --git a/multi-cluster-multi-branch-jee/README.md b/multi-cluster-multi-branch-jee/README.md new file mode 100644 index 00000000..e53805ae --- /dev/null +++ b/multi-cluster-multi-branch-jee/README.md @@ -0,0 +1,138 @@ +# Sample Multi-Cluster with GitFlow Pipeline for JEE Application +This example demonstrates and provides a framework for creating a Multi-Cluster Pipeline using GitFlow source control patterns. This sample demonstrates the following capabilities: + +* reusable pipeline groovy function for use with multiple JEE applications +* Multi-cluster promotion ready by specifying remote cluster credentials and remote image repository credentials +* Designed to work with external image repository such as Red Hat Quay, Sonatype Nexus, or JFrog Artifactory. +* Stages include + * SETUP + * BUILD + * DEV + * TEST + * QA + * PROD +* Example stubbed out openshift-applier inventories for fully infrastructure as code using openshift-applier and Ansible. Pipeline is capability of building ALL required OpenShift projects and resources on demand on the fly as part of the pipeline. + +## Prerequisites +In order to run this pipeline, you will need: + +* Jenkins +* One (1) or more OpenShift clusters + * Tested on OpenShift 3.11, but likely works on older and newer versions +* OpenShift Service Account credentials for each OpenShift cluster with permissions to create Projects +* OpenShift user group that exists in all OpenShift clusters to give owner permissions to for generated OpenShift resources +* External image repository service account credentials with permissions to pull images +* External image repository service account credentials with permissions to push images +* Optionally: configured SonarQube Environment in Jenkins + +## Deployment Instructions + +### 1. Get/Create OpenShift Service Account credentials for each OpenShift cluster that can create projects +There are many ways to go about this, this is just one option. + +``` +CLUSTER_API= +SERVICE_ACCOUNT_NAMESPACE=jenkins +SERVICE_ACCOUNT_NAME=jenkins + +oc login ${CLUSTER_API} +oc new-project ${SERVICE_ACCOUNT_NAMESPACE} +oc create sa ${SERVICE_ACCOUNT_NAME} -n ${SERVICE_ACCOUNT_NAMESPACE} +oc adm policy add-cluster-role-to-user self-provisioners system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:jenkins + +TOKEN_NAME=$(oc get secret -n ${SERVICE_ACCOUNT_NAMESPACE} | grep ${SERVICE_ACCOUNT_NAME}-token | head -n 1 | awk '{ print $1 }') +echo TOKEN_NAME=${TOKEN_NAME} +TOKEN=$(oc get secret ${TOKEN_NAME} --template='{{.data.token}}' -n ${SERVICE_ACCOUNT_NAMESPACE} | base64 --decode) +echo TOKEN=${TOKEN} +``` + +**NOTE** you will need the `TOKEN`(s) in step [6.2 update .applier/inventory/host_vars/app-build.yml](#62-update-applierinventoryhost_varsapp-buildyml) + +### 2. Get Image Repository service account credential(s) +This will depend on your Image Repository tool of choice. At minimum you will need a single service account that can push images and pull images. Depending on your Image Repository tool you may need seperate accounts for push vs pull. + +**NOTE** you will need the credentials in step [6.2 update .applier/inventory/host_vars/app-build.yml](#62-update-applierinventoryhost_varsapp-buildyml) + +### 3. Copy and modify the pipelineJEE8.groovy file +The `pipelineJEE8.groovy file provided here is meant to be a reference for you to build on and add your own stages too or modify how the git branching and promotion process works. + +At minimum you will need to: + +1. search the file for `TODO` and resolve those items.` +2. copy the file into a "public on your network" shared git repository, example `my-orgs-pipeline-library` into a `vars` directory in that repository. This repository will be good for storing your shared jenkins pipelines as well as any other jenkins utils functions not already provided in [redhat-cop/pipeline-library](https://github.com/redhat-cop/pipeline-library). + +### 4. Copy the example `Jenkinsfile.example` to your application and update + +The provided `Jenkinsfile.example` file is meant to be a reference for how to call this pipeline from one of your applications. + +1. copy `Jenkinsfile.example` to your JEE8 project +2. Edit the `library` `remote` URL +3. Optionally set the `library` `credentialsId` +4. set all the required parameters to pass to the `pipelineJEE8` function + +### 5. Copy the `.openshift` directory to your application and update +The `.openshift` directory contains reference templates specific to this pipeline that don't otherwise belong in the more generic and shareable [redhat-cop/openshift-templates](https://github.com/redhat-cop/openshift-templates). + +#### 5.1 `app-deploy-jboss-eap.yml` +This template defines the basic framework for deploying an image for a JEE application on JBoss EAP. It contains +* `DeploymentConfig` +* `Service` - http +* `Service` - ping +* `Route` - HTTPS with edge termination + +This template is meant to be updated and/or replaced with the specifics for how your application deploys, for instance updating for HTTPS passthrough or passing in additional environment variables. + +**NOTE** be sure that if you add additional parameters to this template that in step [6.3 update/copy .applier/inventory/host_vars/app-ENV_NAME.yml files](#63-updatecopy-applierinventoryhost_varsapp-env_nameyml-files) you update them to pass the new parameters. + +### 6. Copy the `.applier` directory to your application and update + +1. copy the `.applier` directory into your applications root directory as a peer to your `Jenkinsfile` + +#### 6.1 update `.applier/inventory/hosts` +The example `.applier/inventory/hosts` file assumes a process of +1. build (required) +2. dev +3. test +4. qa +5. prod + +If your promotion process includes or does not include any of these environments then adjust the host file accordingly. + +#### 6.2 update `.applier/inventory/host_vars/app-build.yml` +This host vars file is mostly completely but it has a few `TODO`s to be filled in with vaulted variables containing your cluster credentials from step [1. Get/Create OpenShift Service Account credentials for each OpenShift cluster that can create projects](#1-getcreate-openshift-service-account-credentials-for-each-openshift-cluster-that-can-create-projects) and your image repository credentials from step [2. Get Image Repository service account credential(s)](#2-get-image-repository-service-account-credentials). + +1. replace `TODO`s with required vaulted variables + * **NOTE** all vaulated variables should be vaulted using the same Ansible Vault password +2. ADVANCED: add in any other additional steps to build OpenShift resources that your build process may required out of the standard process defined here + +#### 6.3 update/copy `.applier/inventory/host_vars/app-ENV_NAME.yml` files +There are sample Ansible host vars files for `dev`, `test`, `qa`, and `prod` environments. In these examples the files are all but identical. The reason there are multiple rather than just one `app-deploy` is because in real world application each one tends to start to differ from each other with unique vaulted variables for things like DB passwords etc. In theory you could instead put those sorts of things in Jenkins credentials and then pass them through to a more generic `app-deploy` inventory, but then those credentials fall out of the everthing is code paradigm implemented here, hence the need for one host file per logical environment rather then just one generic one. You could possibly do some trickery with `group_vars` and `app-deploy` and the only having the differences in `host_vars`. It all gets to complicated for an example like this. It is suggested starting with this example, one file per environment, and playing with it from there. + + +1. update each `.applier/inventory/host_vars/app-ENV_NAME.yml` for the specifics of deploying your applications. + * little to no updates should be needed to deploy a 'hello-world' application + +### 7. Configure Jenkins +Jenkins must be setup to run this pipeline. + +#### 7.1 Install required Jenkins plugins + +* TBD + +#### 7.2 Add Jenkins Credential for ansible vault password +You vaulted a bunch of passwords/etc in your Ansible inventories, in theory they were all vaulted with the same Ansible Vault password. In order for Jenkins to be able to run the Ansible openshift-applier it will need that Ansible Vault password. Therefore you need to put the Ansible Vault password in a Jenkins credential. + +1. Create a Jenkins Credential: + * scope: so long as your new pipeline can see it that is all that matters + * name: matching that of the value of the `ansibleVaultJenkinsCredentialName` parameters you updated your Jenkinsfile (ex: `my-jenkins-credential-with-my-ansible-vault-password`) + * value: the Ansible Vault password used to create all of the vaulted values in your `.applier/inventory` directory + +#### 7.3 Add multi branch build for your application +1. create a Jenkins multi branch build that points at the git repository for your application +2. Optionally: filter the branches based on your git branching and promotion strategy defined in your copy of the `pipelineJEE8.groovy` file + +### 8. Run the pipeline +1. Run the pipeline + +### 9. Troubleshoot the pipeline +This inevitably wont work on the first try. Troubleshoot the errors as you see them, and if you are feeling generous, do PRs here to share those errors and their resolutions here. diff --git a/multi-cluster-multi-branch-jee/pipelineJEE8.groovy b/multi-cluster-multi-branch-jee/pipelineJEE8.groovy new file mode 100644 index 00000000..cbce88d8 --- /dev/null +++ b/multi-cluster-multi-branch-jee/pipelineJEE8.groovy @@ -0,0 +1,707 @@ +#!/usr/bin/env groovy + +/* The branching and promotion stratigy defined in this pipeline + * is based on the GitFlow paradigm merged with OpenShift promotion concepts. + * + * Branches to Stages to OpenShift Project Mapping + * * master - primary release branch + * * SETUP - N/A + * * BUILD - ${applicationName}-ci-cd + * * DEV - ${applicationName}-dev + * * TEST - ${applicationName}-test + * * QA - ${applicationName}-qa + * * PROD - ${applicationName}-prod + * * release/* - pre merging to master release candidate testing branch + * * SETUP - N/A + * * BUILD - ${applicationName}-ci-cd + * * DEV - ${applicationName}-dev + * * TEST - ${applicationName}-test + * * QA - ${applicationName}-qa + * * develop - branch for developer intigration before merging into a release/* or master branch + * * SETUP - N/A + * * BUILD - ${applicationName}-ci-cd + * * DEV - ${applicationName}-dev + * * hotfix/* - branch for a hotfix that needs to be tested through QA without interupting primary development + * * SETUP - N/A + * * BUILD - ${applicationName}-ci-cd-hotfix-* + * * DEV - ${applicationName}-dev-hotfix-* + * * TEST - ${applicationName}-test-hotfix-* + * * QA - ${applicationName}-qa-hotfix-* + * * feature/* - branch for a standard feature being tested by a developer + * * SETUP - N/A + * * BUILD - ${applicationName}-ci-cd-feature-* + * * DEV - ${applicationName}-dev-feature-* + * * pipeline/* - branch for testing changes to the CI/CD (pipeline) process + * * SETUP - N/A + * * BUILD - ${applicationName}-ci-cd-pipeline-* + * * DEV - ${applicationName}-dev-pipeline-* + * * TEST - ${applicationName}-test-pipeline-* + * * QA - ${applicationName}-qa-pipeline-* + * * PROD - ${applicationName}-prod-pipeline-* + */ + +/* + * @param applicationName The name of the application. This will be used as part of the dynamically created + * OpenShift Project names as well as the image group when pushing and pulling + * images form image repository. + * @param serviceName The servcie name within the applciation. Used for creating the OpenShift BuildConfig, Service, etc + * as well as the image name pushed to the image group within the image repository. + * @param ownerGroupName The OpenShift RBAC group that should own the OpenShift resources created by this pipeline. + * @param imagePushRegistry The container image repository to push images to. + * @param imagePullRegistry The container image repository to pull images from. Some external image repositories, such as Nexus, + * won't let you push to a group repository, hence the need for the seperation of push and pull + * repositories, if your repository supports pushing to a default repository within an group repository, + * such as Artifactory, then imagePushRegistry and imagePullRegistry can have the same value. + * @param ansibleVaultJenkinsCredentialName Name of the Jenkins credential that contians the Ansible Vault password to + * access vaulted variables when running Ansible. + * @param immagePushSecret Name of the OpenShift Secret that contains the credentials for pushing to the imagePushRegistry. + * Default: image-push-repo-credenetial + * @param immagePullSecret Name of the OpenShift Secret that contains the credentials for pulling from the imagePullRegistry. + * Default: image-pull-repo-credential + * @param builderImage S2I Image that supports binary builds to use to build the application service image. + * Default: jboss-eap-7/eap72-openjdk11-openshift-rhel8:1.0 + * @param mavenMirrorUrl Optional Maven Mirror URL to use when pulling Maven dependencies. + * @param mvnAdditionalArgs Additional arguments to pass to Maven build. + * Default: -Dcom.redhat.xpaas.repo.jbossorg + * @param sonarQubeEnv Sonar Qube Environment to use when running Sonar Qube Tests + */ +def call( + applicationName, + serviceName, + ownerGroupName, + imagePushRegistry, + imagePullRegistry, + ansibleVaultJenkinsCredentialName = '' + immagePushSecret = 'image-push-repo-credential', + immagePullSecret = 'image-pull-repo-credential', + builderImage = 'jboss-eap-7/eap72-openjdk11-openshift-rhel8:1.0', + mavenMirrorUrl = '', + mvnAdditionalArgs = '-Dcom.redhat.xpaas.repo.jbossorg', + sonarQubeEnv = '' +) { + // if on a feature/*, hotfix/*, or pipeline/* branch then + // use dedicated namespaces so as not to interupt primary + // develop,release/*, and master branch work flows. + String DEDICATED_NAMESPACES_BRANCHES_REGEX = "feature|hotfix|pipeline" + + // branches to build periodiacly + String TRIGGER_CRON_PERIOD = env.BRANCH_NAME == "develop" ? "@midnight" : "" + + // Jenkins workers images + String JENKINS_WORKER_IMAGE_MAVEN = 'openshift3/jenkins-agent-maven-35-rhel7:latest' + String JENKINS_WORKER_IMAGE_ANSIBLE = 'quay.io/redhat-cop/jenkins-worker-ansible:v1.11' + String JENKINS_WORKER_IMAGE_IMAGE_MANAGMENT = 'quay.io/redhat-cop/jenkins-slave-image-mgmt:v1.11' + + pipeline { + triggers { + cron(TRIGGER_CRON_PERIOD) + } + environment { + DEFAULT_CICD_NAMESPACE = "${applicationName}-ci-cd" + DEFAULT_DEV_NAMESPACE = "${applicationName}-dev" + DEFAULT_TEST_NAMESPACE = "${applicationName}-test" + DEFAULT_QA_NAMESPACE = "${applicationName}-qa" + DEFAULT_PROD_NAMESPACE = "${applicationName}-prod" + + DEV_IMAGE_TAG = 'dev' + TEST_IMAGE_TAG = 'test' + QA_IMAGE_TAG = 'qa' + PROD_IMAGE_TAG = 'prod' + + DEV_CLUSTER_CREDENTIAL_SECRET_NAME = 'cluster-credential-dev' + TEST_CLUSTER_CREDENTIAL_SECRET_NAME = 'cluster-credential-test' + QA_CLUSTER_CREDENTIAL_SECRET_NAME = 'cluster-credential-qa' + PROD_CLUSTER_CREDENTIAL_SECRET_NAME = 'cluster-credential-prod' + } + // The options directive is for configuration that applies to the whole job. + options { + buildDiscarder(logRotator(numToKeepStr: '50', artifactNumToKeepStr: '1')) + timeout(time: 15, unit: 'MINUTES') + ansiColor('xterm') + timestamps() + } + agent { + kubernetes { + label "jenkins-${applicationName}-${serviceName}-${env.BUILD_ID}" + cloud 'openshift' + yaml """ +apiVersion: v1 +kind: Pod +spec: + serviceAccount: jenkins + containers: + - name: 'jnlp' + image: "${JENKINS_WORKER_IMAGE_MAVEN}" + tty: true + volumeMounts: + - name: maven-settings + mountPath: /home/jenkins/.m2/settings.xml + subPath: settings.xml + readOnly: true + - name: m2 + mountPath: /home/jenkins/.m2 + - name: 'jenkins-worker-ansible' + image: "${JENKINS_WORKER_IMAGE_ANSIBLE}" + tty: true + command: ['sh', '-c', 'generate_container_user && cat'] + - name: 'jenkins-worker-image-mgmt' + image: "${JENKINS_WORKER_IMAGE_IMAGE_MANAGMENT}" + tty: true + command: ['sh', '-c', 'generate_container_user && cat'] + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/dockerconfigjson + name: dockerconfigjson + readOnly: true + volumes: + - name: dockerconfigjson + secret: + secretName: "${immagePushSecret}" + - name: maven-settings + configMap: + name: maven + - name: m2 + emptyDir: {} +""" + } + } + stages { + stage('SETUP') { + when { + anyOf { + branch 'master'; + branch 'release/*'; + branch 'develop'; + branch 'hotfix/*'; + branch 'feature/*'; + branch 'pipeline/*'; + } + } + steps { + // determine OpenShift/Kubernetes Project/Namespace names + script { + // for certain branches use dedicated namespaces so as not to interupt primary + // develop,release/*, and master branch work flows. + if ( env.BRANCH_NAME =~ /^(${DEDICATED_NAMESPACES_BRANCHES_REGEX})\/.*/) { + namespace_postfix = env.BRANCH_NAME.replaceAll("[^a-zA-Z1-9]","-") + env.CICD_NAMESPACE = "${env.DEFAULT_CICD_NAMESPACE}-${namespace_postfix}" + env.DEV_NAMESPACE = "${env.DEFAULT_DEV_NAMESPACE}-${namespace_postfix}" + env.TEST_NAMESPACE = "${env.DEFAULT_TEST_NAMESPACE}-${namespace_postfix}" + env.QA_NAMESPACE = "${env.DEFAULT_QA_NAMESPACE}-${namespace_postfix}" + env.PROD_NAMESPACE = "${env.DEFAULT_PROD_NAMESPACE}-${namespace_postfix}" + } + + // ensure required environment variables are set + if (!env.CICD_NAMESPACE) env.CICD_NAMESPACE = env.DEFAULT_CICD_NAMESPACE + if (!env.DEV_NAMESPACE) env.DEV_NAMESPACE = env.DEFAULT_DEV_NAMESPACE + if (!env.TEST_NAMESPACE) env.TEST_NAMESPACE = env.DEFAULT_TEST_NAMESPACE + if (!env.QA_NAMESPACE) env.QA_NAMESPACE = env.DEFAULT_QA_NAMESPACE + if (!env.PROD_NAMESPACE) env.PROD_NAMESPACE = env.DEFAULT_PROD_NAMESPACE + } + + // log some helpful information + script { + sh 'printenv' + + echo "applicationName: ${applicationName}" + echo "serviceName: ${serviceName}" + echo "ownerGroupName: ${ownerGroupName}" + echo "imagePushRegistry: ${imagePushRegistry}" + echo "imagePullRegistry: ${imagePullRegistry}" + echo "immagePushSecret: ${immagePushSecret}" + echo "immagePullSecret: ${immagePullSecret}" + echo "ansibleVaultJenkinsCredentialName: ${ansibleVaultJenkinsCredentialName}" + echo "builderImage: ${builderImage}" + echo "mavenMirrorUrl: ${mavenMirrorUrl}" + echo "mvnAdditionalArgs: ${mvnAdditionalArgs}" + + echo "BRANCH_NAME: ${env.BRANCH_NAME}" + + echo "CICD_NAMESPACE: ${env.CICD_NAMESPACE}" + echo "DEV_NAMESPACE: ${env.DEV_NAMESPACE}" + echo "TEST_NAMESPACE: ${env.TEST_NAMESPACE}" + echo "QA_NAMESPACE: ${env.QA_NAMESPACE}" + echo "PROD_NAMESPACE: ${env.PROD_NAMESPACE}" + + echo "DEV_IMAGE_TAG: ${env.DEV_IMAGE_TAG}" + echo "TEST_IMAGE_TAG: ${env.TEST_IMAGE_TAG}" + echo "QA_IMAGE_TAG: ${env.QA_IMAGE_TAG}" + echo "PROD_IMAGE_TAG: ${env.PROD_IMAGE_TAG}" + } + + script { + // get the version + def pom = readMavenPom file: 'pom.xml' + env.MVN_VERSION = pom.version + env.MVN_ARTIFACT_ID = pom.artifactId + env.APP_VERSION = pom.version + // if branch is master don't include branch name in version + // else include branch name in version + // + // NOTE: by strict semantic versioning rules the build information should + // be appended to version using a + but + is not valid in docker tags + if (env.BRANCH_NAME == 'master') { + env.BUILD_VERSION = "v${env.APP_VERSION}.${env.BUILD_ID}" + } else { + safeBranchName = env.BRANCH_NAME.replaceAll('/','_') + env.BUILD_VERSION = "v${env.APP_VERSION}-${safeBranchName}.${env.BUILD_ID}" + } + echo "App Version: ${env.APP_VERSION}" + echo "Build Version: ${env.BUILD_VERSION}" + } + } + } + stage('BUILD') { + when { + anyOf { + branch 'master'; + branch 'release/*'; + branch 'develop'; + branch 'hotfix/*'; + branch 'feature/*'; + branch 'pipeline/*'; + } + } + stages { + stage ("BUILD: Create OpenShift artifacts") { + steps { + container('jenkins-worker-ansible') { + withCredentials([file(credentialsId: "${ansibleVaultJenkinsCredentialName}", variable: 'ANSIBLE_VAULT_PASSWORD_FILE')]) { + // NOTE: openshift_templates_raw MUST be accessible to OpenShift without authentication + applier( + applierPlaybook: "apply.yml", + playbookAdditionalArgs: """ \ + --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} \ + -e app_env=app-build \ + -e app_name=${serviceName} \ + -e ci_cd_namespace=${env.CICD_NAMESPACE} \ + -e app_build_push_secret=${immagePushSecret} \ + -e app_build_pull_secret=${immagePullSecret} \ + -e app_build_destination_image_name=${serviceName} \ + -e app_owner_group_name=${ownerGroupName} \ + -e app_build_destination_repo_name=${imagePushRegistry} \ + -e app_build_destination_image_tag=latest \ + -e app_build_destination_repo_namespace=${applicationName} \ + -e app_build_maven_mirror_url=${mavenMirrorUrl} \ + -e app_build_builder_image_name=${builderImage} \ + -e app_build_maven_args_append=${mvnAdditionalArgs} + """, + inventoryPath: "inventory/hosts", + requirementsPath: "requirements.yml", + ansibleRootDir: ".openshift-applier" + ) + } + } + } + } + stage('BUILD: build') { + steps { + // build the applciation + sh "mvn clean package -U -B ${mvnAdditionalArgs}" + + // copy other binary artifacts expected by S2I build to target directory + sh 'cp -R .s2i target/' + sh 'cp -R configuration target/' + sh 'cp -R extensions target/' + } + } + stage('BUILD: SonarQube') { + steps { + echo '### Running sonar scanner ###' + script { + withSonarQubeEnv(sonarQubeEnv) { + sh 'mvn sonar:sonar -U -B -Dsurefire.useFile=false' + } + } + } + } + stage('BUILD: Build Image') { + steps { + buildAndTag( + imageName : serviceName, + imageNamespace : applicationName, + imageVersion : env.BUILD_VERSION, + registryFQDN : imagePushRegistry, + buildProjectName : env.CICD_NAMESPACE, + fromFilePath : "target/" + ) + } + } + } + post { + success { + //TODO: send success notification to ChatOps / Email / Other notifciation system + } + failure { + //TODO: send failure notification to ChatOps / Email / Other notifciation system + } + } + } + stage('DEV') { + when { + anyOf { + branch 'master'; + branch 'release/*'; + branch 'develop'; + branch 'hotfix/*'; + branch 'feature/*'; + branch 'pipeline/*'; + } + } + stages { + stage('DEV: Approval') { + steps { + echo 'TODO: verify build results' + echo 'Automatic approval given build results.' + } + } + stage('DEV: Promote') { + options { + lock("env-dev-${applicationName}-${serviceName}") + } + stages { + stage ("DEV: Create OpenShift artifacts") { + steps { + container('jenkins-worker-ansible') { + withCredentials([file(credentialsId: "${ansibleVaultJenkinsCredentialName}", variable: 'ANSIBLE_VAULT_PASSWORD_FILE')]) { + // NOTE: openshift_templates_raw MUST be accessible to OpenShift without authentication + applier( + applierPlaybook: "apply.yml", + playbookAdditionalArgs: """ \ + --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} \ + -e app_env=app-dev \ + -e namespace=${env.DEV_NAMESPACE} \ + -e ci_cd_namespace=${env.CICD_NAMESPACE} \ + -e app_owner_group_name=${ownerGroupName} \ + -e app_name=${serviceName} \ + -e app_image_tag=${env.DEV_IMAGE_TAG} \ + -e app_image_namespace=${applicationName} \ + -e repo_name=${imagePullRegistry} \ + -e app_custom_env='{}' \ + -e image_pull_secret=${immagePullSecret} + """, + inventoryPath: "inventory/hosts", + requirementsPath: "requirements.yml", + ansibleRootDir: ".openshift-applier" + ) + } + } + } + } + stage('DEV: Deploy') { + steps { + container('jenkins-worker-image-mgmt') { + script { + def (devAPI, devToken) = clusterCredentials( + projectName: env.CICD_NAMESPACE, + secretName : env.DEV_CLUSTER_CREDENTIAL_SECRET_NAME + ) + env.DEV_API = devAPI + env.DEV_TOKEN = devToken + } + } + tagAndDeploy( + imageName : serviceName, + imageNamespace : applicationName, + imageVersion : env.BUILD_VERSION, + registryFQDN : imagePushRegistry, + clusterAPI : env.DEV_API, + clusterToken : env.DEV_TOKEN, + deployDestinationProjectName : env.DEV_NAMESPACE, + deployDestinationVersionTag : DEV_IMAGE_TAG + ) + } + } + } + } + } + post { + success { + //TODO: send success notification to ChatOps / Email / Other notifciation system + } + failure { + //TODO: send failure notification to ChatOps / Email / Other notifciation system + } + } + } + stage('TEST') { + when { + anyOf { + branch 'master'; + branch 'release/*'; + branch 'hotfix/*'; + branch 'pipeline/*'; + } + } + stages { + stage('TEST: Approval') { + steps { + //TODO: send wiating for approval notification to ChatOps / Email / Other notification system + input 'Promote to TEST environment?' + } + } + stage('TEST: Promote') { + options { + lock("env-test-${applicationName}-${serviceName}") + } + stages { + stage ("TEST: Create OpenShift artifacts") { + steps { + container('jenkins-worker-ansible') { + withCredentials([file(credentialsId: "${ansibleVaultJenkinsCredentialName}", variable: 'ANSIBLE_VAULT_PASSWORD_FILE')]) { + // NOTE: openshift_templates_raw MUST be accessible to OpenShift without authentication + applier( + applierPlaybook: "apply.yml", + playbookAdditionalArgs: """ \ + --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} \ + -e app_env=app-test \ + -e namespace=${env.TEST_NAMESPACE} \ + -e ci_cd_namespace=${env.CICD_NAMESPACE} \ + -e app_owner_group_name=${ownerGroupName} \ + -e app_name=${serviceName} \ + -e app_image_tag=${env.TEST_IMAGE_TAG} \ + -e app_image_namespace=${applicationName} \ + -e repo_name=${imagePullRegistry} \ + -e app_custom_env='{}' \ + -e image_pull_secret=${immagePullSecret} + """, + inventoryPath: "inventory/hosts", + requirementsPath: "requirements.yml", + ansibleRootDir: ".openshift-applier" + ) + } + } + } + } + stage('TEST: Deploy') { + steps { + container('jenkins-worker-image-mgmt') { + script { + def (testAPI, testToken) = clusterCredentials( + projectName: env.CICD_NAMESPACE, + secretName : env.TEST_CLUSTER_CREDENTIAL_SECRET_NAME + ) + env.TEST_API = testAPI + env.TEST_TOKEN = testToken + } + } + tagAndDeploy( + imageName : serviceName, + imageNamespace : applicationName, + imageVersion : env.BUILD_VERSION, + registryFQDN : imagePushRegistry, + clusterAPI : env.TEST_API, + clusterToken : env.TEST_TOKEN, + deployDestinationProjectName : env.TEST_NAMESPACE, + deployDestinationVersionTag : env.TEST_IMAGE_TAG + ) + } + } + stage('TEST: Run Tests') { + steps { + echo "TODO" + } + } + } + } + } + post { + success { + //TODO: send success notification to ChatOps / Email / Other notifciation system + } + failure { + //TODO: send failure notification to ChatOps / Email / Other notifciation system + } + } + } + stage('QA') { + when { + anyOf { + branch 'master'; + branch 'release/*'; + branch 'hotfix/*'; + branch 'pipeline/*'; + } + } + stages { + stage('QA: Approval') { + steps { + //TODO: send wiating for approval notification to ChatOps / Email / Other notification system + input 'Promote to QA environment?' + } + } + stage('QA: Promote') { + options { + lock("env-qa-${applicationName}-${serviceName}") + } + stages { + stage ("QA: Create OpenShift artifacts") { + steps { + container('jenkins-worker-ansible') { + withCredentials([file(credentialsId: "${ansibleVaultJenkinsCredentialName}", variable: 'ANSIBLE_VAULT_PASSWORD_FILE')]) { + // NOTE: openshift_templates_raw MUST be accessible to OpenShift without authentication + applier( + applierPlaybook: "apply.yml", + playbookAdditionalArgs: """ \ + --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} \ + -e app_env=app-qa \ + -e namespace=${env.QA_NAMESPACE} \ + -e ci_cd_namespace=${env.CICD_NAMESPACE} \ + -e app_owner_group_name=${ownerGroupName} \ + -e app_name=${serviceName} \ + -e app_image_tag=${env.QA_IMAGE_TAG} \ + -e app_image_namespace=${applicationName} \ + -e repo_name=${imagePullRegistry} \ + -e app_custom_env='{}' \ + -e image_pull_secret=${immagePullSecret} + """, + inventoryPath: "inventory/hosts", + requirementsPath: "requirements.yml", + ansibleRootDir: ".openshift-applier" + ) + } + } + } + } + stage('QA: Deploy') { + steps { + container('jenkins-worker-image-mgmt') { + script { + def (qaAPI, qaToken) = clusterCredentials( + projectName: env.CICD_NAMESPACE, + secretName : env.QA_CLUSTER_CREDENTIAL_SECRET_NAME + ) + env.QA_API = qaAPI + env.QA_TOKEN = qaToken + } + } + tagAndDeploy( + imageName : serviceName, + imageNamespace : applicationName, + imageVersion : env.BUILD_VERSION, + registryFQDN : imagePushRegistry, + clusterAPI : env.QA_API, + clusterToken : env.QA_TOKEN, + deployDestinationProjectName : env.QA_NAMESPACE, + deployDestinationVersionTag : env.QA_IMAGE_TAG + ) + } + } + stage('QA: Run Tests') { + steps { + echo "TODO" + } + } + } + } + } + post { + success { + //TODO: send success notification to ChatOps / Email / Other notifciation system + } + failure { + //TODO: send failure notification to ChatOps / Email / Other notifciation system + } + } + } + stage('PROD') { + when { + anyOf { + branch 'master'; + branch 'pipeline/*'; + } + } + stages { + stage('PROD: Approval') { + steps { + //TODO: send wiating for approval notification to ChatOps / Email / Other notification system + input 'Promote to PROD environment?' + } + } + stage('PROD: Promote') { + options { + lock("env-prod-${applicationName}-${serviceName}") + } + stages { + stage ("PROD: Create OpenShift artifacts") { + steps { + container('jenkins-worker-ansible') { + withCredentials([file(credentialsId: "${ansibleVaultJenkinsCredentialName}", variable: 'ANSIBLE_VAULT_PASSWORD_FILE')]) { + // NOTE: openshift_templates_raw MUST be accessible to OpenShift without authentication + applier( + applierPlaybook: "apply.yml", + playbookAdditionalArgs: """ \ + --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} \ + -e app_env=app-prod \ + -e namespace=${env.PROD_NAMESPACE} \ + -e ci_cd_namespace=${env.CICD_NAMESPACE} \ + -e app_owner_group_name=${ownerGroupName} \ + -e app_name=${serviceName} \ + -e app_image_tag=${env.PROD_IMAGE_TAG} \ + -e app_image_namespace=${applicationName} \ + -e repo_name=${imagePullRegistry} \ + -e app_custom_env='{}' \ + -e image_pull_secret=${immagePullSecret} + """, + inventoryPath: "inventory/hosts", + requirementsPath: "requirements.yml", + ansibleRootDir: ".openshift-applier" + ) + } + } + } + } + stage('PROD: Deploy') { + steps { + container('jenkins-worker-image-mgmt') { + script { + def (prodAPI, prodToken) = clusterCredentials( + projectName: env.CICD_NAMESPACE, + secretName : env.PROD_CLUSTER_CREDENTIAL_SECRET_NAME + ) + env.PROD_API = prodAPI + env.PROD_TOKEN = prodToken + } + } + tagAndDeploy( + imageName : serviceName, + imageNamespace : applicationName, + imageVersion : env.BUILD_VERSION, + registryFQDN : imagePushRegistry, + clusterAPI : env.PROD_API, + clusterToken : env.PROD_TOKEN, + deployDestinationProjectName : env.PROD_NAMESPACE, + deployDestinationVersionTag : env.PROD_IMAGE_TAG + ) + } + } + } + } + } + post { + success { + //TODO: send success notification to ChatOps / Email / Other notifciation system + } + failure { + //TODO: send failure notification to ChatOps / Email / Other notifciation system + } + } + } + } + post { + post { + success { + //TODO: send success notification to ChatOps / Email / Other notifciation system + } + failure { + //TODO: send failure notification to ChatOps / Email / Other notifciation system + } + } + failure { + mattermostSend "${serviceName}: Pipeline FAILED: ${env.BUILD_VERSION}" + } + always { + archiveArtifacts "**" + } + } + } +}