-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ci(openshift): Add openshift files from isomorphic-starter-kit.
- Loading branch information
1 parent
934309f
commit 82957b0
Showing
5 changed files
with
827 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,289 @@ | ||
/** | ||
* Defines our build pipeline | ||
* See: JENKINS.md | ||
*/ | ||
|
||
String buildVersion = env.BUILD_NUMBER | ||
|
||
try { | ||
String gitCommitId | ||
|
||
stage('Checkout') { | ||
node { | ||
sh "oc project ${env.PROJECT_NAME}" | ||
|
||
checkout scm | ||
stash includes: 'openshift/*.sh', name: 'scripts' | ||
|
||
String gitCommitNum = sh(returnStdout: true, script: "git rev-list HEAD --count").trim() | ||
String gitShortId = sh(returnStdout: true, script: "git rev-parse --short HEAD").trim() | ||
buildVersion = gitCommitNum + '-' + gitShortId | ||
|
||
gitCommitId = sh(returnStdout: true, script: "git rev-parse HEAD").trim() | ||
} | ||
} | ||
|
||
stage('Apply Templates') { | ||
node { | ||
String currentBranch = sh( | ||
returnStdout: true, | ||
script: "oc get bc telus-isomorphic-starter-kit-pipeline -o='jsonpath={.spec.source.git.ref}'" | ||
).trim() | ||
|
||
sh(""" | ||
oc apply -f openshift/openshift-template.yml | ||
oc process telus-isomorphic-starter-kit-pipeline BRANCH=${currentBranch} | oc apply -f - | ||
""") | ||
} | ||
} | ||
|
||
stage('Build') { | ||
parallel 'Build UI':{ | ||
build( | ||
name: 'telus-isomorphic-starter-kit-ui', | ||
buildVersion: buildVersion, | ||
gitCommitId: gitCommitId | ||
) | ||
}, 'Build BFF':{ | ||
build( | ||
name: 'telus-isomorphic-starter-kit-bff', | ||
buildVersion: buildVersion, | ||
gitCommitId: gitCommitId | ||
) | ||
}, 'Build E2E':{ | ||
build( | ||
name: 'telus-isomorphic-starter-kit-e2e', | ||
buildVersion: buildVersion, | ||
gitCommitId: gitCommitId | ||
) | ||
}, 'Build Load Test':{ | ||
build( | ||
name: 'telus-isomorphic-starter-kit-load', | ||
buildVersion: buildVersion, | ||
gitCommitId: gitCommitId | ||
) | ||
} | ||
} | ||
|
||
stage('Test') { | ||
parallel 'Test UI':{ | ||
test( | ||
name: 'telus-isomorphic-starter-kit-ui', | ||
buildVersion: buildVersion | ||
) | ||
}, 'Test BFF':{ | ||
test( | ||
name: 'telus-isomorphic-starter-kit-bff', | ||
buildVersion: buildVersion | ||
) | ||
} | ||
} | ||
|
||
stage('Deploy Staging') { | ||
deploy( | ||
buildVersion: buildVersion, | ||
environment: 'staging', | ||
uiNewRelicId: '24442659', | ||
bffNewRelicId: '24534378', | ||
numReplicas: 1 | ||
) | ||
} | ||
|
||
stage('E2E Staging') { | ||
e2e( | ||
buildVersion: buildVersion, | ||
environment: 'staging' | ||
) | ||
} | ||
|
||
stage('Load Test Staging') { | ||
loadTest( | ||
buildVersion: buildVersion, | ||
environment: 'staging' | ||
) | ||
} | ||
|
||
stage('User Input') { | ||
inputUrl = env.BUILD_URL ? "(${env.BUILD_URL}input)" : ''; | ||
notifyBuild( | ||
message: "Build is ready for Production ${inputUrl}", | ||
color: '#0000FF', | ||
buildVersion: buildVersion | ||
) | ||
timeout(time:1, unit:'DAYS') { | ||
input 'Deploy to Production?' | ||
} | ||
} | ||
|
||
stage('Deploy Production') { | ||
deploy( | ||
buildVersion: buildVersion, | ||
environment: 'production', | ||
uiNewRelicId: '24442164', | ||
bffNewRelicId: '24534533', | ||
numReplicas: 3 | ||
) | ||
} | ||
|
||
stage('E2E Production') { | ||
e2e( | ||
buildVersion: buildVersion, | ||
environment: 'production' | ||
) | ||
} | ||
|
||
currentBuild.result = 'SUCCESS' | ||
} | ||
catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException flowError) { | ||
currentBuild.result = 'ABORTED' | ||
} | ||
catch (err) { | ||
currentBuild.result = 'FAILURE' | ||
consoleUrl = env.BUILD_URL ? "(${env.BUILD_URL}console)" : ''; | ||
notifyBuild( | ||
message: "Build failed ${consoleUrl}", | ||
color: '#FF0000', | ||
buildVersion: buildVersion | ||
) | ||
throw err | ||
} | ||
finally { | ||
if (currentBuild.result == 'SUCCESS') { | ||
notifyBuild( | ||
message: "Production deploy successful", | ||
color: '#00FF00', | ||
buildVersion: buildVersion | ||
) | ||
} | ||
} | ||
|
||
def build(Map attrs) { | ||
node { | ||
boolean imageTagExists = sh( | ||
returnStatus: true, | ||
script: "oc get istag ${attrs.name}:${attrs.buildVersion}" | ||
) == 0 | ||
|
||
if (!imageTagExists) { | ||
openshiftBuild( | ||
buildConfig: attrs.name, | ||
commitID: attrs.gitCommitId, | ||
waitTime: '3600000' | ||
) | ||
|
||
openshiftTag( | ||
sourceStream: attrs.name, | ||
destinationStream: attrs.name, | ||
sourceTag: 'latest', | ||
destinationTag: attrs.buildVersion, | ||
namespace: env.PROJECT_NAME | ||
) | ||
} | ||
} | ||
} | ||
|
||
def test(Map attrs) { | ||
node { | ||
unstash 'scripts' | ||
sh("./openshift/run-test.sh ${attrs.name} ${attrs.buildVersion}") | ||
} | ||
} | ||
|
||
def deploy(Map attrs) { | ||
node { | ||
String uiDockerRegistry = sh( | ||
returnStdout: true, | ||
script: "oc get imagestream telus-isomorphic-starter-kit-ui -o='jsonpath={.status.dockerImageRepository}'" | ||
).trim() | ||
|
||
String bffDockerRegistry = sh( | ||
returnStdout: true, | ||
script: "oc get imagestream telus-isomorphic-starter-kit-bff -o='jsonpath={.status.dockerImageRepository}'" | ||
).trim() | ||
|
||
sh(""" | ||
# workaround for https://github.com/kubernetes/kubernetes/issues/34413 | ||
if oc get hpa/telus-isomorphic-starter-kit-${attrs.environment} > /dev/null 2>&1 | ||
then | ||
oc delete hpa/telus-isomorphic-starter-kit-${attrs.environment} | ||
fi | ||
oc new-app \ | ||
--template='telus-isomorphic-starter-kit' \ | ||
-p VERSION='${attrs.buildVersion}' \ | ||
-p ENVIRONMENT='${attrs.environment}' \ | ||
-p UI_DOCKER_REGISTRY='${uiDockerRegistry}:${attrs.buildVersion}' \ | ||
-p BFF_DOCKER_REGISTRY='${bffDockerRegistry}:${attrs.buildVersion}' \ | ||
-p NUM_REPLICAS='${attrs.numReplicas}' \ | ||
-o yaml | oc apply -f - | ||
oc autoscale dc/telus-isomorphic-starter-kit-${attrs.environment} --min ${attrs.numReplicas} --max 5 --cpu-percent=80 | ||
""") | ||
|
||
openshiftVerifyDeployment( | ||
deploymentConfig: "telus-isomorphic-starter-kit-${attrs.environment}", | ||
waitTime: '1800000' | ||
) | ||
|
||
notifyNewrelic(attrs.uiNewRelicId, attrs.buildVersion) | ||
notifyNewrelic(attrs.bffNewRelicId, attrs.buildVersion) | ||
} | ||
} | ||
|
||
def notifyNewrelic(String newRelicId, String buildVersion) { | ||
sh("""#!/bin/sh | ||
curl -i -X POST 'https://api.newrelic.com/v2/applications/${newRelicId}/deployments.json' \ | ||
-H 'X-Api-Key: ${env.NEW_RELIC_API_KEY}' \ | ||
-H 'Content-Type: application/json' \ | ||
-d '{ \"deployment\": { \"revision\": \"${buildVersion}\" } }' | ||
""") | ||
} | ||
|
||
def e2e(Map attrs) { | ||
node { | ||
String baseUrl = sh( | ||
returnStdout: true, | ||
script: "oc get route telus-isomorphic-starter-kit-${attrs.environment}-ui -o='jsonpath={.spec.host}'" | ||
).trim() | ||
|
||
String e2eDockerRegistry = sh( | ||
returnStdout: true, | ||
script: "oc get imagestream telus-isomorphic-starter-kit-e2e -o='jsonpath={.status.dockerImageRepository}'" | ||
).trim() | ||
|
||
sh(""" | ||
oc run telus-isomorphic-starter-kit-${attrs.environment}-e2e-${attrs.buildVersion} \ | ||
--image='${e2eDockerRegistry}:${attrs.buildVersion}' \ | ||
--rm=true --attach=true --restart=Never \ | ||
--env BASE_URL='https://${baseUrl}' | ||
""") | ||
} | ||
} | ||
|
||
def loadTest(Map attrs) { | ||
node { | ||
String loadDockerRegistry = sh( | ||
returnStdout: true, | ||
script: "oc get imagestream telus-isomorphic-starter-kit-load -o='jsonpath={.status.dockerImageRepository}'" | ||
).trim() | ||
|
||
sh(""" | ||
oc run telus-isomorphic-starter-kit-${attrs.environment}-load-${attrs.buildVersion} \ | ||
--image='${loadDockerRegistry}:${attrs.buildVersion}' \ | ||
--rm=true --attach=true --restart=Never \ | ||
--env BASE_URL='http://telus-isomorphic-starter-kit-${attrs.environment}:3000/en/bc/hello-world' | ||
""") | ||
} | ||
} | ||
|
||
def notifyBuild(Map attrs) { | ||
node { | ||
slackSend( | ||
color: attrs.color, | ||
message: "${env.JOB_NAME} [${attrs.buildVersion}]\n${attrs.message}", | ||
teamDomain: 'telusdigital', | ||
channel: 'starter-kit-builds', | ||
token: env.SLACK_TOKEN | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# OpenShift | ||
|
||
We use OpenShift to orchestrate the deployment of our Docker containers. It uses shared swarm of "minion" hosts as generic infrastructure for our autoscaling applications. This makes it quick to deploy and scale these containers ephemerally. Every time a new application is deployed, its running as if it is an entirely new application "server", built from scratch. | ||
|
||
The script and configuration for the initial deployment to OpenShift is [install.sh](https://github.com/telusdigital/telus-isomorphic-starter-kit/blob/master/openshift/install.sh). It sets up the jenkins server, templates, builds and secrets necessary for exposing our application to consumers. | ||
|
||
After installation, we can use our newly created Jenkins pipeline to build and deploy the docker image to staging and production environments. We can also use our templates to create ephemeral/disposable testing environments, pinned to a specific version or custom builds of a docker container. | ||
|
||
#### Get access | ||
|
||
To use OpenShift, you will need a TELUS Google account. If you have one, [edit this file](https://github.com/telusdigital/openshift-cluster-provisioning/blob/master/dp/data.yaml#L58), add yourself to your respective outcome team, and submit a pull request (for faster results, notify us in #g-delivery on slack). | ||
|
||
## Quickstart | ||
|
||
### Dependencies | ||
|
||
This application requires `openshift-cli` & `vault`. Either install them with `brew` on Mac or `apt-get` on Linux. Also install [ship.py](https://github.com/telusdigital/ship.py). | ||
|
||
### Login | ||
|
||
#### Vault | ||
|
||
In order to kick off the initial installation of a project, or to add or edit any application secrets, you'll need to log in with HashiCorp Vault. | ||
|
||
Use our [ship.py](https://github.com/telusdigital/ship.py) utility command line tool to log into vault. | ||
|
||
It will ask for your github credentials, create an access token, and use it to authenticate you. You should now be able to read secrets: | ||
> `vault read --format json secret/common/starter-kit` | ||
#### Openshift | ||
|
||
For the main cluster (for production apps), log in with: | ||
> `oc login --server=https://api.telusdigital.openshift.com` | ||
For the sandbox cluster (for development apps), log in with: | ||
> `oc login --server=https://api.telusdigitalsandbox.openshift.com` | ||
Visit the URL it tells you to, copy the first `oc login` line, with the token in it, and paste it into your terminal. | ||
|
||
### Select project | ||
|
||
If you are on the sandbox environment, you can create a new personal project space: | ||
> `oc new-project my-project` | ||
To select an existing project (on either main or sandbox): | ||
> `oc project my-project` | ||
Each outcome team also gets an `o-outcome-team` namespace. Only users who are administrators of their outcome teams can make modifications to these spaces. Otherwise, users will only get view access. | ||
|
||
### Install Jenkins | ||
|
||
First your project will need Jenkins. We can clone the [default Jenkins starter kit](https://github.com/telusdigital/openshift-jenkins-starter-kit): | ||
> `git clone git@github.com:telusdigital/openshift-jenkins-starter-kit.git` | ||
To install Jenkins in your cluster, run | ||
> `openshift-jenkins-starter-kit/openshift/install.sh` | ||
### Install pipeline | ||
|
||
Now you are ready to check out the project, e.g.: | ||
> `git clone git@github.com:telusdigital/telus-isomorphic-starter-kit.git` | ||
To create your Jenkins build pipeline in your project run: | ||
> `telus-isomorphic-starter-kit/openshift/install.sh` | ||
Now Jenkins will build your Docker image, and deploy it to OpenShift. When the pipeline is done, you’ll have a staging environment populated. You can visit the OpenShift console ([Sandbox](https://console.telusdigitalsandbox.openshift.com/console/), [Main](https://console.telusdigital.openshift.com/console/)), and, after clicking on your project, you should see your staging environment. Look under `Builds > Pipelines` to see the status of your pipeline. You'll notice that it is waiting at a `User Input` step, asking if you would like to deploy to production. You can find the login credentials for Jenkins by going to `Applications > Deployments > Jenkins > Environment`, and copying the password (the username is `admin`). After logging into Jenkins, click the button to proceed through to production now, and see the other container come online. | ||
|
||
On the sandbox server, you can also override the configured branch, if you want to test specific features or build pipeline changes in isolation. | ||
|
||
> `telus-isomorphic-starter-kit/openshift/install.sh my-branch` | ||
### Setup webhook | ||
|
||
If you create your own project from the starter kit, you can add a GitHub web hook to compile on commit. From the OpenShift pipeline view, if you click `Edit Pipeline` you should be able to copy your GitHub web hook URL. Under GitHub settings for your project you can click `Webhooks`. | ||
|
||
Add an `application/json` webhook and paste in your web hook URL e.g.: | ||
> `https://api.telusdigitalsandbox.openshift.com/oapi/v1/namespaces/tom-test-project/buildconfigs/telus-isomorphic-starter-kit-pipeline/webhooks/tisk/github` | ||
## Configuration | ||
|
||
* [Reference Architecture OpenShift standards](https://github.com/telusdigital/reference-architecture/blob/master/delivery/openshift.md) | ||
* [Reference Architecture Kubernetes standards](https://github.com/telusdigital/reference-architecture/blob/master/delivery/kubernetes.md) | ||
|
||
* [OpenShift documentation](https://docs.openshift.com/container-platform/3.4/dev_guide/index.html) | ||
* [Kubernetes documentation](https://kubernetes.io/docs/home/) | ||
|
||
Our [openshift-template.yml](https://github.com/telusdigital/telus-isomorphic-starter-kit/blob/master/openshift/openshift-template.yml) sets up the jenkins server, templates, builds and secrets necessary for exposing our application to consumers. In order to run this, you must first log in to OpenShift with `oc login` and create or select an existing OpenShift project namespace with `oc project`. | ||
|
||
## Local testing | ||
|
||
To test OpenShift locally, we recommend using [minishift](https://docs.openshift.org/latest/minishift/getting-started/quickstart.html). | ||
|
||
On Mac we can install minishift with brew: | ||
``` | ||
$ brew update | ||
$ brew install docker-machine-driver-xhyve | ||
$ sudo chown root:wheel $(brew --prefix)/opt/docker-machine-driver-xhyve/bin/docker-machine-driver-xhyve | ||
$ sudo chmod u+s $(brew --prefix)/opt/docker-machine-driver-xhyve/bin/docker-machine-driver-xhyve | ||
$ brew cask install minishift | ||
$ minishift config set memory 8192 | ||
$ minishift start | ||
``` | ||
|
||
On other platforms: [see instructions](https://docs.openshift.org/latest/minishift/getting-started/installing.html#installing-instructions) | ||
|
||
Once minishift is running, you can log in, create projects, and install your Jenkins and applications normally. |
Oops, something went wrong.