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