donal
2018-04-09 d98f409f02a0a34a006995ff98a51b6c936ee660
commit | author | age
3772d9 1 # Attack of the Pipelines
0f4d08 2
3772d9 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
5a16fd 7 _____
0f4d08 8
D 9 ## Learning Outcomes
3772d9 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
0f4d08 15
D 16 ## Tools and Frameworks
3772d9 17 > The following tools are used throughout this exercise. Familiarity with them is not required but knowing what they are may help!
0f4d08 18
D 19 1. [Jenkins](https://jenkins.io/) - OpenSource build automation server; highly customisable through plugins
3772d9 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.
0f4d08 23
5a16fd 24 ## Big Picture
3772d9 25 > From the previous exercise; we created some supporting tooling needed by our app/
5a16fd 26
D 27 _____
0f4d08 28
D 29 ## 10,000 Ft View
3772d9 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_
5a16fd 31
3772d9 32 2. Import the projects into your gitlab instance. See README of each for build instructions
5a16fd 33
3772d9 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:
c3a934 37     * a `Build` job is responsible for compiling and packaging our code:
3772d9 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
c3a934 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
3772d9 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
c3a934 47         3. Run an oc start-build of the App's BuildConfig and tag it's imagestream with the provided `${BUILD_TAG}`
3772d9 48         4. Trigger a deploy job using the parameter `${BUILD_TAG}`
c3a934 49     * a `deploy` job should roll out the changes by updating the image tag in the DC:
3772d9 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.
0f4d08 58
D 59 ## Step by Step Instructions
f2b1b4 60 > This is a fairly structured guide with references to exact filenames and sections of text to be added.
0f4d08 61
f2b1b4 62 ### Part 1 - Explore the Sample App
D 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_
5a16fd 64
f2b1b4 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
68 $ git checkout develop
0f4d08 69 ```
f2b1b4 70
D 71 2. 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)
72
73 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
74 ```bash
75 $ git remote remove origin
76 $ git remote add origin <YOUR_GIT_LAB_PROJECT>
77 $ git push -u origin --all
0f4d08 78 ```
f2b1b4 79
D 80 2. To get the app running locally; first check you've got node and npm installed
81 ```bash
82 $ node -v
83 $ npm -v
84 ```
85 <p class="tip" > 
86 NOTE - If you are missing these dependencies; install them with ease using the [Node Version Manager](https://github.com/creationix/nvm)
87 </p>
88 ![node-version](../images/exercise2/node-version.png)
89
90 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
91 ```bash
92 $ npm install
93 ```
94
19df4d 95 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
f2b1b4 96  ![npm-scripts](../images/exercise2/npm-scripts.png)
D 97 ```bash
98 npm run serve
99 ```
100
101 2. This will take sometime to execute; but once done it should open the browser for you displaying the `todolist-fe` app.
102  ![todo-list-app](../images/exercise2/todo-list-app.png)
103     * The server hosting it live reloads; so as you make changes to your code; the app will live update
104     * The Data you see in the screen is dummy / stubbed data. This is served up when there is no backend connection found
105
3691a1 106 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
f2b1b4 107
D 108 3. The structure of the `todolist-fe` is as follows.
109 ```bash
110 todolist-fe
111 ├── jest.config.js
112 ├── jsconfig.json
113 ├── nightwatch.config.js
114 ├── node_modules
115 ├── package.json
116 ├── public
117 │   ├── favicon.ico
118 │   ├── img
119 │   ├── index.html
120 │   └── manifest.json
121 ├── src
122 │   ├── App.vue
123 │   ├── assets
124 │   ├── components
125 │   ├── config
126 │   ├── main.js
127 │   ├── registerServiceWorker.js
128 │   ├── router.js
129 │   ├── scss
130 │   ├── services
131 │   ├── store
132 │   └── views
133 ├── tests
134 │   ├── e2e
135 │   └── unit
136 └── vue.config.js
137 ```
138 where the following are the important things:
139     * `./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.
140     * `./node_modules` is where the dependencies are stored
141     * `./test` contains our end-to-end tests and unit tests. More covered on these in later labs.
142     * `./src/components` contains small, lightweight reusable components for our app. These include the `NewTodo` component which encapsulates the styling, logic and data for adding a new todo to our list
143     * `./src/store` is the `vuex` files for managing application state and backend connectivity
144     * `./src/views` is the view containers; which are responsible for loading components and managing their interactions.
145     * the `./src/router.js` controls routing logic. In our case the app only has one real endpoint.
146     * `./src/scss` contains custom  SCSS used in the application.
147     * `./*.js` is mostly config files for running and managing the app and the tests
0f4d08 148
3691a1 149 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 150 ```bash
151 $ git clone https://github.com/springdo/todolist-api.git
152 $ git checkout develop
153 ```
154
155 2. Create a new project (internal) in GitLab called `todolist-api` to host your clone of the project and copy it's remote address.
156
157 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
158 ```bash
159 $ git remote remove origin
160 $ git remote add origin <YOUR_GIT_LAB_PROJECT>
161 $ git push -u origin --all
162 ```
163
164 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
165 ```bash
166 $ npm i
167 ```
168
169 2. While the dependencies are being installed; explore the project structure.
170 ```bash
171 todolist-api
172 ├── Dockerfile
173 ├── Gruntfile.js
174 ├── README.md
175 ├── node_modules
176 ├── package-lock.json
177 ├── package.json
178 ├── server
179 │   ├── api
180 │   │   └── todo
181 │   ├── app.js
182 │   ├── components
183 │   │   └── errors
184 │   ├── config
185 │   │   ├── environment
186 │   │   ├── express.js
187 │   │   ├── local.env.sample.js
188 │   │   └── seed.js
189 │   ├── mocks
190 │   │   ├── mock-routes-config.json
191 │   │   ├── mock-routes.js
192 │   │   └── mock-routes.spec.js
193 │   ├── routes.js
194 │   └── views
195 │       └── 404.html
196 └── tasks
197     └── perf-test.js
198 ```
199 where the following are the important things:
200     * `./server` is the main collection of files needed by the app. The entrypoint is the `app.js`
201     * `./node_modules` is where the dependencies are stored
202     * `./server/api` is where the api's controller, data model & unit test are stored. 
203     * `./server/mocks` is a mock server used for when there is no DB access    
204     * `./server/config` stores our Express JS config, header information and other middlewear.
205     * `./server/config/environment` stores enviromnent specific config; such as connectivity to backend services like the MongoDB.
206     * `./tasks` is a collection of additional `Grunt` tasks which will be used in later labs
207     * `package.json` contains the dependency list and a lot of very helpful scripts for managing the app lifecycle
208
209 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.
210 ```json
211   "scripts": {
212     "start": "node server/app.js",
213     "dev": "./node_modules/.bin/grunt serve",
214     "jshint": "./node_modules/.bin/grunt jshint",
215     "jshint:ci": "./node_modules/.bin/grunt jshint:ci_server",
216     "clean": "rm -rf reports package-contents*",
217     "build": "mkdir -p package-contents && cp -vr server Dockerfile package.json package-contents",
218     "package": "zip -r package-contents.zip package-contents",
219     "test": "node_modules/.bin/nyc node_modules/.bin/mocha server/**/*.spec.js --exit",
220     "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",
221     "mongo" : "docker run -i -d --name mongo-local -p 27017:27017 mongo",
222     "mongo:drop" : "npm run mongo:stop && docker rm mongo-local",
223     "mongo:stop" : "docker stop mongo-local",
224     "mongo:start" : "docker start mongo-local"
225   },
226 ```
227
228 2. To run the application; start a new instance of the MongoDB by running. 
229 ```bash
230 $ npm run mongo
231 ```
232 <p class="tip">
233 `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
234 </p>
235
236 2. Fire up the `todolist-api` by running.
237 ```bash
238 $ npm run start
239 ```
240 ![node-app-started](../images/exercise2/node-app-started.png)
241
242 2. Check things are responding correctly by running and checking the response. It contains some seeded data (stored in `server/config/seed.js`)
243 ```bash
244 $ curl localhost:9000/api/todos
245 ```
246 ```json
247 [{
248     "_id": "5ac8ff1fdfafb02138698948",
249     "title": "Learn some stuff about MongoDB",
250     "completed": false,
251     "__v": 0
252   },
253   {
254     "_id": "5ac8ff1fdfafb02138698949",
255     "title": "Play with NodeJS",
256     "completed": true,
257     "__v": 0
258 }]
259 ```
260
261 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 
262 ![fullstack-app](../images/exercise2/fullstack-app.png)
263
264
273e4c 265 ### Part 2 - Create a NodeJS Build slave
374f63 266 > _In this exercise; we will create a build configuration to generate a slave for Jenkins to use in it's builds_
273e4c 267
374f63 268 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`.
273e4c 269 ```bash
D 270 $ git checkout exercise2/jenkins-slave docker/ templates/ params/
271 ```
272
273 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`.
274 ```bash
275 SOURCE_REPOSITORY_URL=<YOUR_ENABLEMENT_GIT_REPO>
276 SOURCE_CONTEXT_DIR=docker/jenkins-slave-npm
277 NAME=npm-jenkins-slave
278 ```
279
280 3. Create an item in the ansible variables file under the `ci-cd-builds` object to run the template with. Don't forget to substitute `<YOUR_NAME>`
281 ```yaml
282   - name: "jenkins-npm-slave"
283     namespace: "<YOUR_NAME>-ci-cd"
284     template: "{{ inventory_dir }}/../templates/jenkins-slave-generic-template.yml"
285     params: "{{ inventory_dir }}/../params/jenkins-slave-npm"
286     tags:
287     - jenkins-slave
288 ```
289 ![jenkins-slave-ansible](../images/exercise2/jenkins-slave-ansible.png)
290
291 3. Run the OpenShift Applier to trigger a build of this jenkins slave image.
292 ```bash
293 $ ansible-playbook roles/openshift-applier/playbooks/openshift-cluster-seed.yml \
294      -i inventory/ \
295      -e "filter_tags=jenkins-slave"
296 ```
297
298 3. Verify the build executed successfully by logging into the cluster and checking the `builds` tab of the `<YOUR_NAME>-ci-cd` project.
299 ![jenkins-slave-npm-build](../images/exercise2/jenkins-slave-npm-build.png)
300
301 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
302 <p class="tip">
303 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.
304 </p>
305
306 ### Part 3 - Add configs to cluster 
307 > _In this exercise; we will use the OpenShift Applier to drive the creation of cluster content required by the app such and MongoDB and the Apps Build / Deploy Config_
308
d98f40 309 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 your should seem a familiar looking directory structure for an ansible playbook. 
D 310 ```
311 .openshift-applier
312 ├── README.md
313 ├── apply.yml
314 ├── inventory
315 │   ├── group_vars
316 │   │   └── all.yml
317 │   └── hosts
318 ├── params
319 │   ├── build
320 │   ├── dev
321 │   └── test
322 ├── requirements.yml
323 └── templates
324     ├── todolist-api-build.yml
325     └── todolist-api-deploy.yml
326 ```
327 where the following
328     * the `apply.yml` file is the entrypoint.
329     * the `inventory` contains the objects to populate the cluster with.
330     * the `params` contains the variables we'll apply to the `templates`
331     * the `templates` required by the app. These include the Build, Deploy configs as well as the services, health checks and other app definitions.
332
333 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.
334 ```yaml
335     ci_cd_namespace: donal-ci-cd
336     dev_namespace: donal-dev
337     test_namespace: donal-test
338 ```
339
340 4. In the `params` folder update the `dev` and `test` files with the correct `<YOUR_NAME>` as you've done above.
341 ```bash
342 PIPELINES_NAMESPACE=donal-ci-cd
343 NAME=todolist-api
344 DEPLOYER_USER=jenkins
345 APP_TAG=latest
346 NAMESPACE=donal-dev
347 ```
348
349 4. With those changes in place we can now run the playbook. First install the `openshift-applier` dependency and then run the playbook. This will populate the cluster with all the config needed for the front end app.
350 ```bash
351 $ ansible-galaxy install -r requirements.yml --roles-path=roles
352 $ ansible-playbook apply.yml -i inventory/
353 ```
354
355
273e4c 356
D 357 ### Part 4 - Build > Bake > Deploy 
b15f06 358 > _In this exercise; we take what we have working locally and get it working in OpenShift_
273e4c 359
40ab32 360 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 361 * a *build* job is responsible for compiling and packaging our code:
362     1. Checkout from source code (`develop` for `<yourname>-dev` & `master` for `<yourname>-test`)
363     2. Install node dependencies and run a build / package
364     3. Send the package to Nexus
365     4. Archive the workspace to persist the workspace in case of failure
366     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.
367     5. Trigger the `bake` job with the `${BUILD_TAG}` param
368 * a *bake* job should take the package and put it in a Linux Container
369     1. Take an input of the previous jobs `${BUILD_TAG}` ie `${JOB_NAME}.${BUILD_NUMBER}`.
370     2. Checkout the binary from Nexus and unzip it's contents
371     3. Run an oc start-build of the App's BuildConfig and tag it's imagestream with the provided `${BUILD_TAG}`
372     4. Trigger a deploy job using the parameter `${BUILD_TAG}`
373 * a *deploy* job should roll out the changes by updating the image tag in the DC:
374     1. Take an input of the `${BUILD_TAG}`
375     2. Patch / set the DeploymentConfig to the image's `${BUILD_TAG}`
376     3. Rollout the changes
377     4. Verify the deployment
19df4d 378 * We will now go through these steps in detail.
273e4c 379
40ab32 380 #### Part 4a - Build
273e4c 381
dd060d 382 5. With the BuildConfig and DeployConfig in place from previous steps; Log into Jenkins and create a `New Item` which is jenkins speak for a new job configuration. ![new-item](../images/exercise2/new-item.png)
273e4c 383
dd060d 384 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)
273e4c 385
dd060d 386 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)
5a16fd 387
dd060d 388 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)
0f4d08 389
dd060d 390 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)
0f4d08 391
dd060d 392 5. Scroll down to the Build Environment tab and select the `Color ANSI Console Output` checkbox ![ansi](../images/exercise2/ansi.png)
c3a934 393
19df4d 394 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:
c3a934 395 ```bash
D 396 #!/bin/bash
397 set -o xtrace
398 scl enable rh-nodejs8 'npm install'
399 scl enable rh-nodejs8 'npm run build:ci:dev'
400 scl enable rh-nodejs8 'npm run package'
401 scl enable rh-nodejs8 'npm run publish'
402 ```
403 ![build-step](../images/exercise2/build-step.png)
404
dd060d 405 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)
c3a934 406
dd060d 407 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.
c3a934 408     * Tick the box `Push Only If Build Succeeds`
D 409     * Add the Tag to push of 
410 ```bash
411 ${JOB_NAME}.${BUILD_NUMBER}
412 ```
413     * Specify the commit message to be
414 ```bash
415 Automated commit by jenkins from ${JOB_NAME}.${BUILD_NUMBER}
416 ```
417 ![git-publisher](../images/exercise2/git-publisher.png)
418
dd060d 419 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`. 
c3a934 420     * Set the project to build to be `dev-todolist-fe-bake` 
D 421     * Set the condition to be `Stable or unstable but not failed`. 
422     * Click Add Parameters dropdown and select Predefined parameters. 
423     * In the box, insert our BUILD_TAG as follows
424 ```bash
425 BUILD_TAG=${JOB_NAME}.${BUILD_NUMBER}
426 ```
427 ![param-trigger](../images/exercise2/param-trigger.png)
428 <p class="tip">
eaf747 429     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.
c3a934 430 </p>
D 431
dd060d 432 5. Hit `save` which will take you to the job overview page - and that's it; our *build* phase is complete!
c3a934 433
40ab32 434 #### Part 4b - Bake
D 435
dd060d 436 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`.
c3a934 437
dd060d 438 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.
eaf747 439     * Add string parameter type
D 440     * set the name to `BUILD_TAG`. This will be available to the job as an Enviroment Variable.
441     * You can set `dev-todolist-fe-build.` as the default value for ease when triggering manually.
442     * 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`
443 ![param-trigger-bake](../images/exercise2/param-trigger-bake.png)
0f4d08 444
dd060d 445 5. This time set the `Restrict where this project can be run` label to `master`.
eaf747 446 <p class="tip">
D 447     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.
448 </p>
449
dd060d 450 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`
eaf747 451
dd060d 452 5. Scroll down to the Build Environment tab and select the `Color ANSI Console Output` checkbox ![delete-ansi](../images/exercise2/delete-ansi.png)
eaf747 453
dd060d 454 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:
eaf747 455 ```bash
D 456 #!/bin/bash
436657 457 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
eaf747 458 unzip package-contents.zip
D 459 oc project <YOUR_NAME>-ci-cd # probs not needed
460 NAME=todolist-fe
461 oc patch bc ${NAME} -p "spec:
462    output:
463      to:
464        kind: ImageStreamTag
465        name: '${NAME}:${BUILD_TAG}'"
466 oc start-build ${NAME} --from-dir=package-contents/ --follow
467 ```
468 ![bake-step](../images/exercise2/bake-step.png)
469
dd060d 470 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`.
eaf747 471     * Set the project to build to be `dev-todolist-fe-deploy`
D 472     * Set the condition to be `Stable`.
473     * Click Add Parameters dropdown and select Current build parameters. This will pass the ${BUILD_TAG} to the downstream job which we will create next.
474     * In the box, insert our BUILD_TAG as follows
475 ![downstream-trigger-deploy](../images/exercise2/downstream-trigger-deploy.png)
476
dd060d 477 5. Hit save! That's our *bake* phase done! Finally; on to our *deploy*
eaf747 478
40ab32 479 #### Part 4c - Deploy
D 480
dd060d 481 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 482 ![copy-from](../images/exercise2/copy-from.png)
483
484 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
485 ```bash
486 #!/bin/bash
487 set -o xtrace
488 # VARS
489 PIPELINES_NAMESPACE=donal-ci-cd
490 NAMESPACE=donal-dev
491 NAME=todolist-fe
492 oc project ${NAMESPACE}
493 oc tag ${PIPELINES_NAMESPACE}/${NAME}:${BUILD_TAG} ${NAMESPACE}/${NAME}:${BUILD_TAG}
494 oc set env dc ${NAME} NODE_ENV=dev
495 oc set image dc/${NAME} ${NAME}=docker-registry.default.svc:5000/${NAMESPACE}/${NAME}:${BUILD_TAG}
496 oc rollout latest dc/${NAME}
497 ```
498 ![deploy-step](../images/exercise2/deploy-step.png)
499
500 5. Secondly, add another build step called `Verify OpenShift Deployment` include the following
501     * Set the Project to your `<YOUR_NAME>-dev`
502     * Set the DeploymentConfig to your app's name `todolist-fe`
503     * Set the replica count to `1`
504 ![verify-deployment](../images/exercise2/verify-deployment.png)
505
506 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!
507
40ab32 508 #### Part 4d - Pipeline
D 509
510 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:
511 ![fe-dev-config](../images/exercise2/fe-dev-config.png)
512
513 5. Repeat this for `src/config/test.js` file. Once done; commit your chanages and push them to GitLab
514 ```bash
515 $ git add .
516 $ git commit -m "ADD config for api"
517 $ git push
518 ```
519
520 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`
dd060d 521 ![add-view](../images/exercise2/add-view.png). 
D 522
523 5. Set the Pipeline Flow's Inital Job to `dev-todolist-fe-build` and save.
524 ![pipeline-flow](../images/exercise2/pipeline-flow.png)
525
436657 526 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). 
dd060d 527 ![dev-pipeline-view](../images/exercise2/dev-pipeline-view.png)
D 528
529 ### Part 5 - Backend Pipeline
436657 530 > 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
dd060d 531
436657 532 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 533 ![copy-fe-build](../images/exercise2/copy-fe-build.png)
dd060d 534
436657 535 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 536     * The GitLab project URL
537     * Projects to build on the Post Build Action
dd060d 538
15fb60 539 6. On the Build tab; remove the `:dev` from the `npm run build:ci:dev` so it just reads.
436657 540 ```bash
D 541 scl enable rh-nodejs8 'npm run build:ci'
542 ```
15fb60 543  The rest of the instructions can be left as they are.
dd060d 544
436657 545 6. Save the configuration for `dev-todolist-api-build`
dd060d 546
436657 547 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 548
549 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:
550     * The BUILD_TAG default value and description
551     * NAME in the execute shell step
552     * Projects to build on the Post Build Action
553
554 6. Save the configuration for `dev-todolist-api-build`
555
556 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.
557
558 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:
559     * The BUILD_TAG default value and description
560     * NAME in the execute shell step
561     * The name of the DeploymentConfig to validate in the Verify OpenShift Deployment
562
563 6. Save the configuration for `dev-todolist-api-build` and that's it for wiring together our `todolist-api` pipeline.
564  
15fb60 565 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.
436657 566 ![no-backend-app](../images/exercise2/no-backend-app.png)
D 567
568 6. Run the `dev-todolist-api-build` to trigger the backend pipeline. When complete we should see the sample data has changed
569 ![with-backend-app](../images/exercise2/with-backend-app.png)
0f4d08 570
5a16fd 571 _____
D 572
0f4d08 573 ## Extension Tasks
a4f42c 574 > _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._
0f4d08 575
c3a934 576 - Git Tasks
D 577     * Add a GitHub Webhook to trigger your build on each commit
436657 578 - Pipeline Tasks
D 579     * Add pipeline for <master> branch for each project.
580     * Use `test-` instead of `dev-` across all config and names
c3a934 581 - Promote build
eaf747 582     * Create a _promote-to-uat_ phase after the <master> branch deploy
a4f42c 583     * Create a `uat` env using the OpenShift Applier as seen before
c3a934 584     * Tag and promote the image without rebuilding after the `test-**-deploy`
D 585 - MongoDB tasks 
586     * Add MongoDB Stateful set for the UAT environment (or test)
587     * Inject MongoDB config into the NodeJS app using config map & secrets.
a4f42c 588     * Improve the security of the DB by making the user /passwords randomly generated
c3a934 589 - Setup Nexus as an `npm` mirror registry and use it in the builds to speed up the build time
0f4d08 590
D 591 ## Additional Reading
5a16fd 592 > List of links or other reading that might be of use / reference for the exercise
D 593
f2b1b4 594  -  What's in a package.json?
D 595
5a16fd 596 ## Slide links
374f63 597 > link back to the deck for the supporting material