acammies
2018-04-18 256ca9a7f9cc48fd3c0ad82a88d0127860a5798e
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
14268c 62 ### Part 1 - Explore the Todo List App
f2b1b4 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
256ca9 65 #### Part 1a Todolist-fe
A 66
f2b1b4 67 2. Git clone the `todolist-fe` project to somewhere sensible and checkout the `develop` branch.
D 68 ```bash
69 $ git clone https://github.com/springdo/todolist-fe.git
3050f4 70 $ cd todolist-fe
f2b1b4 71 $ git checkout develop
256ca9 72 ```
A 73 Followed by;
74 ```
75 $ for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master`; do
76    git branch --track ${branch#remotes/origin/} $branch
77 done
0f4d08 78 ```
f2b1b4 79
486224 80 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)
f2b1b4 81
D 82 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
83 ```bash
486224 84 $ git remote set-url origin <YOUR_GIT_LAB_PROJECT>
A 85 # verify the origin has been updated
86 $ git remote -v
f2b1b4 87 $ git push -u origin --all
0f4d08 88 ```
f2b1b4 89
D 90 2. To get the app running locally; first check you've got node and npm installed
91 ```bash
92 $ node -v
93 $ npm -v
94 ```
95 <p class="tip" > 
96 NOTE - If you are missing these dependencies; install them with ease using the [Node Version Manager](https://github.com/creationix/nvm)
97 </p>
98 ![node-version](../images/exercise2/node-version.png)
99
100 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
101 ```bash
102 $ npm install
103 ```
104
19df4d 105 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 106  ![npm-scripts](../images/exercise2/npm-scripts.png)
D 107 ```bash
108 npm run serve
109 ```
110
486224 111 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.
f2b1b4 112  ![todo-list-app](../images/exercise2/todo-list-app.png)
486224 113     * Click 'Todo' at the top of the home page to get to the above page.
f2b1b4 114     * The server hosting it live reloads; so as you make changes to your code; the app will live update
D 115     * The Data you see in the screen is dummy / stubbed data. This is served up when there is no backend connection found
116
3691a1 117 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 118
D 119 3. The structure of the `todolist-fe` is as follows.
120 ```bash
121 todolist-fe
122 ├── jest.config.js
123 ├── jsconfig.json
124 ├── nightwatch.config.js
125 ├── node_modules
126 ├── package.json
127 ├── public
128 │   ├── favicon.ico
129 │   ├── img
130 │   ├── index.html
131 │   └── manifest.json
132 ├── src
133 │   ├── App.vue
134 │   ├── assets
135 │   ├── components
486224 136 │   │   └── *
f2b1b4 137 │   ├── config
D 138 │   ├── main.js
139 │   ├── registerServiceWorker.js
140 │   ├── router.js
141 │   ├── scss
142 │   ├── services
143 │   ├── store
486224 144 │   │   └── *
f2b1b4 145 │   └── views
486224 146 │       └── *
f2b1b4 147 ├── tests
D 148 │   ├── e2e
149 │   └── unit
150 └── vue.config.js
151 ```
152 where the following are the important things:
153     * `./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.
154     * `./node_modules` is where the dependencies are stored
155     * `./test` contains our end-to-end tests and unit tests. More covered on these in later labs.
486224 156     * `./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
f2b1b4 157     * `./src/store` is the `vuex` files for managing application state and backend connectivity
D 158     * `./src/views` is the view containers; which are responsible for loading components and managing their interactions.
159     * the `./src/router.js` controls routing logic. In our case the app only has one real endpoint.
160     * `./src/scss` contains custom  SCSS used in the application.
161     * `./*.js` is mostly config files for running and managing the app and the tests
0f4d08 162
256ca9 163 2. 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.
A 164 ```bash
165 export NEXUS_SERVICE_HOST=nexus-<YOUR_NAME>-ci-cd.apps.somedomain.com
166 export NEXUS_SERVICE_PORT=80
167 npm run prepare-nexus
168 ```
169 <p class="tip">
170 NOTE - This step in a residency would be automated by a more complex nexus deployment in the ci-cd project
171 </p>
172
173 #### Part 1b Todolist-api
f9e311 174
3691a1 175 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 176 ```bash
177 $ git clone https://github.com/springdo/todolist-api.git
486224 178 $ git cd todolist-api
3691a1 179 $ git checkout develop
256ca9 180 ```
A 181 Followed by;
182 ```
183 $ for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master`; do
184    git branch --track ${branch#remotes/origin/} $branch
185 done
3691a1 186 ```
D 187
188 2. Create a new project (internal) in GitLab called `todolist-api` to host your clone of the project and copy it's remote address.
189
190 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
191 ```bash
486224 192 $ git remote set-url origin <YOUR_GIT_LAB_PROJECT>
3691a1 193 $ git push -u origin --all
D 194 ```
195
196 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
197 ```bash
486224 198 # npm i === npm install
3691a1 199 $ npm i
D 200 ```
201
202 2. While the dependencies are being installed; explore the project structure.
203 ```bash
204 todolist-api
205 ├── Dockerfile
206 ├── Gruntfile.js
207 ├── README.md
208 ├── node_modules
209 ├── package-lock.json
210 ├── package.json
211 ├── server
212 │   ├── api
213 │   │   └── todo
214 │   ├── app.js
215 │   ├── components
216 │   │   └── errors
217 │   ├── config
218 │   │   ├── environment
219 │   │   ├── express.js
220 │   │   ├── local.env.sample.js
221 │   │   └── seed.js
222 │   ├── mocks
223 │   │   ├── mock-routes-config.json
224 │   │   ├── mock-routes.js
225 │   │   └── mock-routes.spec.js
226 │   ├── routes.js
227 │   └── views
228 │       └── 404.html
229 └── tasks
230     └── perf-test.js
231 ```
232 where the following are the important things:
233     * `./server` is the main collection of files needed by the app. The entrypoint is the `app.js`
234     * `./node_modules` is where the dependencies are stored
235     * `./server/api` is where the api's controller, data model & unit test are stored. 
236     * `./server/mocks` is a mock server used for when there is no DB access    
486224 237     * `./server/config` stores our Express JS config, header information and other middleware.
A 238     * `./server/config/environment` stores enviromnent specific config; such as connectivity to backend services like MongoDB.
3691a1 239     * `./tasks` is a collection of additional `Grunt` tasks which will be used in later labs
486224 240     * `Grunt` is a taskrunner for use with Node.JS projects
3691a1 241     * `package.json` contains the dependency list and a lot of very helpful scripts for managing the app lifecycle
D 242
243 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.
244 ```json
245   "scripts": {
246     "start": "node server/app.js",
247     "dev": "./node_modules/.bin/grunt serve",
248     "jshint": "./node_modules/.bin/grunt jshint",
249     "jshint:ci": "./node_modules/.bin/grunt jshint:ci_server",
250     "clean": "rm -rf reports package-contents*",
251     "build": "mkdir -p package-contents && cp -vr server Dockerfile package.json package-contents",
252     "package": "zip -r package-contents.zip package-contents",
253     "test": "node_modules/.bin/nyc node_modules/.bin/mocha server/**/*.spec.js --exit",
254     "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",
255     "mongo" : "docker run -i -d --name mongo-local -p 27017:27017 mongo",
256     "mongo:drop" : "npm run mongo:stop && docker rm mongo-local",
257     "mongo:stop" : "docker stop mongo-local",
258     "mongo:start" : "docker start mongo-local"
259   },
f9e311 260 ```
A 261
3691a1 262
D 263 2. To run the application; start a new instance of the MongoDB by running. 
264 ```bash
265 $ npm run mongo
266 ```
267 <p class="tip">
e8cd20 268 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
3691a1 269 </p>
D 270
271 2. Fire up the `todolist-api` by running.
272 ```bash
273 $ npm run start
274 ```
275 ![node-app-started](../images/exercise2/node-app-started.png)
276
486224 277 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`)
3691a1 278 ```bash
D 279 $ curl localhost:9000/api/todos
280 ```
281 ```json
282 [{
283     "_id": "5ac8ff1fdfafb02138698948",
284     "title": "Learn some stuff about MongoDB",
285     "completed": false,
286     "__v": 0
287   },
288   {
289     "_id": "5ac8ff1fdfafb02138698949",
290     "title": "Play with NodeJS",
291     "completed": true,
292     "__v": 0
293 }]
294 ```
295
486224 296 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.
3691a1 297 ![fullstack-app](../images/exercise2/fullstack-app.png)
D 298
299
273e4c 300 ### Part 2 - Create a NodeJS Build slave
374f63 301 > _In this exercise; we will create a build configuration to generate a slave for Jenkins to use in it's builds_
273e4c 302
374f63 303 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 304 ```bash
D 305 $ git checkout exercise2/jenkins-slave docker/ templates/ params/
306 ```
307
308 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`.
309 ```bash
310 SOURCE_REPOSITORY_URL=<YOUR_ENABLEMENT_GIT_REPO>
311 SOURCE_CONTEXT_DIR=docker/jenkins-slave-npm
312 NAME=npm-jenkins-slave
313 ```
314
b92135 315 3. Create an item in the `inventory/host_vars/ci-cd-tooling.yml` under the `ci-cd-builds` object to run the template with.
273e4c 316 ```yaml
D 317   - name: "jenkins-npm-slave"
b92135 318     namespace: "{{ ci_cd_namespace }}"
D 319     template: "{{ playbook_dir }}/templates/jenkins-slave-generic-template.yml"
320     params: "{{ playbook_dir }}/params/jenkins-slave-npm"
273e4c 321     tags:
D 322     - jenkins-slave
323 ```
324 ![jenkins-slave-ansible](../images/exercise2/jenkins-slave-ansible.png)
325
326 3. Run the OpenShift Applier to trigger a build of this jenkins slave image.
327 ```bash
b92135 328 $ ansible-playbook apply.yml -e target=tools \
273e4c 329      -i inventory/ \
D 330      -e "filter_tags=jenkins-slave"
331 ```
332
333 3. Verify the build executed successfully by logging into the cluster and checking the `builds` tab of the `<YOUR_NAME>-ci-cd` project.
334 ![jenkins-slave-npm-build](../images/exercise2/jenkins-slave-npm-build.png)
335
336 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
337 <p class="tip">
338 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.
339 </p>
340
341 ### Part 3 - Add configs to cluster 
14268c 342 > _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_
273e4c 343
14268c 344 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. 
d98f40 345 ```
D 346 .openshift-applier
347 ├── README.md
348 ├── apply.yml
349 ├── inventory
350 │   ├── group_vars
351 │   │   └── all.yml
352 │   └── hosts
353 ├── params
354 │   ├── build
355 │   ├── dev
356 │   └── test
357 ├── requirements.yml
358 └── templates
359     ├── todolist-api-build.yml
360     └── todolist-api-deploy.yml
361 ```
362 where the following
d1565e 363     * the `apply.yml` file is the entrypoint. 
d98f40 364     * the `inventory` contains the objects to populate the cluster with.
D 365     * the `params` contains the variables we'll apply to the `templates`
14268c 366     * the `templates` required by the app. These include the Build, Deploy configs as well as the services, health checks, and other app definitions.
d98f40 367
d1565e 368 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. 
d98f40 369 ```yaml
D 370     ci_cd_namespace: donal-ci-cd
371     dev_namespace: donal-dev
372     test_namespace: donal-test
373 ```
374
d1565e 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: 
d98f40 376 ```bash
D 377 PIPELINES_NAMESPACE=donal-ci-cd
261f19 378 NAME=todolist-fe
d98f40 379 DEPLOYER_USER=jenkins
D 380 APP_TAG=latest
381 NAMESPACE=donal-dev
382 ```
383
14268c 384 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.
d98f40 385 ```bash
D 386 $ ansible-galaxy install -r requirements.yml --roles-path=roles
387 $ ansible-playbook apply.yml -i inventory/
388 ```
69a4e4 389 ![ansible-success](../images/exercise2/ansible-success.png)
d98f40 390
14268c 391 4. Once successful, `commit` and `push` your changes to gitlab.
A 392
176e08 393 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.
261f19 394
D 395 4. In the `apply.yml` update the namespace `<YOUR_NAME>` variables accordingly. For example:
396 ```yaml
397     ci_cd_namespace: donal-ci-cd
398     dev_namespace: donal-dev
399     test_namespace: donal-test
400 ```
401
402 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:
403 ```bash
404 PIPELINES_NAMESPACE=donal-ci-cd
405 NAME=todolist-api
406 DEPLOYER_USER=jenkins
407 APP_TAG=latest
408 NAMESPACE=donal-dev
409 ```
410
d362dc 411 4. Finally; run the Openshift Applier and install its dependencies to run the content into the cluster
261f19 412 ```bash
D 413 $ ansible-galaxy install -r requirements.yml --roles-path=roles
414 $ ansible-playbook apply.yml -i inventory/
415 ```
273e4c 416
176e08 417 4. Validate the build and deploy configs have been created in Openshift by checking `<YOUR_NAME> CI-CD builds` for the `BuildConfigs`
A 418
419 4. Check `<YOUR_NAME>-dev` for the deployment ![ocp-app-deployment-overview](../images/exercise2/ocp-app-deployment-overview.jpeg)
420
273e4c 421 ### Part 4 - Build > Bake > Deploy 
b15f06 422 > _In this exercise; we take what we have working locally and get it working in OpenShift_
273e4c 423
40ab32 424 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 425 * a *build* job is responsible for compiling and packaging our code:
426     1. Checkout from source code (`develop` for `<yourname>-dev` & `master` for `<yourname>-test`)
427     2. Install node dependencies and run a build / package
428     3. Send the package to Nexus
429     4. Archive the workspace to persist the workspace in case of failure
430     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.
431     5. Trigger the `bake` job with the `${BUILD_TAG}` param
432 * a *bake* job should take the package and put it in a Linux Container
433     1. Take an input of the previous jobs `${BUILD_TAG}` ie `${JOB_NAME}.${BUILD_NUMBER}`.
434     2. Checkout the binary from Nexus and unzip it's contents
435     3. Run an oc start-build of the App's BuildConfig and tag it's imagestream with the provided `${BUILD_TAG}`
436     4. Trigger a deploy job using the parameter `${BUILD_TAG}`
437 * a *deploy* job should roll out the changes by updating the image tag in the DC:
438     1. Take an input of the `${BUILD_TAG}`
439     2. Patch / set the DeploymentConfig to the image's `${BUILD_TAG}`
440     3. Rollout the changes
441     4. Verify the deployment
19df4d 442 * We will now go through these steps in detail.
273e4c 443
40ab32 444 #### Part 4a - Build
273e4c 445
261f19 446 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)
273e4c 447
dd060d 448 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 449
19f0bd 450 5. The page that loads is the Job Configuration page andi t 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 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 451
dd060d 452 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 453
19f0bd 454 5. 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)
0f4d08 455
dd060d 456 5. Scroll down to the Build Environment tab and select the `Color ANSI Console Output` checkbox ![ansi](../images/exercise2/ansi.png)
c3a934 457
19df4d 458 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 459 ```bash
D 460 #!/bin/bash
461 set -o xtrace
462 scl enable rh-nodejs8 'npm install'
463 scl enable rh-nodejs8 'npm run build:ci:dev'
464 scl enable rh-nodejs8 'npm run package'
465 scl enable rh-nodejs8 'npm run publish'
466 ```
467 ![build-step](../images/exercise2/build-step.png)
468
dd060d 469 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 470
dd060d 471 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 472     * Tick the box `Push Only If Build Succeeds`
D 473     * Add the Tag to push of 
474 ```bash
475 ${JOB_NAME}.${BUILD_NUMBER}
476 ```
477     * Specify the commit message to be
478 ```bash
479 Automated commit by jenkins from ${JOB_NAME}.${BUILD_NUMBER}
480 ```
19f0bd 481
A 482     * Check `Create New Tag` and set `Target remote name` to `origin`
c3a934 483 ![git-publisher](../images/exercise2/git-publisher.png)
D 484
19f0bd 485 5. 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`. 
c3a934 486     * Set the project to build to be `dev-todolist-fe-bake` 
D 487     * Set the condition to be `Stable or unstable but not failed`. 
488     * Click Add Parameters dropdown and select Predefined parameters. 
489     * In the box, insert our BUILD_TAG as follows
490 ```bash
491 BUILD_TAG=${JOB_NAME}.${BUILD_NUMBER}
492 ```
493 ![param-trigger](../images/exercise2/param-trigger.png)
494 <p class="tip">
eaf747 495     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 496 </p>
D 497
dd060d 498 5. Hit `save` which will take you to the job overview page - and that's it; our *build* phase is complete!
c3a934 499
40ab32 500 #### Part 4b - Bake
D 501
dd060d 502 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 503
19f0bd 504 5. This job will take in the BUILD_TAG from the previous one so check the `This project is parameterized` box on the General tab.
eaf747 505     * Add string parameter type
19f0bd 506     * set the Name to `BUILD_TAG`. This will be available to the job as an Enviroment Variable.
eaf747 507     * You can set `dev-todolist-fe-build.` as the default value for ease when triggering manually.
D 508     * 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`
509 ![param-trigger-bake](../images/exercise2/param-trigger-bake.png)
0f4d08 510
dd060d 511 5. This time set the `Restrict where this project can be run` label to `master`.
eaf747 512 <p class="tip">
19f0bd 513     `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.
eaf747 514 </p>
D 515
dd060d 516 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 517
dd060d 518 5. Scroll down to the Build Environment tab and select the `Color ANSI Console Output` checkbox ![delete-ansi](../images/exercise2/delete-ansi.png)
eaf747 519
dd060d 520 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:
19f0bd 521 Remember to replace `<YOUR_NAME>` accordingly.
eaf747 522 ```bash
D 523 #!/bin/bash
436657 524 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 525 unzip package-contents.zip
D 526 oc project <YOUR_NAME>-ci-cd # probs not needed
527 NAME=todolist-fe
62d4b9 528 oc patch bc ${NAME} -p "{\"spec\":{\"output\":{\"to\":{\"kind\":\"ImageStreamTag\",\"name\":\"${NAME}:${BUILD_TAG}\"}}}}"
eaf747 529 oc start-build ${NAME} --from-dir=package-contents/ --follow
D 530 ```
531 ![bake-step](../images/exercise2/bake-step.png)
532
dd060d 533 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 534     * Set the project to build to be `dev-todolist-fe-deploy`
D 535     * Set the condition to be `Stable`.
536     * Click Add Parameters dropdown and select Current build parameters. This will pass the ${BUILD_TAG} to the downstream job which we will create next.
537 ![downstream-trigger-deploy](../images/exercise2/downstream-trigger-deploy.png)
538
dd060d 539 5. Hit save! That's our *bake* phase done! Finally; on to our *deploy*
eaf747 540
40ab32 541 #### Part 4c - Deploy
D 542
19f0bd 543 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 but scroll to the bottom and Copy from `dev-todolist-fe-bake`.
dd060d 544 ![copy-from](../images/exercise2/copy-from.png)
D 545
19f0bd 546 5. 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.
dd060d 547 ```bash
D 548 #!/bin/bash
549 set -o xtrace
550 # VARS
19f0bd 551 PIPELINES_NAMESPACE=<YOUR_NAME>-ci-cd
A 552 NAMESPACE=<YOUR_NAME>-dev
dd060d 553 NAME=todolist-fe
D 554 oc project ${NAMESPACE}
555 oc tag ${PIPELINES_NAMESPACE}/${NAME}:${BUILD_TAG} ${NAMESPACE}/${NAME}:${BUILD_TAG}
556 oc set env dc ${NAME} NODE_ENV=dev
557 oc set image dc/${NAME} ${NAME}=docker-registry.default.svc:5000/${NAMESPACE}/${NAME}:${BUILD_TAG}
558 oc rollout latest dc/${NAME}
559 ```
560 ![deploy-step](../images/exercise2/deploy-step.png)
561
19f0bd 562 5. Secondly, add another build step called `Verify OpenShift Deployment` including the following:
dd060d 563     * Set the Project to your `<YOUR_NAME>-dev`
D 564     * Set the DeploymentConfig to your app's name `todolist-fe`
565     * Set the replica count to `1`
566 ![verify-deployment](../images/exercise2/verify-deployment.png)
567
568 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!
569
40ab32 570 #### Part 4d - Pipeline
D 571
19f0bd 572 5. With our Jenkins setup in place; now move to our `todolist-fe` app. We need to add our configuration to the frontend to tell it where the API layer is hosted. Open in it your favourite editor and navigate to `src/config/dev.js`. Update `<YOUR_NAME>` accordingly with the root where the Todo List API will live. For example:
40ab32 573 ![fe-dev-config](../images/exercise2/fe-dev-config.png)
D 574
575 5. Repeat this for `src/config/test.js` file. Once done; commit your chanages and push them to GitLab
576 ```bash
577 $ git add .
578 $ git commit -m "ADD config for api"
579 $ git push
580 ```
581
19f0bd 582 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. Give the new view a sensible name like `dev-todolist-fe-pipeline`
dd060d 583 ![add-view](../images/exercise2/add-view.png). 
D 584
585 5. Set the Pipeline Flow's Inital Job to `dev-todolist-fe-build` and save.
19f0bd 586 TODO - change below sscreenshot
dd060d 587 ![pipeline-flow](../images/exercise2/pipeline-flow.png)
D 588
19f0bd 589 5. 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). 
dd060d 590 ![dev-pipeline-view](../images/exercise2/dev-pipeline-view.png)
D 591
592 ### Part 5 - Backend Pipeline
19f0bd 593 > 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 by copying the config.
dd060d 594
19f0bd 595 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`.
436657 596 ![copy-fe-build](../images/exercise2/copy-fe-build.png)
dd060d 597
436657 598 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 599     * The GitLab project URL
600     * Projects to build on the Post Build Action
dd060d 601
19f0bd 602 6. On the Build tab; remove the `:dev` from the `npm run build:ci:dev` so the line reads.
A 603  The rest of the instructions can be left as they are.
436657 604 ```bash
D 605 scl enable rh-nodejs8 'npm run build:ci'
606 ```
dd060d 607
436657 608 6. Save the configuration for `dev-todolist-api-build`
dd060d 609
19f0bd 610 6. On Jenkins home; create a new job for our backend bake 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.
436657 611
D 612 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:
613     * The BUILD_TAG default value and description
614     * NAME in the execute shell step
615     * Projects to build on the Post Build Action
616
617 6. Save the configuration for `dev-todolist-api-build`
618
619 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.
620
621 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:
622     * The BUILD_TAG default value and description
623     * NAME in the execute shell step
624     * The name of the DeploymentConfig to validate in the Verify OpenShift Deployment
625
626 6. Save the configuration for `dev-todolist-api-build` and that's it for wiring together our `todolist-api` pipeline.
627  
f9e311 628 6. Run the `dev-todolist-api-build` to trigger the backend pipeline. While this is building, 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 no backend deployed.
436657 629 ![no-backend-app](../images/exercise2/no-backend-app.png)
D 630
f9e311 631 6.  When `dev-todolist-api-build` has completed we should see the sample data has changed on refresh.
436657 632 ![with-backend-app](../images/exercise2/with-backend-app.png)
0f4d08 633
f50a94 634 ### Part 6 - GitLab Webhooks
b770c6 635 > _In this exercise we will link GitLab to Jenkins so that new build jobs are triggered on each push to the `develop` branch._
13e8fd 636
b770c6 637 <p class="tip" >
D 638 NOTE - This section is optional! Git webhooks are useful but not needed for course completion.
639 </p>
640
641 7. In order to allow GitLab trigger Jenkins (because of the OpenShift Auth Plugin), we need to allow the `Annoymous` user trigger 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.s8.core.rht-labs.com/configureSecurity/` . You should see a screen like so:
f50a94 642 ![jenkins-global-security](../images/exercise2/jenkins-global-security.png)
A 643
644 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`.
645 ![jenkins-anon-permissions](../images/exercise2/jenkins-anon-permissions.png)
646
647 7. Go to your `dev-todolist-fe-build` and head to the `configure` section (`https://jenkins-<YOUR_NAME>-ci-cd.apps.s8.core.rht-labs.com/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.s8.core.rht-labs.com/project/dev-todolist-fe-build`.
c50c7b 648 ![jenkins-build-triggers-gitlab](../images/exercise2/jenkins-build-triggers-gitlab.png)
13e8fd 649
c50c7b 650 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.s8.core.rht-labs.com/<YOUR_NAME>/todolist-fe/settings/integrations`)
A 651 ![gitlab-integrations](../images/exercise2/gitlab-integrations.png)
13e8fd 652
c50c7b 653 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 654 ![gitlab-integrations-details](../images/exercise2/gitlab-integrations-details.png)
13e8fd 655
c50c7b 656 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 657 ![gitlab-integrations-details](../images/exercise2/gitlab-webhook-test.png)
13e8fd 658
c50c7b 659 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.
13e8fd 660
c50c7b 661 7. All that's left to do is to repeat the same steps for `todolist-api`:
A 662 Create Build Trigger: 
663 `https://jenkins-<YOUR_NAME>-ci-cd.apps.s8.core.rht-labs.com/job/dev-todolist-api-build/configure`
664 Create GitLab Integration:
665 `https://gitlab-<YOUR_NAME>-ci-cd.apps.s8.core.rht-labs.com/donal/todolist-api/settings/integrations`
666 Check your build status and you should see something like this. With `Started by Gitlab push by <YOUR_NAME>`:
667 ![jenkins-gitlab-webhook-success](../images/exercise2/jenkins-gitlab-webhook-success.png)
13e8fd 668
c50c7b 669 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.
b770c6 670
5a16fd 671 _____
D 672
0f4d08 673 ## Extension Tasks
a4f42c 674 > _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 675
436657 676 - Pipeline Tasks
f9e311 677     * Add pipeline for `master` branch for each project. Use `test-` instead of `dev-` across all config and names in the pipeline
A 678     * Do the `.openshift-applier` steps as part of the pipeline for greater end to end automation.
c3a934 679 - Promote build
f9e311 680     * Create a _promote-to-uat_ phase after the `master` branch deploy
a4f42c 681     * Create a `uat` env using the OpenShift Applier as seen before
c3a934 682     * Tag and promote the image without rebuilding after the `test-**-deploy`
261f19 683 - MongoDB tasks
c3a934 684     * Add MongoDB Stateful set for the UAT environment (or test)
D 685     * Inject MongoDB config into the NodeJS app using config map & secrets.
a4f42c 686     * Improve the security of the DB by making the user /passwords randomly generated
c3a934 687 - Setup Nexus as an `npm` mirror registry and use it in the builds to speed up the build time
0f4d08 688
D 689 ## Additional Reading
5a16fd 690 > List of links or other reading that might be of use / reference for the exercise
D 691
f2b1b4 692  -  What's in a package.json?
D 693
5a16fd 694 ## Slide links
374f63 695 > link back to the deck for the supporting material