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