Ravi Srinivasan
2019-03-25 96d171175638b13537310ace4fc2a9e6145654d8
commit | author | age
2b8436 1 # Attack of the Pipelines
c951f7 2
b5d705 3 > In this exercise we will explore the sample TODO List application and create a pipeline in Jenkins to build and deploy our code.
2b8436 4
fd3541 5 <!-- ![jenkins-time](../images/exercise2/jenkins-time.jpg) -->
2b8436 6
14cd2d 7 ## Exercise Intro
fd3541 8 This lesson is focused on creating a pipeline for our application. What is a pipeline? A pipeline is a series of steps or stages that takes our code from source to a deployed application. There can be many stages to a pipeline but a simple flow is to run a `build > bake > deploy`. Usually the first stage is triggered by something like a git commit.
RS 9
10 There could be many steps in each of these stages; such as compiling code, running tests and linting. All of these are done to try and drive up code quality and give more assurance that what is deployed is behaving as expected. In the exercise we will create a Jenkins pipeline by configuring it through the Jenkins web UI, which will create an un-gated pathway to production.
14cd2d 11
1173e5 12 First we will explore the sample application and get it running locally. The sample app is a `todolist` app - the `Hello World` app of the modern day.
14cd2d 13
D 14 #### Why create pipelines
15 * Assurance - drive up code quality and remove the need for dedicated deployment / release management teams
16 * Freedom - allow developers to take ownership of how and when code gets built and shipped
3f6da0 17 * Reliability - pipelines are a bit boring; they execute the same way each and every time they're run!
14cd2d 18 * A pathway to production:
D 19     - Puts the product in the hands of the customer quicker
20     - Enables seamless and repeatable deploys
21     - More prod like infrastructure increases assurance
22     - “We have already done it” behavior de-risks go live
23
43f2f2 24 _____
c951f7 25
D 26 ## Learning Outcomes
fd3541 27 As a learner by the end of this lesson you will be able to:
2b8436 28
D 29 - Build and run the full stack of the TODO List application locally
30 - Create an un-gated pipeline using the Jenkins UI for the backend and frontend
fd3541 31 - Add branching to the pipeline to target specific namespaces
c951f7 32
D 33 ## Tools and Frameworks
fd3541 34 > The following tools are used throughout this exercise. Familiarity with them is not required but knowing what they are may help. You will not need to install Vue or MongoDB. They are taken care of by our `todolist` app.
c951f7 35
D 36 1. [Jenkins](https://jenkins.io/) - OpenSource build automation server; highly customisable through plugins
fd3541 37 2. [NodeJS](https://nodejs.org/en/) - Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.
RS 38 3. [MongoDB](https://www.mongodb.com/what-is-mongodb) - MongoDB stores data in flexible, JSON-like documents, meaning fields can vary from document to document and data structure can be changed over time
39 4. [VueJS](https://vuejs.org/) - Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. It is designed from the ground up to be incrementally adoptable, and can easily scale between a library and a framework depending on different use cases. It consists of an approachable core library that focuses on the view layer only, and an ecosystem of supporting libraries that helps you tackle complexity in large Single-Page Applications.
c951f7 40
43f2f2 41 ## Big Picture
664135 42 > From the previous exercise; we created some supporting tooling needed by our app. Now we will introduce our Sample App and create a pipeline for it
D 43
44 ![big-picture](../images/big-picture/big-picture-2.jpg)
43f2f2 45
D 46 _____
c951f7 47
fd3541 48 <!-- ## 10,000 Ft View
2b8436 49 > _This lab requires users to take the sample TODO app and create a build pipeline in Jenkins by clicking your way to success ending up with an app deployed to each of the namespaces created previously_
43f2f2 50
e43fd2 51 2. Import the projects into your gitlab instance. See the README of each for build instructions
43f2f2 52
2b8436 53 2. Deploy a `MongoDB` using the provided template to all project namespace.
D 54
1173e5 55 2. Create 2 pipelines with three stages (`build`, `bake`, `deploy`) in Jenkins for `develop` & `master` branches on the `todolist-fe` such that:
e919d0 56     * a `Build` job is responsible for compiling and packaging our code:
2b8436 57         1. Checkout from source code (`develop` for `<yourname>-dev` & `master` for `<yourname>-test`)
D 58         2. Install node dependencies and run a build / package
e919d0 59         3. Send the package to Nexus
D 60         4. Archive the workspace to persist the workspace in case of failure
61         4. Tag the GitLab repository with the `${JOB_NAME}.${BUILD_NUMBER}` from Jenkins. This is our `${BUILD_TAG}` which will be used on downstream jobs.
62         5. Trigger the `bake` job with the `${BUILD_TAG}` param
63     * a `Bake` job should take the package and put it in a Linux Container
2b8436 64         1. Take an input of the previous jobs `${BUILD_TAG}` ie `${JOB_NAME}.${BUILD_NUMBER}`.
8894bf 65         2. Checkout the binary from Nexus and unzip its contents
W 66         3. Run an oc start-build of the App's BuildConfig and tag its imagestream with the provided `${BUILD_TAG}`
2b8436 67         4. Trigger a deploy job using the parameter `${BUILD_TAG}`
e919d0 68     * a `deploy` job should roll out the changes by updating the image tag in the DC:
2b8436 69         1. Take an input of the `${BUILD_TAG}`
D 70         2. Patch / set the DeploymentConfig to the image's `${BUILD_TAG}`
71         3. Rollout the changes
72         4. Verify the deployment
73
74 2. Repeat the above setup for the backend `todolist-fe`. TIP - use the copy config to speed things up!
75
fd3541 76 2. Verify that both apps and the DB are talking to one another as expected. -->
c951f7 77
D 78 ## Step by Step Instructions
fd3541 79 <!-- > This is a fairly structured guide with references to exact filenames and sections of text to be added. -->
c951f7 80
f016b7 81 ### Part 1 - Explore the Todo List App
7383de 82 > _In this part of the exercise we will explore the sample application, become familiar with it locally before building and deploying in OCP Land_
43f2f2 83
fd3541 84 #### Todo List Front End (todolist-fe)
4c8010 85
fd3541 86 1. Git clone the `todolist-fe` project to the `do500-workspace` folder and checkout the `develop` branch using the following commands.
RS 87
88 <p class="tip" >
89 NOTE: Microsoft Windows users, run the following commands in a `Git Bash` window. Recall that the full path to the `do500-workspace` folder is `/c/do500-workspace` in `Git Bash`.
90 </p>
91
7383de 92 ```bash
fd3541 93 cd ~/do500-workspace
fad576 94 ```
D 95 ```bash
bec65b 96 git clone https://github.com/RedHatTraining/rht-labs-todolist-fe todolist-fe
fad576 97 ```
D 98 ```bash
b6978f 99 cd todolist-fe
4c8010 100 ```
b6978f 101 ```bash
D 102 ./git-pull-all.sh
fad576 103 ```
D 104 ```bash
105 git checkout develop
c951f7 106 ```
7383de 107
8894bf 108 2. Open up Gitlab and login. Create a new project (internal) in GitLab called `todolist-fe` to host your clone of the project and copy its remote address. ![new-gitlab-proj](../images/exercise2/new-gitlab-proj.png)
7383de 109
e209d3 110 3. In your local clone of the `todolist-fe`, remove the origin and add the GitLab origin by replacing `<YOUR_GIT_LAB_PROJECT>`. Push your app to GitLab. (As before, we will bypass SSL key verification in this repo since we are using self-signed certificates on the GitLab sever.)
7383de 111 ```bash
e209d3 112 git config http.sslVerify false
b6978f 113 git remote set-url origin <YOUR_GIT_LAB_PROJECT>
10416f 114 # verify the origin has been updated
b6978f 115 git remote -v
D 116 git push -u origin --all
c951f7 117 ```
7383de 118
fd3541 119 <!-- 2. To get the app running locally; first check you've got node and npm installed
7383de 120 ```bash
b6978f 121 node -v
D 122 npm -v
7383de 123 ```
1173e5 124 <p class="tip" >
7383de 125 NOTE - If you are missing these dependencies; install them with ease using the [Node Version Manager](https://github.com/creationix/nvm)
D 126 </p>
fd3541 127 ![node-version](../images/exercise2/node-version.png) -->
7383de 128
fd3541 129 4. The `todolist-fe` has a package.json at the root of the project, this defines some configuration for the app including its dependencies, dev dependencies, scripts and other configuration. Install the app's dependencies
7383de 130 ```bash
b6978f 131 npm install
7383de 132 ```
D 133
fd3541 134 5. The `todolist-fe` has some scripts defined in the package.json at the root of the project. A snippet of the npm scripts are shown below. To run any of these scripts run `npm run <SCRIPT_NAME>`. Let's start by serving our application
RS 135
7383de 136  ![npm-scripts](../images/exercise2/npm-scripts.png)
D 137 ```bash
138 npm run serve
139 ```
fd3541 140 <p class="tip" >
RS 141 NOTE: Microsoft Windows users, if you are running this command for the first time, you may be prompted by the Windows Defender Firewall to allow access. Click `Allow Access` to continue.
142 </p>
7383de 143
fd3541 144 6. This will take sometime to execute; but once done it should open the browser for you displaying the homepage of the `todolist-fe` app.
7383de 145  ![todo-list-app](../images/exercise2/todo-list-app.png)
10416f 146     * Click 'Todo' at the top of the home page to get to the above page.
abb0a5 147     * The server hosting it live reloads; so if you make changes to your code base the app will live update
7383de 148     * The Data you see in the screen is dummy / stubbed data. This is served up when there is no backend connection found
D 149
fd3541 150 7. The app is a todolist manager built in Vue.js. Play around with the App. You will notice when you add todos they appear and clear as expected. If you refresh the page you'll lose all additions. This is because there is no persistence layer. We will add one in the next part.
7383de 151
fd3541 152 8. The structure of the `todolist-fe` is as follows.
7383de 153 ```bash
D 154 todolist-fe
155 ├── jest.config.js
156 ├── jsconfig.json
157 ├── nightwatch.config.js
158 ├── node_modules
159 ├── package.json
160 ├── public
161 │   ├── favicon.ico
162 │   ├── img
163 │   ├── index.html
164 │   └── manifest.json
165 ├── src
166 │   ├── App.vue
167 │   ├── assets
168 │   ├── components
10416f 169 │   │   └── *
7383de 170 │   ├── config
D 171 │   ├── main.js
172 │   ├── registerServiceWorker.js
173 │   ├── router.js
174 │   ├── scss
175 │   ├── services
176 │   ├── store
10416f 177 │   │   └── *
7383de 178 │   └── views
10416f 179 │       └── *
7383de 180 ├── tests
D 181 │   ├── e2e
182 │   └── unit
183 └── vue.config.js
184 ```
185 where the following are the important things:
186     * `./src` is the main collection of files needed by the app. The entrypoint is the `main.js` which is used to load the root `App.vue` file.
187     * `./node_modules` is where the dependencies are stored
b5d705 188     * `./test` contains our end-to-end tests and unit tests. More covered on these in later exercises.
10416f 189     * `./src/components` contains small, lightweight reusable components for our app. For example, the `NewTodo` component which encapsulates the styling, logic and data for adding a new todo to our list
7383de 190     * `./src/store` is the `vuex` files for managing application state and backend connectivity
D 191     * `./src/views` is the view containers; which are responsible for loading components and managing their interactions.
192     * the `./src/router.js` controls routing logic. In our case the app only has one real endpoint.
1173e5 193     * `./src/scss` contains custom SCSS used in the application.
7383de 194     * `./*.js` is mostly config files for running and managing the app and the tests
c951f7 195
fd3541 196 9. To prepare Nexus to host the binaries created by the frontend and backend builds we need to run a prepare-nexus script. Before we do this we need to export some variables and change `<YOUR_NAME>` accordingly in the below commands.
RS 197
198 <p class="tip" >
d62b18 199 NOTE: Microsoft Windows users, run the following commands in the *do500-toolbox* container. Linux and MacOS users should skip this step and continue from the *oc login* step.
fd3541 200 </p>
RS 201
202 ```bash
313b88 203 docker run -it -v C:/do500-workspace:/home/tool-box/workarea:Z quay.io/redhat/do500-toolbox /bin/bash
fd3541 204 ```
RS 205
206 ```bash
207 oc login -u <username> -p <password> <CLUSTER_URL>
208 ```
4c8010 209 ```bash
18681b 210 export NEXUS_SERVICE_HOST=$(oc get route nexus --template='{{.spec.host}}' -n <YOUR_NAME>-ci-cd)
33c738 211 ```
D 212 ```bash
4c8010 213 export NEXUS_SERVICE_PORT=80
33c738 214 ```
D 215 ```bash
4c8010 216 npm run prepare-nexus
A 217 ```
fd3541 218 <!-- <p class="tip">
4c8010 219 NOTE - This step in a residency would be automated by a more complex nexus deployment in the ci-cd project
fd3541 220 </p> -->
RS 221
222 #### Todolist API (todolist-api)
223
224 1. Now let's move on to the `todolist-api` and wire them together. As with the `todolist-fe` we need to clone the repo and add it to our GitLab in the cluster.
225
226 <p class="tip" >
227 NOTE: Microsoft Windows users, run the following commands in a `Git Bash` window. Recall that the full path to the `do500-workspace` folder is `/c/do500-workspace` in `Git Bash`.
4c8010 228 </p>
A 229
d2e708 230 ```bash
fd3541 231 cd ~/do500-workspace
fad576 232 ```
D 233 ```bash
bec65b 234 git clone https://github.com/RedHatTraining/rht-labs-todolist-api todolist-api
fad576 235 ```
D 236 ```bash
b6978f 237 cd todolist-api
4c8010 238 ```
b6978f 239 ```bash
D 240 ./git-pull-all.sh
fad576 241 ```
D 242 ```bash
243 git checkout develop
d2e708 244 ```
D 245
8894bf 246 2. On GitLab; create a new project (internal) called `todolist-api` to host your clone of the project and copy its remote address as you did for the previous repositories.
d2e708 247
e209d3 248 3. In your local clone of the `todolist-api`, remove the origin and add the GitLab origin by replacing `<YOUR_GIT_LAB_PROJECT>`. Push your app to GitLab. (As before, we will bypass SSL key verification in this repo since we are using self-signed certificates on the GitLab sever.) 
d2e708 249 ```bash
e209d3 250 git config http.sslVerify false
b6978f 251 git remote set-url origin <YOUR_GIT_LAB_PROJECT>
D 252 ```
253 ```bash
254 git push -u origin --all
d2e708 255 ```
D 256
fd3541 257 4. Once pushed; explore the application. It is a NodeJS application with the Express.js framework and MongoDB for persistent storage. Same as before, the `package.json` defines most of the configuration etc. Install the dependencies
d2e708 258 ```bash
b6978f 259 npm i
d2e708 260 ```
D 261
fd3541 262 5. While the dependencies are being installed; explore the project structure.
d2e708 263 ```bash
D 264 todolist-api
265 ├── Dockerfile
266 ├── Gruntfile.js
267 ├── README.md
268 ├── node_modules
269 ├── package-lock.json
270 ├── package.json
271 ├── server
272 │   ├── api
273 │   │   └── todo
274 │   ├── app.js
275 │   ├── components
276 │   │   └── errors
277 │   ├── config
278 │   │   ├── environment
279 │   │   ├── express.js
280 │   │   ├── local.env.sample.js
281 │   │   └── seed.js
282 │   ├── mocks
283 │   │   ├── mock-routes-config.json
284 │   │   ├── mock-routes.js
285 │   │   └── mock-routes.spec.js
286 │   ├── routes.js
287 │   └── views
288 │       └── 404.html
289 └── tasks
290     └── perf-test.js
291 ```
292 where the following are the important things:
293     * `./server` is the main collection of files needed by the app. The entrypoint is the `app.js`
294     * `./node_modules` is where the dependencies are stored
1173e5 295     * `./server/api` is where the api's controller, data model & unit test are stored.
d2e708 296     * `./server/mocks` is a mock server used for when there is no DB access    
10416f 297     * `./server/config` stores our Express JS config, header information and other middleware.
1173e5 298     * `./server/config/environment` stores environment specific config; such as connectivity to backend services like MongoDB.
b5d705 299     * `./tasks` is a collection of additional `Grunt` tasks which will be used in later exercises
10416f 300     * `Grunt` is a taskrunner for use with Node.JS projects
d2e708 301     * `package.json` contains the dependency list and a lot of very helpful scripts for managing the app lifecycle
D 302
fd3541 303 6. A snippet of the npm scripts are shown below. There are application start scripts, build and test items which will be used in the build. The ones for MongoDB are just provided for convenience and require Docker installed to execute.
d2e708 304 ```json
D 305   "scripts": {
306     "start": "node server/app.js",
307     "dev": "./node_modules/.bin/grunt serve",
308     "jshint": "./node_modules/.bin/grunt jshint",
309     "clean": "rm -rf reports package-contents*",
310     "package": "zip -r package-contents.zip package-contents",
311     "test": "node_modules/.bin/nyc node_modules/.bin/mocha server/**/*.spec.js --exit",
312     "mongo" : "docker run -i -d --name mongo-local -p 27017:27017 mongo",
313     "mongo:drop" : "npm run mongo:stop && docker rm mongo-local",
314     "mongo:stop" : "docker stop mongo-local",
315     "mongo:start" : "docker start mongo-local"
316   },
456daa 317 ```
A 318
fd3541 319 7. To run the application; start a new instance of MongoDB by running the following. This will pull a mongodb image from Dockerhub and then start it for our API to connect to.
d2e708 320 ```bash
b6978f 321 npm run mongo
d2e708 322 ```
D 323 <p class="tip">
fd3541 324 NOTE - `npm run mongo:drop` is used to completely remove the running container. `npm run mongo:stop` & `npm run mongo:start` will preserve data in the container. Microsoft Windows users, if you are running this command for the first time, you may be prompted by the Windows Defender Firewall to allow access. Click `Allow Access` to continue.
d2e708 325 </p>
D 326
fd3541 327 8. Fire up the `todolist-api` by running.
d2e708 328 ```bash
b6978f 329 npm run start
d2e708 330 ```
D 331 ![node-app-started](../images/exercise2/node-app-started.png)
332
fd3541 333 9. Check things are up and running by testing the API with a `curl`. The API should return some seeded data (stored in `server/config/seed.js`)
d2e708 334 ```bash
b6978f 335 curl localhost:9000/api/todos
d2e708 336 ```
D 337 ```json
338 [{
339     "_id": "5ac8ff1fdfafb02138698948",
340     "title": "Learn some stuff about MongoDB",
341     "completed": false,
342     "__v": 0
343   },
344   {
345     "_id": "5ac8ff1fdfafb02138698949",
346     "title": "Play with NodeJS",
347     "completed": true,
348     "__v": 0
349 }]
350 ```
351
fd3541 352 10. Now let's check out `todolist-fe` app by reloading the browser. We should now see our dummy front end data is replaced by the backend seed data. Adding new todos will add them in the backend, these will persist when the page is refreshed.
d2e708 353 ![fullstack-app](../images/exercise2/fullstack-app.png)
D 354
1173e5 355 ### Part 2 - Add configs to cluster
f016b7 356 > _In this exercise; we will use the OpenShift Applier to drive the creation of cluster content required by the app such as MongoDB and the Apps Build / Deploy Config_
9fc88c 357
fd3541 358 1. On your terminal navigate to the root of the `todolist-fe` application. The app contains a hidden folder called `.openshift-applier`. Move into this `.openshift-applier` directory and you should see a familiar looking directory structure for an Ansible playbook.
cdcafd 359 ```
D 360 .openshift-applier
361 ├── README.md
362 ├── apply.yml
363 ├── inventory
364 │   ├── group_vars
365 │   │   └── all.yml
366 │   └── hosts
367 ├── params
368 │   ├── build
369 │   ├── dev
68d81a 370 │   ├── ocp-pipeline
cdcafd 371 │   └── test
D 372 ├── requirements.yml
373 └── templates
68d81a 374     ├── ocp-pipeline.yml
D 375     ├── todolist-fe-build.yml
376     └── todolist-fe-deploy.yml
cdcafd 377 ```
ea5b30 378 with the following
1173e5 379     * the `apply.yml` file is the entrypoint.
cdcafd 380     * the `inventory` contains the objects to populate the cluster with.
D 381     * the `params` contains the variables we'll apply to the `templates`
f016b7 382     * the `templates` required by the app. These include the Build, Deploy configs as well as the services, health checks, and other app definitions.
cdcafd 383
fd3541 384 2. There are a few updates to these manifests we need to make before applying the cluster content. In the `apply.yml` update the namespace `<YOUR_NAME>` variables accordingly.
cdcafd 385 ```yaml
D 386     ci_cd_namespace: donal-ci-cd
387     dev_namespace: donal-dev
388     test_namespace: donal-test
389 ```
390
fd3541 391 3. In the `params` folder update the `dev` and `test` files with the correct `<YOUR_NAME>` as you've done above. Example for the `dev` file:
cdcafd 392 ```bash
D 393 PIPELINES_NAMESPACE=donal-ci-cd
c58300 394 NAME=todolist-fe
cdcafd 395 DEPLOYER_USER=jenkins
D 396 APP_TAG=latest
397 NAMESPACE=donal-dev
398 ```
399
f016b7 400 4. With those changes in place we can now run the playbook. First install the `openshift-applier` dependency and then run the playbook (from the `.openshift-applier` directory). This will populate the cluster with all the config needed for the front end app.
fd3541 401
RS 402 <p class="tip">
403 NOTE - Microsoft Windows users, run the *oc* and *ansible* commands inside the *do500-toolbox* container from the appropriate folder.
404 </p>
405
406 ```bash
407 oc login -u <username> -p <password> <CLUSTER_URL>
408 ```
409
cdcafd 410 ```bash
b6978f 411 ansible-galaxy install -r requirements.yml --roles-path=roles
D 412 ```
413 ```bash
414 ansible-playbook apply.yml -i inventory/
cdcafd 415 ```
071905 416 ![ansible-success](../images/exercise2/ansible-success.png)
cdcafd 417
fd3541 418 5. Once successful, `commit` and `push` your changes to gitlab.
68d81a 419 ```bash
b6978f 420 git add .
D 421 ```
422 ```bash
423 git commit -m "UPDATE - change namespace vars to donal"
424 ```
425 ```bash
426 git push
68d81a 427 ```
f016b7 428
fd3541 429 6. Back on your terminal navigate to the root of the `todolist-api` application. Open the `.openshift-applier` directory in your editor. The same layout as seen in `todolist-fe` should be visible with one noticeable difference; the api requires `MongoDB` to connect to at runtime.
c58300 430
fd3541 431 7. In the `apply.yml` update the namespace `<YOUR_NAME>` variables accordingly. For example:
c58300 432 ```yaml
D 433     ci_cd_namespace: donal-ci-cd
434     dev_namespace: donal-dev
435     test_namespace: donal-test
436 ```
437
fd3541 438 8. In the `params` folder update the `dev` and `test` files with the correct `<YOUR_NAME>` as you've done above. Example for the `dev` file:
c58300 439 ```bash
D 440 PIPELINES_NAMESPACE=donal-ci-cd
441 NAME=todolist-api
442 DEPLOYER_USER=jenkins
443 APP_TAG=latest
444 NAMESPACE=donal-dev
445 ```
446
fd3541 447 9. Finally; run the Openshift Applier and install its dependencies to run the content into the cluster
RS 448
449 <p class="tip">
450 NOTE - Microsoft Windows users, run the *ansible* commands inside the *do500-toolbox* container from the appropriate folder.
451 </p>
452
c58300 453 ```bash
b6978f 454 ansible-galaxy install -r requirements.yml --roles-path=roles
D 455 ```
456 ```bash
457 ansible-playbook apply.yml -i inventory/
c58300 458 ```
9fc88c 459
fd3541 460 10. Once successful, `commit` and `push` your changes to gitlab.
68d81a 461 ```bash
b6978f 462 git add .
D 463 ```
464 ```bash
465 git commit -m "UPDATE - change namespace vars to donal"
466 ```
467 ```bash
468 git push
68d81a 469 ```
e90e9c 470
fd3541 471 11. Validate the build and deploy configs have been created in Openshift by checking `<YOUR_NAME> CI-CD builds` for the `BuildConfigs`
68d81a 472 ![ocp-app-bc](../images/exercise2/ocp-app-bc.png)
D 473
fd3541 474 12. Check `<YOUR_NAME>-dev` to see the deployment configs are in place
6c8424 475 ![ocp-app-dc](../images/exercise2/ocp-app-dc.png)
e90e9c 476
1173e5 477 ### Part 3 - Build > Bake > Deploy
0e648a 478 > _In this exercise; we take what we have working locally and get it working in OpenShift_
9fc88c 479
41217d 480 This exercise will involve creating three stages (or items) in our pipeline, each of these is detailed below at a very high level. Move on to the next step to begin implementation.
D 481 * a *build* job is responsible for compiling and packaging our code:
482     1. Checkout from source code (`develop` for `<yourname>-dev` & `master` for `<yourname>-test`)
483     2. Install node dependencies and run a build / package
484     3. Send the package to Nexus
485     4. Archive the workspace to persist the workspace in case of failure
486     4. Tag the GitLab repository with the `${JOB_NAME}.${BUILD_NUMBER}` from Jenkins. This is our `${BUILD_TAG}` which will be used on downstream jobs.
487     5. Trigger the `bake` job with the `${BUILD_TAG}` param
488 * a *bake* job should take the package and put it in a Linux Container
489     1. Take an input of the previous jobs `${BUILD_TAG}` ie `${JOB_NAME}.${BUILD_NUMBER}`.
8894bf 490     2. Checkout the binary from Nexus and unzip its contents
W 491     3. Run an oc start-build of the App's BuildConfig and tag its imagestream with the provided `${BUILD_TAG}`
41217d 492     4. Trigger a deploy job using the parameter `${BUILD_TAG}`
D 493 * a *deploy* job should roll out the changes by updating the image tag in the DC:
494     1. Take an input of the `${BUILD_TAG}`
495     2. Patch / set the DeploymentConfig to the image's `${BUILD_TAG}`
496     3. Rollout the changes
497     4. Verify the deployment
76d54e 498 * We will now go through these steps in detail.
9fc88c 499
5b1604 500 #### 3a - Build
9fc88c 501
fd3541 502 1. In the previous lab, if you deleted and re-created the three namespaces (*-ci-cd, *-dev and *-test), the Jenkins NPM slave image streams are no longer available. Run the following to re-label the Jenkins NPM slave image streams. Windows users should run these commands from inside the `do500-toolbox` container.
9fc88c 503
fd3541 504 ```bash
RS 505 oc project <YOUR_NAME>-ci-cd
506 ```
507 ```bash
508 oc tag openshift/jenkins-slave-npm:latest jenkins-slave-npm:latest
509 ```
510 ```bash
511 oc label is jenkins-slave-npm role=jenkins-slave
512 ```
9fc88c 513
fd3541 514 2. With the BuildConfig and DeployConfig in place for both our apps (`*-fe` & `*-api`) from previous steps; Log into Jenkins and create a `New Item`. This is just jenkins speak for a new job configuration. ![new-item](../images/exercise2/new-item.png)
RS 515
516 3. Name this job `dev-todolist-fe-build` and select `Freestyle Project`. All our jobs will take the form of `<ENV>-<APP_NAME>-<JOB_PURPOSE>`. ![freestyle-job](../images/exercise2/freestyle-job.png)
517
518 4. The page that loads is the Job Configuration page and it can be returned to at anytime from Jenkins. Let's start configuring our job. To conserve space; we will make sure Jenkins only keeps the last build's artifacts. Tick the `Discard old builds` checkbox, then `Advanced` and set `Max # of builds to keep with artifacts` to 1 as indicated below
b815cc 519 ![keep-artifacts](../images/exercise2/keep-artifacts.png)
43f2f2 520
5b1604 521 5. Our NodeJS build needs to be run on the `jenkins-slave-npm` we bought in in the previous chapter. Specify this in the box labelled `Restrict where this project can be run` ![label-jenkins-slave](../images/exercise2/label-jenkins-slave.png)
c951f7 522
fd3541 523 6. On the Source Code Management tab, select the Git radio button, specify the endpoint for our GitLab `todolist-fe` Project and specify your credentials from the dropdown box. Set the Branch Specifier to `develop`. ![git-scm](../images/exercise2/git-scm.png)
c951f7 524
fd3541 525 7. Scroll down to the Build Environment tab and select the `Color ANSI Console Output` checkbox ![ansi](../images/exercise2/ansi.png)
e919d0 526
fd3541 527 8. Move on to the Build section and select `Add build step`. From the dropdown select `Execute Shell`. On the box that appears; insert the following, to build package and deploy our app to Nexus:
e919d0 528 ```bash
D 529 set -o xtrace
4acca2 530 npm install
D 531 npm run build:ci:dev
532 npm run package
533 npm run publish
e919d0 534 ```
D 535 ![build-step](../images/exercise2/build-step.png)
536
fd3541 537 9. Scroll to the final section; the Post-build Actions. Add a new post-build action from the dropdown called `Archive the artifacts` and specify `**` in the box. This will zip the entire workspace and copy it back to Jenkins for inspection if needed. ![archive-artifacts](../images/exercise2/archive-artifacts.png)
e919d0 538
fd3541 539 10. On the Post-build Actions; Add another post-build action from the dropdown called `Git Publisher`. This is useful for tying the git check-in to the feature in your tracking tool to the built product.
e919d0 540     * Tick the box `Push Only If Build Succeeds`
1173e5 541     * Add the Tag to push of
e919d0 542 ```bash
D 543 ${JOB_NAME}.${BUILD_NUMBER}
544 ```
545     * Specify the commit message to be
546 ```bash
547 Automated commit by jenkins from ${JOB_NAME}.${BUILD_NUMBER}
548 ```
1b285d 549
A 550     * Check `Create New Tag` and set `Target remote name` to `origin`
e919d0 551 ![git-publisher](../images/exercise2/git-publisher.png)
D 552
fd3541 553 11. Finally; add the trigger for the next job in the pipeline. This is to trigger the bake job with the current build tag. Add another post-build action from the dropdown called `Trigger parameterized build on other projects`.
1173e5 554     * Set the project to build to be `dev-todolist-fe-bake`
CM 555     * Set the condition to be `Stable or unstable but not failed`.
556     * Click Add Parameters dropdown and select Predefined parameters.
e919d0 557     * In the box, insert our BUILD_TAG as follows
D 558 ```bash
559 BUILD_TAG=${JOB_NAME}.${BUILD_NUMBER}
560 ```
561 ![param-trigger](../images/exercise2/param-trigger.png)
562 <p class="tip">
3b5f91 563     NOTE - Jenkins might say "No such project ‘dev-todolist-fe-bake’. Did you mean ...." at this point. Don't worry; it's because we have not created the next job yet.
e919d0 564 </p>
D 565
fd3541 566 12. Hit `save` which will take you to the job overview page - and that's it; our *build* phase is complete!
e919d0 567
5b1604 568 #### 3b - Bake
41217d 569
fd3541 570 1. Next we will setup our *bake* phase; which is a little simpler. Go to Jenkins home and create another Freestyle Job (as before) called `dev-todolist-fe-bake`.
e919d0 571
fd3541 572 2. This job will take in the BUILD_TAG from the previous one so check the `This project is parameterized` box on the General tab.
3b5f91 573     * Add string parameter type
1b285d 574     * set the Name to `BUILD_TAG`. This will be available to the job as an Enviroment Variable.
3b5f91 575     * You can set `dev-todolist-fe-build.` as the default value for ease when triggering manually.
1173e5 576     * The description is not required but a handy one for reference would be `${JOB_NAME}.${BUILD_NUMBER} of previous build e.g. dev-todolist-fe-build.1232`
2ff842 577 <p class="tip">
A 578     NOTE - Don't forget to include the `.` after `dev-todolist-fe-build` in the Default Value box.
579 </p>
580
3b5f91 581 ![param-trigger-bake](../images/exercise2/param-trigger-bake.png)
c951f7 582
fd3541 583 3. This time set the `Restrict where this project can be run` label to `master`.
3b5f91 584 <p class="tip">
2ff842 585     NOTE - `Master` is the default node that jobs run on. We don't want jenkins to execute the *bake* on any other nodes if the `master` is busy so it is always safer to specify it here.
3b5f91 586 </p>
D 587
fd3541 588 4. There is no Git or SCM needed for this job so move down to the Build Environment and tick `Delete workspace before build starts`
3b5f91 589
579436 590 5. Scroll down to the Build Environment tab and select the `Color ANSI Console Output` checkbox ![delete-ansi](../images/exercise2/delete-ansi.png)
3b5f91 591
fd3541 592 6. Move on to the Build section and select `Add build step`. From the dropdown select `Execute Shell`. On the box the appears; insert the following, to pull the package from Nexus. We patch the BuildConfig with the Jenkins Tag to get traceablility from feature to source code to built item. Finally; the oc start-build command is run:
1b285d 593 Remember to replace `<YOUR_NAME>` accordingly.
3b5f91 594 ```bash
D 595 #!/bin/bash
15b8f6 596 curl -v -f \
D 597     http://admin:admin123@${NEXUS_SERVICE_HOST}:${NEXUS_SERVICE_PORT}/repository/zip/com/redhat/todolist/${BUILD_TAG}/package-contents.zip \
598     -o package-contents.zip
3b5f91 599 unzip package-contents.zip
cebd30 600 oc project <YOUR_NAME>-ci-cd
3b5f91 601 NAME=todolist-fe
9adaf7 602 oc patch bc ${NAME} -p "{\"spec\":{\"output\":{\"to\":{\"kind\":\"ImageStreamTag\",\"name\":\"${NAME}:${BUILD_TAG}\"}}}}"
3b5f91 603 oc start-build ${NAME} --from-dir=package-contents/ --follow
D 604 ```
605 ![bake-step](../images/exercise2/bake-step.png)
606
fd3541 607 7. Finally; add the trigger for the next job in the pipeline. Add a post-build action from the dropdown called `Trigger parameterized build on other projects`.
3b5f91 608     * Set the project to build to be `dev-todolist-fe-deploy`
D 609     * Set the condition to be `Stable`.
3dce53 610     * Click Add Parameters dropdown and select `Current build parameters`. This will pass the `${BUILD_TAG}` to the downstream job which we will create next.
3b5f91 611 ![downstream-trigger-deploy](../images/exercise2/downstream-trigger-deploy.png)
D 612
fd3541 613 8. Hit save! That's our *bake* phase done! Finally; on to our *deploy*
3b5f91 614
5b1604 615 #### 3c - Deploy
41217d 616
fd3541 617 1. Next we will setup our *deploy* phase. This job is very similar in setup to the *bake* phase so this time go to Jenkins home and create `dev-todolist-fe-deploy` Job but scroll to the bottom and Copy from `dev-todolist-fe-bake`.
579436 618 ![copy-from](../images/exercise2/copy-from.png)
D 619
fd3541 620 2. The only two differences between these jobs is the Build Step and there are no Post Build Actions. First to the Build tab and add the following to the shell box. The process for running the deploy is to tag the image created previously for use in the `ci-cd` namespace for use in the dev project. Then update the DeploymentConfig to use the Jenkins Tag which kicked the process off. Once successful; the changes are rolled out. Remember to change `<YOUR_NAME>` accordingly.
579436 621 ```bash
D 622 #!/bin/bash
623 set -o xtrace
624 # VARS
1b285d 625 PIPELINES_NAMESPACE=<YOUR_NAME>-ci-cd
A 626 NAMESPACE=<YOUR_NAME>-dev
579436 627 NAME=todolist-fe
D 628 oc project ${NAMESPACE}
629 oc tag ${PIPELINES_NAMESPACE}/${NAME}:${BUILD_TAG} ${NAMESPACE}/${NAME}:${BUILD_TAG}
630 oc set env dc ${NAME} NODE_ENV=dev
631 oc set image dc/${NAME} ${NAME}=docker-registry.default.svc:5000/${NAMESPACE}/${NAME}:${BUILD_TAG}
632 oc rollout latest dc/${NAME}
633 ```
634 ![deploy-step](../images/exercise2/deploy-step.png)
635
fd3541 636 3. When a deployment has completed; OpenShift can verify its success. Add another step by clicking the `Add build Step` on the Build tab then `Verify OpenShift Deployment` including the following:
579436 637     * Set the Project to your `<YOUR_NAME>-dev`
D 638     * Set the DeploymentConfig to your app's name `todolist-fe`
639     * Set the replica count to `1`
640 ![verify-deployment](../images/exercise2/verify-deployment.png)
641
fd3541 642 4. Delete the Post Build Action to trigger another job (by hitting the red X). Save the configuration. We're almost ready to run the pipeline!
RS 643
644 5. Since we are using self-signed certificates for the Git server instance in the lab, you need to set some global environment variables in the Jenkins configuration to bypass SSL certificate verification and git client configuration
645     * From the Jenkins main page, navigate to `Manage Jenkins` > `Configure System` and then scroll down to the `Git plugin` section and add your username and email as follows:
646
647     ![jenkins-git-client-config](../images/exercise2/jenkins-git-client-config.png)
648
649     * In the same page, locate the `jenkins-slave-npm` section under `Kubernetes Pod Template` and add add a new environment variable called `GIT_SSL_NO_VERIFY` and set its value to `true`:
650     
651     ![jenkins-slave-npm-nossl](../images/exercise2/jenkins-slave-npm-nossl.png)
652
653     * Click `Save` at the bottom of the page to save your global settings.
579436 654
5b1604 655 #### 3d - Pipeline
41217d 656
fd3541 657 1. With our Jenkins setup in place; now move to our `todolist-fe` app's source code. We have to add our configuration to the frontend to tell it where the API layer will be hosted. Open the source in your favourite editor and navigate to `src/config/dev.js`.
3dce53 658
fd3541 659 2. Update `<YOUR_NAME>` accordingly with the route where the Todo List API will live when it is deployed. The correct full URL can also be found on the OpenShift Console; if you copy it from there remember to append `/api/todos` to the URL. For example:
41217d 660 ![fe-dev-config](../images/exercise2/fe-dev-config.png)
D 661
fd3541 662 3. Repeat this for `src/config/test.js` file. If you copy the URL from the previous step; change `dev` to `test`.
33c738 663 For example:
D 664 ![fe-test-config](../images/exercise2/fe-test-config.png)
665
fd3541 666 4. With the config in place; commit your changes and push them to GitLab:
41217d 667 ```bash
b6978f 668 git add .
D 669 ```
670 ```bash
671 git commit -m "ADD config for api"
672 ```
673 ```bash
674 git push
41217d 675 ```
D 676
cebd30 677 5. Back on Jenkins; We can tie all the jobs in the pipeline together into a nice single view using the Build Pipeline view. Back on the Jenkins home screen Click the + beside the all tab on the top.
D 678 ![add-view](../images/exercise2/add-view.png)
679
fd3541 680 6. On the view that loads; Give the new view a sensible name like `dev-todolist-fe-pipeline` and select Build Pipeline View
cebd30 681 ![new-pipeline](../images/exercise2/new-pipeline.png)
579436 682
fd3541 683 7. Set the Pipeline Flow's Inital Job to `dev-todolist-fe-build` and save.
579436 684 ![pipeline-flow](../images/exercise2/pipeline-flow.png)
D 685
fd3541 686 8. You should now see the pipeline view. Run the pipeline by hitting run (you can move onto the next part while it is running as it may take some time).
af284d 687 ![dev-pipeline-view](../images/exercise2/dev-pipeline-view.jpeg)
579436 688
fd3541 689 <p class="tip">
RS 690     NOTE - The pipeline may fail on the first run. In such cases, re-run the pipeline once more and the three stages will run successfully and show three green cards. 
691 </p>
692
693 9. To check the deployment in OpenShift; open the web console and go to your `dev` namespace. You should see the deployment was successful; hit the URL to open the app (the screenshot below has both apps deployed).
cebd30 694 ![ocp-deployment](../images/exercise2/ocp-deployment.png)
D 695
fd3541 696 10. If it has been a success we should see our dummyData. This is because there is no backend deployed, in later labs we will deploy the backend and the mongodb for persistence but to do this we will use Jenkins Pipeline as code.
d00794 697 ![no-backend-app](../images/exercise2/no-backend-app.png)
c951f7 698
fd3541 699 <!-- ### Part 4 - (Optional) GitLab Webhooks
b00f5a 700 > _In this exercise we will link GitLab to Jenkins so that new build jobs are triggered on each push to the `develop` branch._
11b338 701
b00f5a 702 <p class="tip" >
943e9f 703 NOTE - This section is optional! Git webhooks are useful but not needed for Enablement completion.
b00f5a 704 </p>
D 705
530a25 706 7. In order to allow GitLab to trigger Jenkins (because of the OpenShift Auth Plugin), we need to allow the `Anonymous` user triggered builds. Head to your Jenkins Dashboard and click on `Manage Jenkins` on the left hand side. Then scroll down and click `Configure Global Security`. Alternatively, type in `https://jenkins-<YOUR_NAME>-ci-cd.<APPS_URL>/configureSecurity/` . You should see a screen like so:
106095 707 ![jenkins-global-security](../images/exercise2/jenkins-global-security.png)
A 708
709 7. Scroll down to the `Authorization` section and allow `Anonymous` to create jobs. Do this by navigating through the matrix of checkboxes and check `Build` and `Cancel` under the Job heading. Leave all other user behaviour as is. Anonymous is the user that GitLab will act as so this allows the WebHook to trigger builds. (The screenshot has been cropped to bring Job further to the left.) Hit `Save` or `Apply`.
710 ![jenkins-anon-permissions](../images/exercise2/jenkins-anon-permissions.png)
711
530a25 712 7. Go to your `dev-todolist-fe-build` and head to the `configure` section (`https://jenkins-<YOUR_NAME>-ci-cd.<APPS_URL>/job/dev-todolist-fe-build/configure`). Scroll down to the `Build Triggers` section and check the `Build when a change is pushed to GitLab` box. Leave all the other settings as they are but copy the `GitLab webhook URL`. `https://jenkins-<YOUR_NAME>-ci-cd.<APPS_URL>/project/dev-todolist-fe-build`. Remember to Save and Apply this change.
0c725b 713 ![jenkins-build-triggers-gitlab](../images/exercise2/jenkins-build-triggers-gitlab.png)
11b338 714
530a25 715 7. Switch over to GitLab and select your `todolist-fe` repository. On the left hand task bar hover over the settings cog and select `integrations`. (`https://gitlab-<YOUR_NAME>-ci-cd.<APPS_URL>/<YOUR_NAME>/todolist-fe/settings/integrations`)
0c725b 716 ![gitlab-integrations](../images/exercise2/gitlab-integrations.png)
11b338 717
0c725b 718 7. Paste the `GitLab webhook URL` that we copied earlier into the `URL` field. Check Push events as the trigger, and make sure you `uncheck` the `SSL verification` checkbox. Click Add webhook at the bottom.
A 719 ![gitlab-integrations-details](../images/exercise2/gitlab-integrations-details.png)
11b338 720
0c725b 721 7. Before we move on let's test the webhook. Select the Test drop down and click `Push events`. This will trigger the test and return a status code at the top of the page. If all goes well it should be a cool blue 200.
A 722 ![gitlab-integrations-details](../images/exercise2/gitlab-webhook-test.png)
11b338 723
0c725b 724 7. We can now test this properly by heading into the `todolist-fe` repository through <YOUR_FAVOURITE_EDITOR>. Make a small change to your code, then commit and push it, ensuring you're on the develop branch. Then head over to Jenkins and wait until the `dev-todolist-fe-build` job has been triggered.
11b338 725
0c725b 726 7. We now have a working GitLab webhook so any time we push code it will automatically build! Next up we'll show you how to add tests to your pipeline.
b00f5a 727
43f2f2 728 _____
D 729
c951f7 730 ## Extension Tasks
cf415b 731 > _Ideas for go-getters. Advanced topic for doers to get on with if they finish early. These will usually not have a solution available and are provided for additional scope._
c951f7 732
d00794 733 - Pipeline Tasks
456daa 734     * Add pipeline for `master` branch for each project. Use `test-` instead of `dev-` across all config and names in the pipeline
A 735     * Do the `.openshift-applier` steps as part of the pipeline for greater end to end automation.
e919d0 736 - Promote build
456daa 737     * Create a _promote-to-uat_ phase after the `master` branch deploy
cf415b 738     * Create a `uat` env using the OpenShift Applier as seen before
e919d0 739     * Tag and promote the image without rebuilding after the `test-**-deploy`
c58300 740 - MongoDB tasks
e919d0 741     * Add MongoDB Stateful set for the UAT environment (or test)
D 742     * Inject MongoDB config into the NodeJS app using config map & secrets.
cf415b 743     * Improve the security of the DB by making the user /passwords randomly generated
e919d0 744 - Setup Nexus as an `npm` mirror registry and use it in the builds to speed up the build time
c951f7 745
D 746 ## Additional Reading
43f2f2 747 > List of links or other reading that might be of use / reference for the exercise
7383de 748
43f2f2 749 ## Slide links
7c832b 750
01c4da 751 - [Intro](https://docs.google.com/presentation/d/1t1CONuy-_IRPZYmU010Qgk2rshiDJTennvLyQR8GllE)
RH 752 - [Wrap-up](https://docs.google.com/presentation/d/1kZ8SV6iJnrKk_AqPpyPuNZifv7VzItHOB9HYdOnNJjI)
fd3541 753 - [All Material](https://drive.google.com/drive/folders/1lf66ks2tT0eQ4A9RSU48u0ZhvBXzoHWJ) -->