acammies
2018-04-11 e90e9c00de9d3592889df0181e78c5d38c0acfd3
commit | author | age
2b8436 1 # Attack of the Pipelines
c951f7 2
2b8436 3 > In this lab we will explore the sample TODO List application and create a pipeline in Jenkins to build and deploy our code.
D 4
5 ![jenkins-time](../images/exercise2/jenkins-time.jpg)
6
43f2f2 7 _____
c951f7 8
D 9 ## Learning Outcomes
2b8436 10 As a learner by the end of this lesson you will be able to
D 11
12 - Build and run the full stack of the TODO List application locally
13 - Create an un-gated pipeline using the Jenkins UI for the backend and frontend
14 - Add branching to the pipeline to target specific namespace
c951f7 15
D 16 ## Tools and Frameworks
2b8436 17 > The following tools are used throughout this exercise. Familiarity with them is not required but knowing what they are may help!
c951f7 18
D 19 1. [Jenkins](https://jenkins.io/) - OpenSource build automation server; highly customisable through plugins
2b8436 20 1. [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.
D 21 1. [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
22 1. [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 23
43f2f2 24 ## Big Picture
2b8436 25 > From the previous exercise; we created some supporting tooling needed by our app/
43f2f2 26
D 27 _____
c951f7 28
D 29 ## 10,000 Ft View
2b8436 30 > _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 31
2b8436 32 2. Import the projects into your gitlab instance. See README of each for build instructions
43f2f2 33
2b8436 34 2. Deploy a `MongoDB` using the provided template to all project namespace.
D 35
36 2. Create 2 pipline with three stages (`build`, `bake`, `deploy`) in jenkins for `develop` & `master` branches on the `todolist-fe` such that:
e919d0 37     * a `Build` job is responsible for compiling and packaging our code:
2b8436 38         1. Checkout from source code (`develop` for `<yourname>-dev` & `master` for `<yourname>-test`)
D 39         2. Install node dependencies and run a build / package
e919d0 40         3. Send the package to Nexus
D 41         4. Archive the workspace to persist the workspace in case of failure
42         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.
43         5. Trigger the `bake` job with the `${BUILD_TAG}` param
44     * a `Bake` job should take the package and put it in a Linux Container
2b8436 45         1. Take an input of the previous jobs `${BUILD_TAG}` ie `${JOB_NAME}.${BUILD_NUMBER}`.
D 46         2. Checkout the binary from Nexus and unzip it's contents
e919d0 47         3. Run an oc start-build of the App's BuildConfig and tag it's imagestream with the provided `${BUILD_TAG}`
2b8436 48         4. Trigger a deploy job using the parameter `${BUILD_TAG}`
e919d0 49     * a `deploy` job should roll out the changes by updating the image tag in the DC:
2b8436 50         1. Take an input of the `${BUILD_TAG}`
D 51         2. Patch / set the DeploymentConfig to the image's `${BUILD_TAG}`
52         3. Rollout the changes
53         4. Verify the deployment
54
55 2. Repeat the above setup for the backend `todolist-fe`. TIP - use the copy config to speed things up!
56
57 2. Verify that both apps and the DB are talking to one another as expected.
c951f7 58
D 59 ## Step by Step Instructions
7383de 60 > This is a fairly structured guide with references to exact filenames and sections of text to be added.
c951f7 61
f016b7 62 ### Part 1 - Explore the Todo List App
7383de 63 > _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 64
7383de 65 2. Git clone the `todolist-fe` project to somewhere sensible and checkout the `develop` branch.
D 66 ```bash
67 $ git clone https://github.com/springdo/todolist-fe.git
10416f 68 $ git cd todolist-fe
7383de 69 $ git checkout develop
c951f7 70 ```
7383de 71
10416f 72 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 it's remote address. ![new-gitlab-proj](../images/exercise2/new-gitlab-proj.png)
7383de 73
D 74 2. 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
75 ```bash
10416f 76 $ git remote set-url origin <YOUR_GIT_LAB_PROJECT>
A 77 # verify the origin has been updated
78 $ git remote -v
7383de 79 $ git push -u origin --all
c951f7 80 ```
7383de 81
D 82 2. To get the app running locally; first check you've got node and npm installed
83 ```bash
84 $ node -v
85 $ npm -v
86 ```
87 <p class="tip" > 
88 NOTE - If you are missing these dependencies; install them with ease using the [Node Version Manager](https://github.com/creationix/nvm)
89 </p>
90 ![node-version](../images/exercise2/node-version.png)
91
92 2. The `todolist-fe` has a package.json at the root of the project, this defines some configuration for the app including it's dependencies, dev dependencies, scripts and other configuration. Install the apps dependencies
93 ```bash
94 $ npm install
95 ```
96
76d54e 97 2. The `todolist-fe` has some scripts defined in the package.json at the root of the project. To run any of these scripts run `npm run <SCRIPT_NAME>`. Let's start by serving our application
7383de 98  ![npm-scripts](../images/exercise2/npm-scripts.png)
D 99 ```bash
100 npm run serve
101 ```
102
10416f 103 2. 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 104  ![todo-list-app](../images/exercise2/todo-list-app.png)
10416f 105     * Click 'Todo' at the top of the home page to get to the above page.
7383de 106     * The server hosting it live reloads; so as you make changes to your code; the app will live update
D 107     * The Data you see in the screen is dummy / stubbed data. This is served up when there is no backend connection found
108
d2e708 109 2. 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 loose all additions. This is because there is persistence
7383de 110
D 111 3. The structure of the `todolist-fe` is as follows.
112 ```bash
113 todolist-fe
114 ├── jest.config.js
115 ├── jsconfig.json
116 ├── nightwatch.config.js
117 ├── node_modules
118 ├── package.json
119 ├── public
120 │   ├── favicon.ico
121 │   ├── img
122 │   ├── index.html
123 │   └── manifest.json
124 ├── src
125 │   ├── App.vue
126 │   ├── assets
127 │   ├── components
10416f 128 │   │   └── *
7383de 129 │   ├── config
D 130 │   ├── main.js
131 │   ├── registerServiceWorker.js
132 │   ├── router.js
133 │   ├── scss
134 │   ├── services
135 │   ├── store
10416f 136 │   │   └── *
7383de 137 │   └── views
10416f 138 │       └── *
7383de 139 ├── tests
D 140 │   ├── e2e
141 │   └── unit
142 └── vue.config.js
143 ```
144 where the following are the important things:
145     * `./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.
146     * `./node_modules` is where the dependencies are stored
147     * `./test` contains our end-to-end tests and unit tests. More covered on these in later labs.
10416f 148     * `./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 149     * `./src/store` is the `vuex` files for managing application state and backend connectivity
D 150     * `./src/views` is the view containers; which are responsible for loading components and managing their interactions.
151     * the `./src/router.js` controls routing logic. In our case the app only has one real endpoint.
152     * `./src/scss` contains custom  SCSS used in the application.
153     * `./*.js` is mostly config files for running and managing the app and the tests
c951f7 154
d2e708 155 2. 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.
D 156 ```bash
157 $ git clone https://github.com/springdo/todolist-api.git
10416f 158 $ git cd todolist-api
d2e708 159 $ git checkout develop
D 160 ```
161
162 2. Create a new project (internal) in GitLab called `todolist-api` to host your clone of the project and copy it's remote address.
163
164 2. 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
165 ```bash
10416f 166 $ git remote set-url origin <YOUR_GIT_LAB_PROJECT>
d2e708 167 $ git push -u origin --all
D 168 ```
169
170 2. 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
171 ```bash
10416f 172 # npm i === npm install
d2e708 173 $ npm i
D 174 ```
175
176 2. While the dependencies are being installed; explore the project structure.
177 ```bash
178 todolist-api
179 ├── Dockerfile
180 ├── Gruntfile.js
181 ├── README.md
182 ├── node_modules
183 ├── package-lock.json
184 ├── package.json
185 ├── server
186 │   ├── api
187 │   │   └── todo
188 │   ├── app.js
189 │   ├── components
190 │   │   └── errors
191 │   ├── config
192 │   │   ├── environment
193 │   │   ├── express.js
194 │   │   ├── local.env.sample.js
195 │   │   └── seed.js
196 │   ├── mocks
197 │   │   ├── mock-routes-config.json
198 │   │   ├── mock-routes.js
199 │   │   └── mock-routes.spec.js
200 │   ├── routes.js
201 │   └── views
202 │       └── 404.html
203 └── tasks
204     └── perf-test.js
205 ```
206 where the following are the important things:
207     * `./server` is the main collection of files needed by the app. The entrypoint is the `app.js`
208     * `./node_modules` is where the dependencies are stored
209     * `./server/api` is where the api's controller, data model & unit test are stored. 
210     * `./server/mocks` is a mock server used for when there is no DB access    
10416f 211     * `./server/config` stores our Express JS config, header information and other middleware.
A 212     * `./server/config/environment` stores enviromnent specific config; such as connectivity to backend services like MongoDB.
d2e708 213     * `./tasks` is a collection of additional `Grunt` tasks which will be used in later labs
10416f 214     * `Grunt` is a taskrunner for use with Node.JS projects
d2e708 215     * `package.json` contains the dependency list and a lot of very helpful scripts for managing the app lifecycle
D 216
217 2. 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.
218 ```json
219   "scripts": {
220     "start": "node server/app.js",
221     "dev": "./node_modules/.bin/grunt serve",
222     "jshint": "./node_modules/.bin/grunt jshint",
223     "jshint:ci": "./node_modules/.bin/grunt jshint:ci_server",
224     "clean": "rm -rf reports package-contents*",
225     "build": "mkdir -p package-contents && cp -vr server Dockerfile package.json package-contents",
226     "package": "zip -r package-contents.zip package-contents",
227     "test": "node_modules/.bin/nyc node_modules/.bin/mocha server/**/*.spec.js --exit",
228     "test:ci": "export MOCHA_FILE='reports/server/mocha/test-results.xml' && export NODE_ENV=ci && node_modules/.bin/nyc node_modules/.bin/mocha server/**/*.spec.js -R mocha-junit-reporter --exit",
229     "mongo" : "docker run -i -d --name mongo-local -p 27017:27017 mongo",
230     "mongo:drop" : "npm run mongo:stop && docker rm mongo-local",
231     "mongo:stop" : "docker stop mongo-local",
232     "mongo:start" : "docker start mongo-local"
233   },
234 ```
235
236 2. To run the application; start a new instance of the MongoDB by running. 
237 ```bash
238 $ npm run mongo
239 ```
240 <p class="tip">
241 `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
242 </p>
243
244 2. Fire up the `todolist-api` by running.
245 ```bash
246 $ npm run start
247 ```
248 ![node-app-started](../images/exercise2/node-app-started.png)
249
10416f 250 2. 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 251 ```bash
D 252 $ curl localhost:9000/api/todos
253 ```
254 ```json
255 [{
256     "_id": "5ac8ff1fdfafb02138698948",
257     "title": "Learn some stuff about MongoDB",
258     "completed": false,
259     "__v": 0
260   },
261   {
262     "_id": "5ac8ff1fdfafb02138698949",
263     "title": "Play with NodeJS",
264     "completed": true,
265     "__v": 0
266 }]
267 ```
268
10416f 269 2. 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 270 ![fullstack-app](../images/exercise2/fullstack-app.png)
D 271
272
9fc88c 273 ### Part 2 - Create a NodeJS Build slave
399baa 274 > _In this exercise; we will create a build configuration to generate a slave for Jenkins to use in it's builds_
9fc88c 275
399baa 276 3. In order for Jenkins to be able to run `npm` builds and installs as we have done locally, we must configure a `jenkins-build-slave` for Jenkins to use. This slave will be dynamically provisioned when we run a build. It needs to have NodeJS and npm installed in it. In your `enablement-cd-cd` repository, checkout the template and configuration. This will bring in the template, the params & the `Dockerfile`.
9fc88c 277 ```bash
D 278 $ git checkout exercise2/jenkins-slave docker/ templates/ params/
279 ```
280
281 3. Open the `params/jenkins-slave-npm` file and update `<YOUR_ENABLEMENT_GIT_REPO>` accordingly. This set of parameters will clone from the enablement repo and run a docker build of the Dockerfile stored in `docker/jenkins-slave-npm`.
282 ```bash
283 SOURCE_REPOSITORY_URL=<YOUR_ENABLEMENT_GIT_REPO>
284 SOURCE_CONTEXT_DIR=docker/jenkins-slave-npm
285 NAME=npm-jenkins-slave
286 ```
287
f016b7 288 3. Create an item in the `inventory/group_vars/all.yml` under the `ci-cd-builds` object to run the template with. Don't forget to substitute `<YOUR_NAME>`
9fc88c 289 ```yaml
D 290   - name: "jenkins-npm-slave"
291     namespace: "<YOUR_NAME>-ci-cd"
292     template: "{{ inventory_dir }}/../templates/jenkins-slave-generic-template.yml"
293     params: "{{ inventory_dir }}/../params/jenkins-slave-npm"
294     tags:
295     - jenkins-slave
296 ```
297 ![jenkins-slave-ansible](../images/exercise2/jenkins-slave-ansible.png)
298
299 3. Run the OpenShift Applier to trigger a build of this jenkins slave image.
300 ```bash
301 $ ansible-playbook roles/openshift-applier/playbooks/openshift-cluster-seed.yml \
302      -i inventory/ \
303      -e "filter_tags=jenkins-slave"
304 ```
305
306 3. Verify the build executed successfully by logging into the cluster and checking the `builds` tab of the `<YOUR_NAME>-ci-cd` project.
307 ![jenkins-slave-npm-build](../images/exercise2/jenkins-slave-npm-build.png)
308
309 3. You should now be able to apply the label `jenkins-slave-npm` to a build job to run a build on this newly created slave as we will see in the rest of this lab
310 <p class="tip">
311 NOTE - Jenkins may need to be restarted for the configuration to appear. To do this; navigate to your jenkins instance and add `/restart` to the url.
312 </p>
313
314 ### Part 3 - Add configs to cluster 
f016b7 315 > _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 316
f016b7 317 4. On your terminal navigate to the root of the `todolist-fe` application. The app contains a hidden folder called `.openshift-applier`. Move `cd .openshift-applier` into this directory and you should see a familiar looking directory structure for an ansible playbook. 
cdcafd 318 ```
D 319 .openshift-applier
320 ├── README.md
321 ├── apply.yml
322 ├── inventory
323 │   ├── group_vars
324 │   │   └── all.yml
325 │   └── hosts
326 ├── params
327 │   ├── build
328 │   ├── dev
329 │   └── test
330 ├── requirements.yml
331 └── templates
332     ├── todolist-api-build.yml
333     └── todolist-api-deploy.yml
334 ```
335 where the following
336     * the `apply.yml` file is the entrypoint.
337     * the `inventory` contains the objects to populate the cluster with.
338     * the `params` contains the variables we'll apply to the `templates`
f016b7 339     * the `templates` required by the app. These include the Build, Deploy configs as well as the services, health checks, and other app definitions.
cdcafd 340
D 341 4. 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.
342 ```yaml
343     ci_cd_namespace: donal-ci-cd
344     dev_namespace: donal-dev
345     test_namespace: donal-test
346 ```
347
f016b7 348 4. 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 349 ```bash
D 350 PIPELINES_NAMESPACE=donal-ci-cd
c58300 351 NAME=todolist-fe
cdcafd 352 DEPLOYER_USER=jenkins
D 353 APP_TAG=latest
354 NAMESPACE=donal-dev
355 ```
356
f016b7 357 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.
cdcafd 358 ```bash
D 359 $ ansible-galaxy install -r requirements.yml --roles-path=roles
360 $ ansible-playbook apply.yml -i inventory/
361 ```
071905 362 ![ansible-success](../images/exercise2/ansible-success.png)
cdcafd 363
f016b7 364 4. Once successful, `commit` and `push` your changes to gitlab.
A 365
e90e9c 366 4. Back on your terminal navigate to the root of the `todolist-api` application. Open the `.openshift-applier` directory. 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 367
D 368 4. In the `apply.yml` update the namespace `<YOUR_NAME>` variables accordingly. For example:
369 ```yaml
370     ci_cd_namespace: donal-ci-cd
371     dev_namespace: donal-dev
372     test_namespace: donal-test
373 ```
374
375 4. 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:
376 ```bash
377 PIPELINES_NAMESPACE=donal-ci-cd
378 NAME=todolist-api
379 DEPLOYER_USER=jenkins
380 APP_TAG=latest
381 NAMESPACE=donal-dev
382 ```
383
e90e9c 384 4. Finally; run the applier and install its dependencies to run the content into the cluster
c58300 385 ```bash
D 386 $ ansible-galaxy install -r requirements.yml --roles-path=roles
387 $ ansible-playbook apply.yml -i inventory/
388 ```
9fc88c 389
e90e9c 390 4. Validate the build and deploy configs have been created in Openshift by checking `<YOUR_NAME> CI-CD builds` for the `BuildConfigs`
A 391
392 4. Check `<YOUR_NAME>-dev` for the deployment ![ocp-app-deployment-overview](../images/exercise2/ocp-app-deployment-overview.jpeg)
393
9fc88c 394 ### Part 4 - Build > Bake > Deploy 
0e648a 395 > _In this exercise; we take what we have working locally and get it working in OpenShift_
9fc88c 396
41217d 397 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 398 * a *build* job is responsible for compiling and packaging our code:
399     1. Checkout from source code (`develop` for `<yourname>-dev` & `master` for `<yourname>-test`)
400     2. Install node dependencies and run a build / package
401     3. Send the package to Nexus
402     4. Archive the workspace to persist the workspace in case of failure
403     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.
404     5. Trigger the `bake` job with the `${BUILD_TAG}` param
405 * a *bake* job should take the package and put it in a Linux Container
406     1. Take an input of the previous jobs `${BUILD_TAG}` ie `${JOB_NAME}.${BUILD_NUMBER}`.
407     2. Checkout the binary from Nexus and unzip it's contents
408     3. Run an oc start-build of the App's BuildConfig and tag it's imagestream with the provided `${BUILD_TAG}`
409     4. Trigger a deploy job using the parameter `${BUILD_TAG}`
410 * a *deploy* job should roll out the changes by updating the image tag in the DC:
411     1. Take an input of the `${BUILD_TAG}`
412     2. Patch / set the DeploymentConfig to the image's `${BUILD_TAG}`
413     3. Rollout the changes
414     4. Verify the deployment
76d54e 415 * We will now go through these steps in detail.
9fc88c 416
41217d 417 #### Part 4a - Build
9fc88c 418
c58300 419 5. 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)
9fc88c 420
579436 421 5. Name this job `dev-todolist-fe-build` and select `Freestyle Job`. All our jobs will take the form of `<ENV>-<APP_NAME>-<JOB_PURPOSE>`. ![freestyle-job](../images/exercise2/freestyle-job.png)
9fc88c 422
579436 423 5. The page that loads is the Job Configuration page. It can be returned to at anytime from Jenkins. To conserve space; we will make sure Jenkins only keeps the last builds artifacts. Tick the `Discard old builds` checkbox and set `Max # of builds to keep with artifacts` to 1 as below ![keep-artifacts](../images/exercise2/keep-artifacts.png)
43f2f2 424
579436 425 5. Our NodeJS build needs to be run on the `jenkins-slave-npm` we created earlier. Specify this in the box labelled `Restrict where this project can be run` ![label-jenkins-slave](../images/exercise2/label-jenkins-slave.png)
c951f7 426
579436 427 5. On the Source Code Management tab, 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 428
579436 429 5. Scroll down to the Build Environment tab and select the `Color ANSI Console Output` checkbox ![ansi](../images/exercise2/ansi.png)
e919d0 430
76d54e 431 5. 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 432 ```bash
D 433 #!/bin/bash
434 set -o xtrace
435 scl enable rh-nodejs8 'npm install'
436 scl enable rh-nodejs8 'npm run build:ci:dev'
437 scl enable rh-nodejs8 'npm run package'
438 scl enable rh-nodejs8 'npm run publish'
439 ```
440 ![build-step](../images/exercise2/build-step.png)
441
579436 442 5. 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 443
579436 444 5. 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 445     * Tick the box `Push Only If Build Succeeds`
D 446     * Add the Tag to push of 
447 ```bash
448 ${JOB_NAME}.${BUILD_NUMBER}
449 ```
450     * Specify the commit message to be
451 ```bash
452 Automated commit by jenkins from ${JOB_NAME}.${BUILD_NUMBER}
453 ```
454 ![git-publisher](../images/exercise2/git-publisher.png)
455
579436 456 5. Finally; add the trigger for the next job in the pipeline. Add another post-build action from the dropdown called `Trigger parameterized build on other projects`. 
e919d0 457     * Set the project to build to be `dev-todolist-fe-bake` 
D 458     * Set the condition to be `Stable or unstable but not failed`. 
459     * Click Add Parameters dropdown and select Predefined parameters. 
460     * In the box, insert our BUILD_TAG as follows
461 ```bash
462 BUILD_TAG=${JOB_NAME}.${BUILD_NUMBER}
463 ```
464 ![param-trigger](../images/exercise2/param-trigger.png)
465 <p class="tip">
3b5f91 466     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 467 </p>
D 468
579436 469 5. Hit `save` which will take you to the job overview page - and that's it; our *build* phase is complete!
e919d0 470
41217d 471 #### Part 4b - Bake
D 472
579436 473 5. 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 474
579436 475 5. This job is will take in the BUILD_TAG from the previous one so check the `This project is parameterized` box on the General tab.
3b5f91 476     * Add string parameter type
D 477     * set the name to `BUILD_TAG`. This will be available to the job as an Enviroment Variable.
478     * You can set `dev-todolist-fe-build.` as the default value for ease when triggering manually.
479     * The description is not required but a handy one for reference would be `${JOB_NAME}.${BUILD_NUMBER} of previous build eg dev-todolist-fe-build.1232`
480 ![param-trigger-bake](../images/exercise2/param-trigger-bake.png)
c951f7 481
579436 482 5. This time set the `Restrict where this project can be run` label to `master`.
3b5f91 483 <p class="tip">
D 484     This 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.
485 </p>
486
579436 487 5. 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 488
579436 489 5. Scroll down to the Build Environment tab and select the `Color ANSI Console Output` checkbox ![delete-ansi](../images/exercise2/delete-ansi.png)
3b5f91 490
579436 491 5. 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:
3b5f91 492 ```bash
D 493 #!/bin/bash
d00794 494 curl -v -f http://admin:admin123@${NEXUS_SERVICE_HOST}:${NEXUS_SERVICE_PORT}/repository/zip/com/redhat/todolist/${BUILD_TAG}/package-contents.zip -o package-contents.zip
3b5f91 495 unzip package-contents.zip
D 496 oc project <YOUR_NAME>-ci-cd # probs not needed
497 NAME=todolist-fe
498 oc patch bc ${NAME} -p "spec:
499    output:
500      to:
501        kind: ImageStreamTag
502        name: '${NAME}:${BUILD_TAG}'"
503 oc start-build ${NAME} --from-dir=package-contents/ --follow
504 ```
505 ![bake-step](../images/exercise2/bake-step.png)
506
579436 507 5. 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 508     * Set the project to build to be `dev-todolist-fe-deploy`
D 509     * Set the condition to be `Stable`.
510     * Click Add Parameters dropdown and select Current build parameters. This will pass the ${BUILD_TAG} to the downstream job which we will create next.
511     * In the box, insert our BUILD_TAG as follows
512 ![downstream-trigger-deploy](../images/exercise2/downstream-trigger-deploy.png)
513
579436 514 5. Hit save! That's our *bake* phase done! Finally; on to our *deploy*
3b5f91 515
41217d 516 #### Part 4c - Deploy
D 517
579436 518 5. 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 and but scroll to the bottom and Copy from `dev-todolist-fe-bake`.
D 519 ![copy-from](../images/exercise2/copy-from.png)
520
521 5. The only two differences between the 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 `dev` project. Then update the DeploymentConfig to use the Jenkins Tag which kicked the process off. Once successful; the changes are rolled out
522 ```bash
523 #!/bin/bash
524 set -o xtrace
525 # VARS
526 PIPELINES_NAMESPACE=donal-ci-cd
527 NAMESPACE=donal-dev
528 NAME=todolist-fe
529 oc project ${NAMESPACE}
530 oc tag ${PIPELINES_NAMESPACE}/${NAME}:${BUILD_TAG} ${NAMESPACE}/${NAME}:${BUILD_TAG}
531 oc set env dc ${NAME} NODE_ENV=dev
532 oc set image dc/${NAME} ${NAME}=docker-registry.default.svc:5000/${NAMESPACE}/${NAME}:${BUILD_TAG}
533 oc rollout latest dc/${NAME}
534 ```
535 ![deploy-step](../images/exercise2/deploy-step.png)
536
537 5. Secondly, add another build step called `Verify OpenShift Deployment` include the following
538     * Set the Project to your `<YOUR_NAME>-dev`
539     * Set the DeploymentConfig to your app's name `todolist-fe`
540     * Set the replica count to `1`
541 ![verify-deployment](../images/exercise2/verify-deployment.png)
542
543 5. Finally; 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!
544
41217d 545 #### Part 4d - Pipeline
D 546
547 5. With our Jenkins setup in place; now move to our `todolist-fe` app. Open in it your favourite editor and navigate to `src/config/dev.js`. Update `<YOUR_NAME>` accordingly. For example:
548 ![fe-dev-config](../images/exercise2/fe-dev-config.png)
549
550 5. Repeat this for `src/config/test.js` file. Once done; commit your chanages and push them to GitLab
551 ```bash
552 $ git add .
553 $ git commit -m "ADD config for api"
554 $ git push
555 ```
556
557 5. Back on Jenkins; 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. Give the new view a sensible name like `dev-todolist-fe-pipeline`
579436 558 ![add-view](../images/exercise2/add-view.png). 
D 559
560 5. Set the Pipeline Flow's Inital Job to `dev-todolist-fe-build` and save.
561 ![pipeline-flow](../images/exercise2/pipeline-flow.png)
562
d00794 563 5. You should now see the pipeline view. Run the pipeline by hitting build (you can move onto the next part while it is running as it may take some time). 
579436 564 ![dev-pipeline-view](../images/exercise2/dev-pipeline-view.png)
D 565
566 ### Part 5 - Backend Pipeline
d00794 567 > In this exercise we will use the Jobs created for the `todolist-fe` as a template to create a pipeline for the `todolist-api` app
579436 568
d00794 569 6. On Jenkins home; create a new job for our backend build called `dev-todolist-api-build`. Use the Copy from section to copy all the configuration from the `dev-todolist-fe-build`.
D 570 ![copy-fe-build](../images/exercise2/copy-fe-build.png)
579436 571
d00794 572 6. When this has loaded; find and replace both occurrences `-fe` with `-api` within the Job's configuration. Places to make sure you check are: 
D 573     * The GitLab project URL
574     * Projects to build on the Post Build Action
579436 575
5eb156 576 6. On the Build tab; remove the `:dev` from the `npm run build:ci:dev` so it just reads.
d00794 577 ```bash
D 578 scl enable rh-nodejs8 'npm run build:ci'
579 ```
5eb156 580  The rest of the instructions can be left as they are.
579436 581
d00794 582 6. Save the configuration for `dev-todolist-api-build`
579436 583
d00794 584 6. On Jenkins home; create a new job for our backend build called `dev-todolist-api-bake`. Use the Copy from section to copy all the configuration from the `dev-todolist-fe-bake` as you've just done.
D 585
586 6. When this has loaded; find and replace the occurrences `-fe` with `-api` within the Job's configuration. Places to make sure you check are:
587     * The BUILD_TAG default value and description
588     * NAME in the execute shell step
589     * Projects to build on the Post Build Action
590
591 6. Save the configuration for `dev-todolist-api-build`
592
593 6. On Jenkins home; create a new job for our backend build called `dev-todolist-api-deploy`. Use the Copy from section to copy all the configuration from the `dev-todolist-fe-deploy` as you've just done.
594
595 6. When this has loaded; find and replace the occurrences `-fe` with `-api` within the Job's configuration. Places to make sure you check are:
596     * The BUILD_TAG default value and description
597     * NAME in the execute shell step
598     * The name of the DeploymentConfig to validate in the Verify OpenShift Deployment
599
600 6. Save the configuration for `dev-todolist-api-build` and that's it for wiring together our `todolist-api` pipeline.
601  
5eb156 602 6. Before we kick off the build; check our front end app and see if it has deployed successfully. Check the deployment in OpenShift and load the url. If it has been a success we should see our dummyData. This is because there is not backend deployed.
d00794 603 ![no-backend-app](../images/exercise2/no-backend-app.png)
D 604
605 6. Run the `dev-todolist-api-build` to trigger the backend pipeline. When complete we should see the sample data has changed
606 ![with-backend-app](../images/exercise2/with-backend-app.png)
c951f7 607
43f2f2 608 _____
D 609
c951f7 610 ## Extension Tasks
cf415b 611 > _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 612
e919d0 613 - Git Tasks
D 614     * Add a GitHub Webhook to trigger your build on each commit
d00794 615 - Pipeline Tasks
D 616     * Add pipeline for <master> branch for each project.
617     * Use `test-` instead of `dev-` across all config and names
c58300 618     * Do the `.openshift-applier` steps as part of the pipeline.
e919d0 619 - Promote build
3b5f91 620     * Create a _promote-to-uat_ phase after the <master> branch deploy
cf415b 621     * Create a `uat` env using the OpenShift Applier as seen before
e919d0 622     * Tag and promote the image without rebuilding after the `test-**-deploy`
c58300 623 - MongoDB tasks
e919d0 624     * Add MongoDB Stateful set for the UAT environment (or test)
D 625     * Inject MongoDB config into the NodeJS app using config map & secrets.
cf415b 626     * Improve the security of the DB by making the user /passwords randomly generated
e919d0 627 - Setup Nexus as an `npm` mirror registry and use it in the builds to speed up the build time
c951f7 628
D 629 ## Additional Reading
43f2f2 630 > List of links or other reading that might be of use / reference for the exercise
D 631
7383de 632  -  What's in a package.json?
D 633
43f2f2 634 ## Slide links
399baa 635 > link back to the deck for the supporting material