text:
<file_sep>
## Integrating Cloud Native Applications on OpenShift with Cloud Pak for Integration

Welcome to our workshop! In this workshop we'll be using the Cloud Pak for Integration platform to wire together a set of micro-service applications into a running solution on OpenShift. The goals of this workshop are:

* Deploy an API Gateway using `API Connect`, to provide an API management point across the micro-services
* Simplify data access from external data sources, such as Salesforce, using `App Connect`
* Integrate payment flow into an existing `MQ` payment service
* Simplify flow of status udpates to the end user with a kafka event stream, using `Event Streams`
* Find performace bottlenecks in the overall flow, using `Tracing`

[Rewrite everything below this line]

![Tools used in the workshop](.gitbook/images/tools-for-workshop.png)

### About this workshop

The introductory page of the workshop is broken down into the following sections:

* [Agenda](#agenda)
* [Compatability](#compatability)
* [About Cloud Pak for Applications](#about-cloud-pak-for-applications)
* [Credits](#credits)

## Agenda

### Day 1: Kabanero and Appsody for Developers and Operators

In this first day we'll learn how to use Appsody to run the *inner loop* of the development and test cycle for a developer, and how these tools can be integrated into your favorite IDE. We'll also explore how to deploy an application to OpenShift, first manually with Appsody for dev/test purposes, and then using the standard Kabanero Tekton piplines with GitOps as part of a continual test/production cycle.

|   |   |
| - | - |
| [Lecture 1: What is Cloud Native?](https://ibm.box.com/s/3pvl4jdi3xifs1olzcl9np904zvk5ueo) | Learn about the technologies that underpin Cloud Native applications |
| [Lecture 2: Kabanero Overview](https://ibm.box.com/s/6jl4b7sj8xqgh7rvxtea5ykpsjyu1siz) | Learn about Kabanero. An open source project to rapidly create Cloud Native applications |
| [Exercise 1: Introduction to Appsody and Codewind](exercise-1/README.md) | Install the Appsody component of Kabanero into the IDE with Codewind, Learn about the developer flow, building your first application with Appsody |
| [Exercise 2: Using Appsody CLI to develop, test, and debug applications](exercise-2/README.md) | Use the Appsody CLI to quickly create frontend and backend applications for a sample application using two different technologies (Spring and nodejs express) |
| [Exercise 3: Deploying to OpenShift with Appsody](exercise-3/README.md) | Deploy the built applications to IBM Managed OpenShift with Appsody for dev/test purposes |
| [Lecture 3: Adding value with IBM Cloud Pak for Applications](https://ibm.box.com/s/y4wh104vdos1vw5kdjwwuhebf8jgq580) | Learn about how IBM Cloud Pak for Applications bundles everything together |
| [Exercise 4: Use Tekton and Kabanero Pipelines to continuously deploy](exercise-4/README.md) | Deploy the built applications to IBM Managed OpenShift using GitOps to trigger a Tekton pipeline |

### Day 2: Customizing Stacks, Pipelines in Collections

In the second day we'll learn about the Kabanero open source project and how to productionize our applications with custom Appsody Stacks, custom Collections, and custom Tekton pipelines.

|   |   |
| - | - |
| [Lecture 4: Customizing Appsody and Kabanero](https://ibm.box.com/s/kbuympaqftxswyi1aoswdlqussmqf1ba) | Learn all about the stacks and repos |
| [Exercise 6: Building a custom Collection](exercise-6/README.md) | Create a collection that will contain custom appsody stacks and pipelines |
| [Exercise 5: Customizing an existing Appsody Stack](exercise-5/README.md) | Create a custom stack, to be hosted in our custom repository |
| [Exercise 7: Using a custom Collection with Appsody](exercise-7/README.md) | Learn how to manage these custom stacks and how to make them available to developers |
| [Lecture 5: Tekton Overview](https://ibm.box.com/s/tg0f6nhs91trlzkb5pfnh5e1rdzg4wm6) | Learn all Tekton CI/CD and how Kabanero uses it |
| [Exercise 8: Create a custom Tekton Task and Pipleline](exercise-8/README.md) | Build a pipeline that will fit into a custom Collection |
| [Exercise 9: Deploy an application with a custom Stack, custom Collection, and custom Pipeline](exercise-9/README.md) | Build and deploy an application using the custom stack, collection and pipelines built by the Architects' and Operators' tracks |

## Compatability

This workshop has been tested on the following platforms:

* **macOS**: Mojave (10.14), Catalina (10.15)
* **Windows** Windows 10 (with enterprise AAD and git bash)

## About Cloud Pak for Applications

IBM Cloud Pak for Applications is an enterprise-ready, containerized software solution for modernizing existing applications and developing new cloud-native apps that run on Red Hat® OpenShift®. Built on IBM WebSphere offerings and Red Hat OpenShift Container Platform with the Kabanero open source project, Cloud Pak for Applications provides a long-term solution to help you transition between public, private, and hybrid clouds, and create new business applications.

### A few other noteworthy mentions

Cloud Pak for Applications:

* ... includes
  * the Kabanero open source project
  * WebSphere
  * Mobile Foundation
  * IBM Cloud Private
  * Transformation Advisory
  * ...and more
* ... can be deployed on any major cloud provider supporting OpenShift (IBM Cloud, AWS, Azure, GCP)

![Cloud Pak for Application Stack](.gitbook/images/cp4apps.png)

## Credits

This workshop was primarily written by [<NAME>](https://github.com/henrynash) and [<NAME>](https://github.com/stevemar). Many other IBMers have contributed to help shape, test, and contribute to the workshop.

* [<NAME>](https://github.com/GregDritschler): For his [Insurance Quote Code Pattern](https://github.com/IBM/appsody-sample-quote-app)
* [<NAME>](https://github.com/csantanapr): For his [Learning Kabanero 101 Tutorial](https://ibm-cloud-architecture.github.io/Learning-Kabanero-101)
* [<NAME>](https://github.com/timroster): For testing the lab on Windows
<file_sep># Exercise 5: Customizing an existing Appsody Stack

The goals for this day are to customize each of the pre-configured asset from Day 1, meaning we will:

* extend an Appsody Stack
* publish a new Collections
* add a new Tekton task to our Tekton pipeline

Specifically, when you have completed this exercise, you will understand how to:

* extend an Appsody stack to create a new asset to be used in our Collection

![Tools used during Exercise 5](images/ex5.png)

## Prerequisites

You should have already carried out the prerequisites defined in the [Pre-work](../pre-work/README.md). Check that you have access to the Appsody CLI by typing (the exact version number my be greater than shown below):

> **NOTE:** In the exercises that follow you will see the actual command to run, followed by a separate example of running the command with the expected output. You only need to run the first example and never need to run a command you see preceded by a "$". You can even use the copy button on the right side of the command to make copying easier.

```bash
appsody version
```

You should see output similar to the following:

```bash
$ appsody version
appsody 0.5.3
```

## Steps

1. [The role of a stack in the development process](#1-the-role-of-a-stack-in-the-development-process)
1. [Stack structure](#2-stack-structure)
1. [Create a new stack, based on an existing one](#3-create-a-new-stack-based-on-an-existing-one)
1. [Use the new stack in our example application](#4-use-the-new-stack-in-our-example-application)

### 1. The role of a stack in the development process

Developers use stacks to simplify building applications that require a specific set of technologies or development patterns. While there are numerous publicly available stacks to choose from, many enterprises want to build their own set of stacks that uphold their specific requirements and standards for how they want to their developers to build cloud native applications.

In this exercise, you will learn how to modify an existing stack to more closely match your requirements. Before starting this, let's do a quick review of the design requirements for stacks. A stack is designed to support the developer in either a rapid, local development mode or a build-and-deploy mode.

#### Rapid, local development mode

In this mode, the stack contains everything a developer needs to build a new application on a local machine, with the application always running in a local containerized Docker environment. Introducing containerization from the start of the application development process (as opposed to development solely in the user space of the local machine) decreases the introduction of subtle errors in the containerization process and removes the need for a developer to install the core technology components of their application.

In this mode, the stack is required to have all the dependencies for the specific technologies pre-built into the Docker image, and also to dynamically compliment these with whatever dependencies the developer adds explicitly for his or her code.

Rapid local development mode in Appsody consists of the Appsody CLI (hooked into a local IDE if required) communicating with a local Docker container that is running the application under development. With this mode, application code can be held on the local file system, while being mounted in the Docker container, so that a local change can automatically trigger a restart of the application.

#### Build-and-deploy mode

In this mode, the stack enables the Appsody CLI to build a self-contained Docker image that includes both the core technologies in the stack plus the application code, along with the combined dependencies of both. You can deploy the resulting image manually or programmatically to any platform that supports Docker images (such as a local or public Kubernetes cluster).

A pictorial view of how an application developer uses a stack, looks like this:

![Appsody Flow](images/appsody.png)

The above development flow shows the manual deployment to a Kubernetes cluster. In more production-orientated environments, GitOps might trigger the build and deploy steps and Tekton Pipelines would drive the deployment. [Collections](https://github.com/kabanero-io/collections/), which are a part of [Cloud Pak for Applications](https://www.ibm.com/cloud/cloud-pak-for-applications), bring together Appsody stacks, GitOps, and Tekton Pipelines to provide an enterprise-ready solution for cloud-native application development and deployment. We'll look at this in later exercises.

### 2. Stack structure

Because a single Appsody stack can enable both rapid, local development and build-and-deploy modes, all stacks follow a standard structure. The structure below represents the source structure of a stack:

```ini
my-stack
├── README.md
├── stack.yaml
├── image/
|   ├── config/
|   |   └── app-deploy.yaml
|   ├── project/
|   |   ├── [files that provide the technology components of the stack]
|   |   └── Dockerfile
│   ├── Dockerfile-stack
|   └── LICENSE
└── templates/
    ├── my-template-1/
    |       └── [example files as a starter for the application, e.g. "hello world"]
    └── my-template-2/
            └── [example files as a starter for a more complex application]

```

As a *Stack Architect* you must create the above structure, build it into an actual stack image ready for use by an *Application Developer* who bases their new application on your stack. Part of your role as a stack architect is to include one of more sample applications (known as *templates*) to help the application developer get started.

Hence, when you build a stack, the structure above is processed and generates a Docker image for the stack, along with tar files of each of the templates, which can then all be stored and referenced in a local or public Appsody repo. The Appsody CLI can access the repo to use the stack to initiate local development.

For this exercise we will modify the nodejs-express stack that we have been using for our quote-frontend, to add some additional security hardening (individual enterprises often have specific security standards that need to be met to allow deployment).

> **NOTE**: For future reference, to make your own stack from scratch, instead of extending an existing one, follow [this tutorial](https://developer.ibm.com/tutorials/create-appsody-stack/).

### 3. Create a new stack, based on an existing one

The goal of this step is to create a new Node.js Express stack by modifying the existing one. We'll copy it, build, and modify it.

#### Initialize the stack

To create a new stack, you must first construct a scaffold of the above structure. Stacks are classified as being `stable`, `incubating` or `experimental`. You can read more about these classifications [here](https://appsody.dev/docs/stacks/stacks-overview). To make things easy, the Appsody CLI supports an `appsody stack create` command to create a new stack, by copying an existing one.

By running the `appsody stack create` command, *nodejs-express* stack will be copied and moved, a directory will be created containing the new stack.

```bash
cd ~/appsody-apps
appsody stack create my-nodejs-express --copy incubator/nodejs-express
cd my-nodejs-express
ls -al
```

You should see output similar to the following:

```bash
$ ls -la
total 24
drwxr-xr-x  6 henrynash  staff   192  8 Nov 11:51 .
drwxr-xr-x  5 henrynash  staff   160  8 Nov 11:51 ..
-rw-r--r--  1 henrynash  staff  5026  8 Nov 11:51 README.md
drwxr-xr-x  7 henrynash  staff   224  8 Nov 11:51 image
-rw-r--r--  1 henrynash  staff   319  8 Nov 11:51 stack.yaml
drwxr-xr-x  4 henrynash  staff   128  8 Nov 11:51 templates
```

If you inspect the contents of the `image` directory, you will see how it matches the stack structure given earlier.

#### Build your new stack

Before we make any changes, let's go through the steps of building (or *packaging*) a stack, to create a stack image (which is a Docker image) that the Appsody CLI can use to initiate a project using that stack.

There is a Docker file (`Dockerfile-stack`) within the sample stack structure you copied. The `appsody stack package` command will use this to build the image.

To build your new stack in this way, from the `my-nodejs-express` directory enter:

```bash
appsody stack package
```

This runs a Docker build, installs `my-nodejs-express` into a local Appsody repository (called `dev.local`), and runs some basic tests to make sure the file is well formed.

Once the build is complete, use the `appsody list` command to check that it is now available in the local repo:

```bash
appsody list dev.local
```

You should see output similar to the following:

```bash
$ appsody list dev.local
REPO        ID                  VERSION     TEMPLATES           DESCRIPTION
dev.local   my-nodejs-express   0.2.8       scaffold, *simple   Express web framework for Node.js
```

#### Run the new stack

So, at this point, you have been carrying out your role as a stack architect to build and install your new (albeit unchanged) stack. Now it's time to try it out as an application developer.

Create a new directory and initialize it with this new Appsody stack:

```bash
mkdir ~/appsody-apps/test-my-stack
cd ~/appsody-apps/test-my-stack
appsody init dev.local/my-nodejs-express
```

Now use `appsody run` to test running an application based on your copy of the stack:

```bash
appsody run
```

You should see output similar to the following:

```bash
$ appsody run
Running development environment...
Using local cache for image dev.local/appsody/my-nodejs-express:0.2
Running docker command: docker run --rm -p 3000:3000 -p 9229:9229 --name test73-dev -v /Users/henrynash/codewind-workspace/test73/:/project/user-app -v test73-deps:/project/user-app/node_modules -v /Users/henrynash/.appsody/appsody-controller:/appsody/appsody-controller -t --entrypoint /appsody/appsody-controller dev.local/appsody/my-nodejs-express:0.2 --mode=run
[Container] Running APPSODY_PREP command: npm install --prefix user-app && npm audit fix --prefix user-app
added 170 packages from 578 contributors and audited 295 packages in 3.5s
[Container] found 0 vulnerabilities
...
[Container] App started on PORT 3000
```

To check it is running, in a separate terminal window we can use `curl` to hit the endpoint:

```bash
curl -v localhost:3000
```

So now we are ready to make change to our new stack. For this exercise we will harden the HTTP headers that an application, built using this stack, responds with. We can look at the current headers returned:

```bash
$ curl -v localhost:3000
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 19
< ETag: W/"13-0ErcqB22cNteJ3vXrBgUhlCj8os"
< Date: Mon, 21 Oct 2019 12:09:49 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
Hello from Appsody!
```

Stop this current appsody run by running `appsody stop` in a separate terminal window, from within the same directory.

For this exercise we will modify the stack to include the popular HTTP header security module [helmet](https://helmetjs.github.io), and hence this should change the headers we see returned to us. Note we will do this as a *stack architect* since we don't want to rely on *application developers* remembering to do this. By doing this in the stack itself, all applications built using our modified stack will have helmet automatically enabled.

#### Modify your custom stack

When creating a custom stack, based on an existing stack, the first thing to do is to take a look at what the existing stack has provided. A more detailed description of the stack components can be found [here](https://appsody.dev/docs/stacks/stack-structure), but the key ones are:

* A Dockerfile (`image/Dockerfile-stack`) that builds your stack image. This is what the `appsody stack package` command used above to build a Docker image of your stack - which is, if you like, the eventual artifact that you deliver as a stack architect to application developers.
* A Dockerfile (`image/project/Dockerfile`) that builds the final application image. This final image will contain both your stack and their application, and this Dockerfile is processed by the application developer running `appsody build` and `appsody deploy`.
* Typically some kind of server side code that is enabling the application the developer will create and run. For this stack, this is `image/project/server.js`.
* Some kind of dependency management, ensuring both the correct inclusion of components defined by the stack, as well as, potentially, any added by the application developer. For this stack, this is `image/project/package.json`.
* At least one sample application (or *template*); these are stored in the `templates` directory.

It is worth taking some time checking out the files given above to get a feel of the stack.

For some stack modifications, you can actually use a form of stack inheritance - i.e. by using the existing stack images as the `FROM` image in `Dockerfile-stack`. An example of this might be where you just want to change one of the Dockerfile variables. In general, however, most modified stacks are effectively copies of an existing stack, with the additional changes added to gain the new, required functionality.

Having examined the files above, you might have already spotted what we need to do to incorporate helmet into the new stack - namely to modify `image/project/server.js` to enable it.

Go back to the `my-nodejs-express` directory.

```bash
cd ~/appsody-apps/my-nodejs-express
```

The current code in `image/project/server.js` looks something like this:

```javascript
// Requires statements and code for non-production mode usage
if (!process.env.NODE_ENV || !process.env.NODE_ENV === 'production') {
require('appmetrics-dash').attach();
}
const express = require('express');
const health = require('@cloudnative/health-connect');
const fs = require('fs');

require('appmetrics-prometheus').attach();

const app = express();

const basePath = __dirname + '/user-app/';

function getEntryPoint() {
    let rawPackage = fs.readFileSync(basePath + 'package.json');
    let package = JSON.parse(rawPackage);
    if (!package.main) {
        console.error("Please define a primary entrypoint of your application by adding 'main: <entrypoint>' to package.json.")
        process.exit(1)
    }
    return package.main;
}

// Register the users app. As this is before the health/live/ready routes,
// those can be overridden by the user
const userApp = require(basePath + getEntryPoint()).app;
app.use('/', userApp);

const healthcheck = new health.HealthChecker();
app.use('/live', health.LivenessEndpoint(healthcheck));
app.use('/ready', health.ReadinessEndpoint(healthcheck));
app.use('/health', health.HealthEndpoint(healthcheck));

app.get('*', (req, res) => {
res.status(404).send("Not Found");
});

const PORT = process.env.PORT || 3000;
const server = app.listen(PORT, () => {
console.log(`App started on PORT ${PORT}`);
});

// Export server for testing purposes
module.exports.server = server;
module.exports.PORT = PORT;
```

We will modify this file by adding two lines, to import helmet (with `require()`), and to enable it with `app.use()`:

```javascript
// Requires statements and code for non-production mode usage
if (!process.env.NODE_ENV || !process.env.NODE_ENV === 'production') {
require('appmetrics-dash').attach();
}
const express = require('express');
const helmet = require('helmet');
const health = require('@cloudnative/health-connect');
const fs = require('fs');

require('appmetrics-prometheus').attach();

const app = express();
app.use(helmet());

const basePath = __dirname + '/user-app/';
...
```

Since we have added a new module that is required, we must also update the dependency management (package.json), to ensure this is pulled in:

```json
{
...
"dependencies": {
    "@cloudnative/health-connect": "^2.0.0",
    "appmetrics-prometheus": "^3.0.0",
    "express": "~4.16.0",
    "helmet": "^3.21.1"
},
...
}
```

Now that we have modified our stack, we need to re-package it, using the same command as before:

```bash
appsody stack package
```

> **NOTE** The `appsody run` command should pull down the latest packaged version, but in case this doesn't work, delete the directory and re-initialize.

```bash
cd ~/appsody-apps
rm -rf test-my-stack
mkdir test-my-stack
cd test-my-stack
appsody init dev.local/my-nodejs-express
```

This will have updated the dev.local index, so we can again go and run our application:

```bash
appsody run
```

If we now hit the endpoint as before with `curl` in verbose mode, we can see if the HTTP headers have changed:

```bash
curl -v localhost:3000
```

You should now see security related headers like `X-DNS-Prefetch-Control`, `Strict-Transport-Security`, and `X-Download-Options`:

```bash
$ curl -v localhost:3000
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< X-DNS-Prefetch-Control: off
< X-Frame-Options: SAMEORIGIN
< Strict-Transport-Security: max-age=15552000; includeSubDomains
< X-Download-Options: noopen
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 19
< ETag: W/"13-0ErcqB22cNteJ3vXrBgUhlCj8os"
< Date: Fri, 08 Nov 2019 19:39:22 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
Hello from Appsody!*
```

As you should see, because the stack now incorporates helmet, the HTTP headers have changes, and our application runs with this protection. The inclusion of helmet is just an example of some of the security hardening you might want to take within your own enterprise.

Stop this current appsody run by running `appsody stop` in a separate terminal window, from within the same directory.

### 4. Use the new stack in our example application

A final step is to switch the actual quote-frontend application we built in [Exercise 2](../exercise-2/README.md) to use our new stack (rather than the original `nodejs-express` stack).

The formal way of doing this is to repeat the steps from Exercise 2, where the new project is initialized (using our new stack), and the dependencies and code for the frontend are copied into the new project directory. However, in this case, where we have not changed anything that is actually placed directly in the project directory, we can take a short cut and just update the project to point at our new stack. This also gives you a bit more of an idea as to how an application project is linked to a stack. In the `quote-frontend` directory you created in Exercise 2, you should see a file called `.appsody-config.yaml`, which was created by the `appsody init` step.

```bash
cd ~/appsody-apps/quote-frontend
ls -la
```

You should see output similar to the following:

```bash
$ ls -al
total 192
drwxr-xr-x  16 henrynash  staff    512 15 Oct 12:42 .
drwxr-xr-x+ 85 henrynash  staff   2720 17 Oct 21:37 ..
-rw-r--r--   1 henrynash  staff     64 19 Oct 11:10 .appsody-config.yaml
-rw-r--r--   1 henrynash  staff   1316 15 Oct 11:12 .gitignore
drwxr-xr-x   4 henrynash  staff    128 15 Oct 11:12 .vscode
-rw-rw-r--   1 henrynash  staff    806 15 Oct 12:50 app-deploy.yaml
-rw-r--r--   1 henrynash  staff    290 15 Oct 11:15 app.js
drwxr-xr-x   4 henrynash  staff    128 15 Oct 11:15 config
drwxr-xr-x   2 henrynash  staff     64 15 Oct 11:16 node_modules
-rw-r--r--   1 henrynash  staff      0 15 Oct 11:19 nodejs_dc.log
-rw-r--r--   1 henrynash  staff      0 15 Oct 11:19 nodejs_restclient.log
-rw-r--r--@  1 henrynash  staff  73319 15 Oct 11:16 package-lock.json
-rw-r--r--   1 henrynash  staff    615 15 Oct 11:15 package.json
-rw-r--r--   1 henrynash  staff   2779 15 Oct 11:15 quote.js
drwxr-xr-x   3 henrynash  staff     96 15 Oct 11:12 test
drwxr-xr-x   3 henrynash  staff     96 15 Oct 11:15 views
```

Inspecting that file, reveals that it contains a pointer to the stack:

```bash
cat .appsody-config.yaml
```

Should output a configuration that uses `nodejs-express`:

```ini
project-name: quote-frontend
stack: kabanero/nodejs-express:0.2
```

We can simply change the second line to, instead, point to our new stack, i.e.:

> **NOTE**: When using a stack that is in development, it will carry semantic versioning derived from the original copied stack in addition to a latest tag.

```bash
project-name: quote-frontend
stack: dev.local/appsody/my-nodejs-express:latest
```

Now re-run the frontend with `appsody run`:

```bash
appsody run
```

It should use our new stack:

```bash
$ appsody run
Running development environment...
Using local cache for image dev.local/appsody/my-nodejs-express:latest
...
[Container] App started on PORT 3000
```

We can confirm that our new HTTP protection is being used by, instead of using a browser, again using `curl` in verbose mode to hit the published endpoint:

```bash
curl -v localhost:3000
```

You should see output similar to the following:

```bash
$ curl -v localhost:3000
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 302 Found
< X-DNS-Prefetch-Control: off
< X-Frame-Options: SAMEORIGIN
< Strict-Transport-Security: max-age=15552000; includeSubDomains
< X-Download-Options: noopen
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-Powered-By: Express
< Location: /quote
< Vary: Accept
< Content-Type: text/plain; charset=utf-8
< Content-Length: 28
< Date: Fri, 08 Nov 2019 19:51:50 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
Found. Redirecting to /quote
```

We can tell our sample application is now using the new stack because it includes the new security related headers.

Stop this current appsody run by running `appsody stop` in a separate terminal window, from within the same directory.

**Congratulations**! We have successfully built and tested out our modified stack - and seen how applications built against this stack automatically gain the (new) features it provides (without the application developer having to do anything themselves). In later exercises, we will discover how to publish this stack for other developers to utilize to build their own applications.
<file_sep># Exercise 9: Deploy an application with a custom Stack, custom Collection, and custom Pipeline

In this exercise, we will show how to bring all the custom components (Stacks, Collections, Pipelines) together.

When you have completed this exercise, you will understand how to

* Create a custom pipeline and add it to a collection
* Update the Kabanero Custom Resource to point to a new collection
* Deploy an application based on a custom stack using a custom Tekton pipeline

![Tools used during Exercise 9](images/ex9.png)

## Prerequisites

You should have already carried out the prerequisites defined in [Exercise 8](../exercise-8/README.md).

* Delete the webhook from Exercise 8:
  * Open the Tekton dashboard, and select `Webhooks`.
  * Next, click on the box next to the `custom-slack-webhook` to select it and click on *Delete*.
  * In the dialog, check the box to delete pipeline runs associated with the webhook and click on *Confirm*.

* Delete the task and pipeline from Exercise 8, too.

    ```bash
    cd ~/appsody-apps/tekton-tasks
    oc delete -f test-pipeline.yaml
    oc delete -f test-task.yaml
    ```

## Steps

### 1. Add the tasks to the collection

In your local `collections/incubator/my-nodejs-express/pipelines` folder add a new folder called `custom-pipeline` and add two files `test-task.yaml` and `test-build-deploy-pipeline.yaml`. The file structure is seen below

```ini
incubator
└── my-nodejs-express/
    └── pipelines/
        └── custom-pipeline/
              └── test-task.yaml
              └── test-build-deploy-pipeline.yaml
```

The `test-task.yaml` is slightly updated from exercise 8 to pull in the relevant basename from any collection that uses it

```yaml
#Kabanero! on activate substitute CollectionId for text 'CollectionId'
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: CollectionId-test-task
spec:
  inputs:
    resources:
      - name: git-source
        type: git
  outputs:
    resources:
      - name: docker-image
        type: image
  steps:
    - name: test-echo
      image: kabanero/nodejs-express:0.2
      workingDir: ${inputs.resources.git-source.path}
      command:
        - /bin/bash
      args:
        - -c
        - |
          set -e
          echo "APPSODY_INSTALL:"
          echo $APPSODY_INSTALL
          echo "APPSODY_TEST:"
          echo $APPSODY_TEST
          echo "APPSODY_WATCH_DIR:"
          echo $APPSODY_WATCH_DIR
          echo "------"
          echo "My working directory is:"
          pwd
          ls
          echo "------"
          echo "I built my first Kabanero based Tekton task"
```

And the `test-build-deploy-pipeline.yaml` is a modification of the defauly pipeline:

```yaml
#Kabanero! on activate substitute CollectionId for text 'CollectionId'
apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
  name: CollectionId-test-build-deploy-pipeline
spec:
  resources:
    - name: git-source
      type: git
    - name: docker-image
      type: image
  tasks:
    - name: test-task
      taskRef:
        name: CollectionId-test-task
      resources:
        inputs:
        - name: git-source
          resource: git-source
        outputs:
        - name: docker-image
          resource: docker-image
    - name: build-task
      taskRef:
        name: CollectionId-build-task
      runAfter: [test-task]
      resources:
        inputs:
        - name: git-source
          resource: git-source
        outputs:
        - name: docker-image
          resource: docker-image
    - name: deploy-task
      taskRef:
        name: CollectionId-deploy-task
      runAfter: [build-task]
      resources:
        inputs:
        - name: git-source
          resource: git-source
        - name: docker-image
          resource: docker-image
```

In `incubator/my-nodejs-express` update `collection.yaml` to change the default pipeline

```yaml
default-image: my-nodejs-express
default-pipeline: my-nodejs-express-test-build-deploy-pipeline
images:
- id: my-nodejs-express
  image: $IMAGE_REGISTRY_ORG/my-nodejs-express:0.3
```

Unusually in these exercises, we have been using a version of appsody that is ahead of the current level baked into some the standard kabanero tekton artifacts, so there is one additional change that is needed to get around this. You need to update a tag in the common `build-task.yaml` used for all stacks in the collection. This update will select a builder container that uses appsody 0.5.3 to support the private registry semantics used in these exercises.

Open the `incubator/common/pipelines/default/build-task.yaml` file and add the tag `:0.5.3-buildah1.9.0` to the image spec in the **assemble-extract** step as shown:

```yaml
#Kabanero! on activate substitute CollectionId for text 'CollectionId'
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: CollectionId-build-task
spec:
  inputs:
    resources:
      - name: git-source
        type: git
    params:
      - name: pathToDockerFile
        default: /workspace/extracted/Dockerfile
      - name: pathToContext
        default: /workspace/extracted
  outputs:
    resources:
      - name: docker-image
        type: image
  steps:
    - name: assemble-extract
      securityContext:
        privileged: true
      image: appsody/appsody-buildah:0.5.3-buildah1.9.0
      command: ["/bin/bash"]
      args:
        - -c
        - "/extract.sh"
      env:
        - name: gitsource
          value: git-source
      volumeMounts:
        - mountPath: /var/lib/containers
          name: varlibcontainers
...
```

### 2. Re-run the scripts

Run `build.sh` and `release.sh` as before. From collections home run:

```bash
IMAGE_REGISTRY_ORG=$IMAGE_REGISTRY/$IMAGE_REGISTRY_ORG ./ci/build.sh
IMAGE_REGISTRY="" IMAGE_REGISTRY_PASSWORD="" ./ci/release.sh
```

Check the `ci/release/` folder to make sure there is a file called (your version may be different):

```ini
incubator.my-nodejs-express.v0.3.0.pipeline.custom-pipeline.tar.gz
```

And that the generated `ci/release/kabanero-index.yaml` has a section like the following:

```yaml
- default-image: my-nodejs-express
  ...
  pipelines:
  - id: custom-pipeline
    sha256: e3c3050850bf88b97c8fba728592d0cf671bb9d27b582ebaa9f90d939bfa60a5
    url: https://github.com/stevemar/collections/releases/download/0.2.1-custom/incubator.my-nodejs-express.v0.3.0.pipeline.custom-pipeline.tar.gz
```

### 3. Update the current release

Upload the changes

```bash
# Add your custom stack changes
git add -A

# Create a commit message
git commit -m "Add custom task and pipeline"

# Push the changes to your repository.  For example:
git push -u my-org
```

Navigating back to your GitHub repo, select the current custom release:

![Our own collection, version 0.2.1-custom](images/new-release-published-0.2.1-custom.png)

Clicking on the release name (0.2.1-custom) will allow you to edit the release. Click on *Edit tag*. Click on the **x** to delete existing items from the release and then upload all the files in `collections/ci/release/` which were generated from the previous steps, by clicking on the *Attach binaries...* box.

![Our own collection, page for version 0.2.1-custom](images/new-release-revise-0.2.1-custom.png)

### 4. Update the Kabanero Custom Resource

Use `oc get kabaneros -n kabanero` to obtain a list of all Kabanero CR instances in namespace `kabanero`. The default name for the CR instance is `kabanero`.

```bash
oc get kabaneros -n kabanero
```

You should see output similar to the following:

```bash
$ oc get kabaneros -n kabanero
NAME       AGE       VERSION   READY
kabanero   17d       0.1.0     True
```

Edit the specific CR instance using `oc edit kabaneros <name> -n kabanero`, replacing `<name>` with the instance name.

```bash
oc edit kabaneros kabanero -n kabanero
```

Modify your Kabanero custom resource (CR) instance to target the new collections that were pushed to the remote Github repository. The `Spec.Collections.Repositories.url` attribute should be set to the URL of the collection repository.

```yaml
apiVersion: kabanero.io/v1alpha1
kind: Kabanero
metadata:
  name: kabanero
  namespace: kabanero
spec:
  collections:
    repositories:
    - name: custom
      url: https://github.com/<username>/collections/releases/download/0.2.1-custom/kabanero-index.yaml
      activateDefaultCollections: true
```

When you are done editing, save your changes and exit the editor. The updated Kabanero CR instance will be applied to your cluster.

### 5. Test it all out

If you go to the tekton dashboard, you should see the new pipeline and task (it was added when updating the Kabanero CR by the Kabanero Operator)

![New pipelines!](images/new-pipelines.png)

Re-add the webhook, this time you should see the new pipeline as an option (without having to do `kubectl apply`). Use similar settings for the webhook as in Exercise 8:

```ini
Name: custom-stack-webhook
Repository URL: http://github.com/{username}/test-custom-stack
Access Token: <PASSWORD>

Namespace: kabanero
Pipeline: my-nodejs-express-test-build-deploy-pipeline
Service account: kabanero-operator
Docker Registry: docker-registry.default.svc:5000/kabanero
```

When the pipeline runs, it should, in addition to running the new test task, also run the build and deploy task. You can test this by making a minor update to your test-custom-stack repo. Once you have done this then re-commit to trigger the webhook by:

```bash
git add -u
git commit -m "Minor update to test pipeline"
git push -u my-org
```

Return to the Tekton *Pipeline Runs* tab to check on the status of your deployment.

![Pipeline running](images/tekton-pipeline-run-in-progress.png)

 After a while it should show that the pipeline run is complete.

![Pipeline running](images/tekton-pipeline-run-complete.png)

You can also click on the specific pipeline run link (`custom-stack-1576283066` in the above example), and see the progress of the three tasks which make up the new pipeline. Once they are all complete, it should look similar to the picture below. Notice how you can click on a given task (e.g. the new test-task we created) to see the output.

![Pipeline tasks complete](images/tekton-pipleline-tasks-complete.png)

Although the task we added was a test task, you can imagine how you might add additional tasks that are more relevant to your enterprise environment - for example to check that the application code added by the developer meets your security standards.

Once the pipeline run is complete, you can confirm that our test application, with the upgraded stack, is now running with a curl -v to the route. You should see helmet responses, e.g.:

```bash
$ curl -v http://test-custom-stack-kabanero.timro-roks1-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdom
ain.cloud/
*   Trying 172.16.17.32...
* TCP_NODELAY set
* Connected to test-custom-stack-kabanero.timro-roks1-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud (169.62.48.20) port 80 (#0)
> GET / HTTP/1.1
> Host: test-custom-stack-kabanero.timro-roks1-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud
> User-Agent: curl/7.63.0
> Accept: */*
>
< HTTP/1.1 200 OK
< X-DNS-Prefetch-Control: off
< X-Frame-Options: SAMEORIGIN
< Strict-Transport-Security: max-age=15552000; includeSubDomains
< X-Download-Options: noopen
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 34
< ETag: W/"22-E8hT/HhjJRx/5AG3TOq9TBd5R7g"
< Date: Sat, 14 Dec 2019 00:40:30 GMT
< Set-Cookie: e23eec15db4cc24793d795555bb55650=b8749458e2714fe3be1767f782a35058; path=/; HttpOnly
< Cache-control: private
<
Hello from a Custom Appsody Stack!* Connection #0 to host test-custom-stack-kabanero.timro-roks1-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud left intact
```

**Congratulations!!** You have completed Day 2 of the workshop! You have successfully created a custom appsody stack, included it in a custom Collection that also includes a custom pipeline - showing how you can adapt the standard kabanero infrastructure to suit the needs of your enterprise.
<file_sep># Exercise 8: Create a custom Tekton Task and Pipleline

In this exercise, we will show how to create Tekton artifacts, and how to plug into the existing pipelines and environment knowledge that Kabanero provides to those artifacts.

When you have completed this exercise, you will understand how to

* create a custom Task in Tekton
* create a custom Pipeline in Tekton

![Tools used during Exercise 8](images/ex8.png)

## Prerequisites

You should have already carried out the prerequisites defined in [Exercise 7](../exercise-7/README.md).

In addition, recall how to do the following from [Exercise 4](../exercise-4/README.md)

* [Access the Tekton Dashboard](../exercise-4/README.md#1-launch-the-tekton-dashboard)
* [Get a GitHub Access Token](../exercise-4/README.md#2-get-a-github-access-token)

## Steps

1. [Upload custom stack test to GitHub](#1-upload-custom-stack-test-to-github)
1. [Create a custom Tekton Task](#2-create-a-custom-tekton-task)
1. [Create a custom Tekton Pipeline](#3-create-a-custom-tekton-pipeline)
1. [Add a webhook to Tekton to watch Github for changes](#4-add-a-webhook-to-tekton-to-watch-github-for-changes)
1. [Test it all out](#5-test-it-all-out)

### 1. Upload custom stack test to GitHub

From completing the previous exercise you should now have a code base in the following folder

```bash
cd ~/appsody-apps/test-custom-stack
ls -la
```

Should result in output that looks like the following:

```bash
$ ls -la
total 144
drwxr-xr-x  14 stevemar  staff    448 19 Nov 14:20 .
drwxr-xr-x   9 stevemar  staff    288 19 Nov 16:26 ..
-rw-r--r--   1 stevemar  staff     70 15 Nov 10:02 .appsody-config.yaml
-rw-r--r--   1 stevemar  staff   1316 15 Nov 10:02 .gitignore
-rw-r--r--   1 stevemar  staff    130 15 Nov 10:12 app.js
drwxr-xr-x   2 stevemar  staff     64 15 Nov 10:03 node_modules
-rw-r--r--   1 stevemar  staff  51421 15 Nov 10:02 package-lock.json
-rw-r--r--   1 stevemar  staff    455 19 Nov 12:52 package.json
drwxr-xr-x   3 stevemar  staff     96 15 Nov 10:02 test
```

We will now upload that code to Github.

Go to <https://github.com/new> and create a new repository, `test-custom-stack`. Do not initiatize the repos with a license file or README.

From your `test-custom-stack` directory, run the commands below, replacing `<username>` with your own.

```bash
git init
git add -A
git commit -m "first commit"
git remote add my-org https://github.com:<username>/test-custom-stack.git
git push -u my-org master
```

The repo should look like this:

![Repo code base](images/repo-code-base.png)

### 2. Create a custom Tekton Task

Create a new directory to iterate on new tasks

```bash
cd ~/appsody-apps
mkdir tekton-tasks
cd tekton-tasks
```

Create a new file named `test-task.yaml` with the following contents:

```yaml
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: test-task
spec:
  inputs:
    resources:
      - name: git-source
        type: git
  steps:
    - name: test-echo
      image: kabanero/nodejs-express:0.2
      workingDir: ${inputs.resources.git-source.path}
      command:
        - /bin/bash
      args:
        - -c
        - |
          set -e
          echo "APPSODY_INSTALL:"
          echo $APPSODY_INSTALL
          echo "APPSODY_TEST:"
          echo $APPSODY_TEST
          echo "APPSODY_WATCH_DIR:"
          echo $APPSODY_WATCH_DIR
          echo "------"
          echo "My working directory is:"
          pwd
          ls
          echo "------"
          echo "I built my first Kabanero based Tekton task"
```

Run the following to create the Task in Tekton:

```bash
oc apply -f test-task.yaml -n kabanero
```

### 3. Create a custom Tekton Pipeline

Create a new file named `test-pipeline.yaml` with the following contents:

```yaml
apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
  name: test-pipeline
spec:
  resources:
    - name: git-source
      type: git
    - name: docker-image
      type: image
  tasks:
    - name: test-task
      taskRef:
        name: test-task
      resources:
        inputs:
        - name: git-source
          resource: git-source
```

Run the following to create the Pipeline in Tekton:

```bash
oc apply -f test-pipeline.yaml -n kabanero
```

### 4. Add a webhook to Tekton to watch Github for changes

From the Tekton dashboard, we need to add a new webhook. Launch a browser and access the dashboard.

To configure the GitHub webhook to your repo. Go to `Webhooks` > `Add Webhook` and then create the webhook.

![new webhook options](images/create-tekton-webhook.png)

#### Create a webhook for the backend

```ini
Name: custom-stack-webhook
Repository URL: http://github.com/{username}/test-custom-stack
Access Token: <PASSWORD>

Namespace: kabanero
Pipeline: test-pipeline
Service account: kabanero-operator
Docker Registry: docker-registry.default.svc:5000/kabanero
```

Return to your git repo and check the `Settings` > `Webhooks` panel to verify that the webhook has been added and is working.

### 5. Test it all out

In your `test-custom-stack` repo, change the file `app.js`. Change a value in the `res.send("Hello from Appsody!");` function. Then commit this change and push to your github repo, for example:

```bash
git add -u
git commit -m "test change"
git push -f -u origin master
```

This will trigger the tekton pipleine. Go to the tekton dashboard and access the new pipeline it created.

![See the custom task and custom pipeline](images/view-tasks.png)
<file_sep># Workshop Day 2

## Day 2: Customizing Stacks, Pipelines in Collections

In the second day we'll learn about the Kabanero open source project and how to productionize our applications with custom Appsody Stacks, custom Collections, and custom Tekton pipelines.

| Section | Description |
| - | - |
| **[Lecture 4: Customizing Appsody and Kabanero](https://ibm.box.com/s/kbuympaqftxswyi1aoswdlqussmqf1ba)** | Learn about the stacks and repos |
| **[Exercise 5: Customizing an existing Appsody Stack](../exercise-5/README.md)** | Create a custom stack, to be hosted in our custom repository |
| **[Exercise 6: Building a custom Collection](../exercise-6/README.md)** | Create a collection that will contain custom appsody stacks and pipelines |
| **[Exercise 7: Using a custom Collection with Appsody](../exercise-7/README.md)** | Test the developer experience with the custom appsody stack in the collection |
| **[Lecture 5: Tekton Overview](https://ibm.box.com/s/tg0f6nhs91trlzkb5pfnh5e1rdzg4wm6)** | Learn about Tekton CI/CD and how Kabanero uses it |
| **[Exercise 8: Create a custom Tekton Task and Pipleline](../exercise-8/README.md)** | Build a pipeline that will fit into a custom Collection |
| **[Exercise 9: Deploy an application with a custom Stack, custom Collection, and custom Pipeline](../exercise-9/README.md)** | Build and deploy an application using the custom stack, collection and pipelines built by the Architects' and Operators' tracks |
<file_sep># Exercise 6: Building a custom Appsody Stack Collection in Kabanero

In this exercise, we will show how to create a custom Collection, that includes the custom Appsody Stack from the previous exercise.

When you have completed this exercise, you will understand how to

* clone and host your own Collection
* modify the Collection to include a custom Appsody Stack

![Tools used during Exercise 6](images/ex6.png)

## Prerequisites

You should have already carried out the prerequisites defined in [Exercise 5](../exercise-5/README.md). In addition, you need to ensure you have the following installed on your local machine:

* yq
* python3
* pyYAML

On macOS, you can install the above with:

```bash
brew install yq
brew install python
pip install pyYAML
```

> NOTE: do not install the python yq package, it has a different syntax than the yq application and will cause the build scripts to fail.

## About custom Kabanero Repositories

By default the Kabanero open source project is configured to automatically use the latest release at [https://github.com/kabanero-io/collections](https://github.com/kabanero-io/collections).

The default collections can be modified to meet an organization's unique needs.

* Collections are categorized as either `stable`, `incubator` or `experimental` depending on the content of the collection.

  * `stable`: collections that meet a set of technical requirements.
  * `incubator`: collections that are actively being worked on to satisfy the stable criteria.
  * `experimental`: collections that are used for trying out specific capabilities or proof of concept work.

* Collections include an Appsody stack, and a Tekton pipeline.

## Steps

1. [Create a new repo to host your custom collection](#1-create-a-new-repo-to-host-your-custom-collection)
1. [Create a new image namespace to host images on OpenShift](#2-create-a-new-image-namespace-to-host-images-on-openshift)
1. [Set up a local build environment](#3-set-up-a-local-build-environment)
1. [Add our custom stack to the new collection](#4-add-our-custom-stack-to-the-new-collection)
1. [Build the new collection](#5-build-the-new-collection)
1. [Push collection images](#6-push-collection-images)
1. [Update code repo and release a new collection](#7-update-code-repo-and-release-a-new-collection)

### 1. Create a new repo to host your custom collection

Go to <https://github.com/new> and create a new repository, `collections`. Do not initiatize the repos with a license file or README.

![New repo](images/new-repo.png)

Clone the default collections repository and create a new copy of it in your GitHub organization, replacing `<username>` with your own.

```bash
cd ~/appsody-apps/
git clone https://github.com/kabanero-io/collections
cd collections
git checkout tags/0.2.1 -b 0.2.1-custom
git remote add my-org https://github.com/<username>/collections.git
git push -u my-org
```

#### About the Kabanero Repo structure

```ini
ci
├── [ files used for CI/CD of the Collections ]
experimental (or incubator, or stable)
├── common/
|   ├── pipelines/
|   |   ├── common-pipeline-1/
|   |   |       └── [pipeline files that make up a full tekton pipeline used with all collections in experimental category]
|   |   └── common-pipeline-n/
|   |           └── [pipeline files that make up a full tekton pipeline used with all collections in experimental category]
├── collection-1/
|   ├── [collection files - see collection structure below]
└── collection-n/
    └── [collection files - see collection structure below]
```

### 2. Create a new image namespace to host images on OpenShift

To actually use the stacks we need images, images will be hosted on openshift

```bash
oc new-project kabanero-noauth
oc policy add-role-to-group registry-viewer system:unauthenticated -n kabanero-noauth
```

> NOTE: You may seem a warning about this step, but it should still have worked, e.g.

```bash
oc new-project kabanero-noauth
oc policy add-role-to-group registry-viewer system:unauthenticated -n kabanero-noauth
Warning: Group 'system:unauthenticated' not found
role "registry-viewer" added: "system:unauthenticated"
```

### 3. Set up a local build environment

There are several environment variables that need to be set up. These are required in order to correctly build the collections.

> NOTE: When setting the `TRAVIS_REPO_SLUG` variable, make sure you substitute the actual github repo you created above in place of `<username>`.

```bash
export IMAGE_REGISTRY_ORG=kabanero-noauth
export IMAGE_REGISTRY_PUBLISH=true
export IMAGE_REGISTRY_USERNAME=$(oc whoami)
export IMAGE_REGISTRY_PASSWORD=$(oc whoami -t)
export TRAVIS_REPO_SLUG=<username>/collections
export TRAVIS_TAG=0.2.1-custom
```

In addition to the above, the IMAGE_REGISTRY environment variable should still be set from [Exercise 3](../exercise-3/README.md), which you can check with:

```bash
echo $IMAGE_REGISTRY
```

```bash
$ echo $IMAGE_REGISTRY
IMAGE_REGISTRY=docker-registry-default.henrycluster6-5290c8c8e5797924dc1ad5d1b85b37c0-0001.eu-gb.containers.appdomain.cloud
```

If `IMAGE_REGISTRY` is not set, then you need to set it up again, as described in [Exercise 3 - Access the internal docker registry](../exercise-3/README.md#2-access-the-internal-docker-registry)

### 4. Add our custom stack to the new collection

Now we take our custom stack from exercise 5 (recall that is was named `my-nodejs-express` and includes the `helmet` library) and copy it over to the incubator folder of our collection repo. From the `collections` repo, perform the following steps:

List the collections before the copy:

```bash
ls incubator
common java-spring-boot2 nodejs-express
java-microprofile nodejs nodejs-loopback
```

Now copy in our new custom stack:

```bash
cp -R ~/appsody-apps/my-nodejs-express incubator
```

Check that we now have this in the list of collections:

```bash
ls incubator
common java-spring-boot2 nodejs nodejs-loopback
java-microprofile my-nodejs-express nodejs-express triggers
```

Create a new file called `collection.yaml` in `collections/incubator/my-nodejs-express`, add the following:

```yaml
default-image: my-nodejs-express
default-pipeline: default
images:
- id: my-nodejs-express
  image: $IMAGE_REGISTRY_ORG/my-nodejs-express:0.3
```

Edit the new file called `stack.yaml` in `collections/incubator/my-nodejs-express`. Update the name and description fields to add "with Helmet" and also replace the maintainer information with your information if desired (you will probably see a different version, **don't** change that and keep as-is):

```yaml
name: Node.js Express with Helmet
version: 0.3.0
description: Express web framework for Node.js with Helmet
license: Apache-2.0
language: nodejs
maintainers:
  - name: <NAME>
    email: your email
    github-id: yourgithubid
default-template: simple
```

And also create a directory called `pipelines` in `collections/incubator/my-nodejs-express`, and add a single empty file called `.gitkeep`.

### 5. Build the new collection

This step builds the `kabanero-index.yaml` file.

From the collections directory, run the build script. For example:

```bash
cd ~/appsody-apps/collections
IMAGE_REGISTRY_ORG=$IMAGE_REGISTRY/$IMAGE_REGISTRY_ORG ./ci/build.sh
```

> **NOTE**: This process can take several minutes to complete.

Note that this will build all the collections in the incubator directory, including the new `my-nodejs-express` stack.

Following the build, you can find the generated collection assets in the `collections/ci/assets/` directory and all the docker images in your local docker registry.

You should see output like the following, take note of the `my-nodejs-express` stack being built, and ensure there are no errors in the output:

```bash
...
Listing all stacks
Building stacks: incubator/java-microprofile incubator/java-spring-boot2 incubator/my-nodejs-express incubator/nodejs-express incubator/nodejs-loopback incubator/nodejs
...
- BUILDING stack: incubator/my-nodejs-express
File containing output from image build: /Users/stevemar/appsody-apps/collections/ci/build/image.my-nodejs-express.0.2.8.log
created kabanero/my-nodejs-express:0.2.8
--- Created template archive: incubator.my-nodejs-express.v0.2.8.templates.scaffold.tar.gz
--- Created template archive: incubator.my-nodejs-express.v0.2.8.templates.simple.tar.gz
...
=== Testing my-nodejs-express : scaffold

~/appsody-apps/collections/ci/build/test/kabanero-index-local/my-nodejs-express/scaffold ~/appsody-apps/collections

> appsody init kabanero-index-local/my-nodejs-express scaffold

> appsody run -P --name my-nodejs-express-scaffold
...
> appsody stop --name my-nodejs-express-scaffold

Stopping development environment
Running command: docker stop my-nodejs-express-scaffold
...
> appsody build

error=0
~/appsody-apps/collections
```

### 6. Push collection images

To push the collection images, there is a release script that will do the actual pushing to the image registry. The current scripts have a minor issue with private registry login and tagging so first log in to the private registry, and then run the release script. The two commands below will achieve this:

```bash
echo $IMAGE_REGISTRY_PASSWORD | docker login -u $IMAGE_REGISTRY_USERNAME --password-stdin $IMAGE_REGISTRY
IMAGE_REGISTRY="" IMAGE_REGISTRY_PASSWORD="" ./ci/release.sh
```

You should see output like the following, take note of the `my-nodejs-express` stack being pushed to the registry, and ensure there are no errors in the output:

```bash
$ IMAGE_REGISTRY="" IMAGE_REGISTRY_PASSWORD="" ./ci/release.sh
 == Running pre_env.d scripts
 == Done pre_env.d scripts
 == Running post_env.d scripts
 == Done post_env.d scripts
...
Releasing: /Users/stevemar/appsody-apps/collections/ci/assets/incubator.my-nodejs-express.v0.2.8.templates.scaffold.tar.gz
Releasing: /Users/stevemar/appsody-apps/collections/ci/assets/incubator.my-nodejs-express.v0.2.8.templates.simple.tar.gz
...
Pushing docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express
The push refers to repository [docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express]
535ab22146d1: Layer already exists
0: digest: sha256:535ab22146d1 size: 3883
535ab22146d1: Layer already exists
0.2: digest: sha256:535ab22146d1 size: 3883
535ab22146d1: Layer already exists
0.2.8: digest: sha256:535ab22146d1 size: 3883
535ab22146d1: Layer already exists
latest: digest: sha256:535ab22146d1 size: 3883
Tagging docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.2.8
> docker tag kabanero-noauth/my-nodejs-express:0.2.8 docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.2.8
...
```

> NOTE: Since this above is pushing a lot of images, it is possible that you might see this command fail with a timeout error, or unknown blob. Retrying the command (more than once if necessary) will resolve this issue.

### 7. Update code repo and release a new collection

Once you have made all the changes to the collection and you are ready to push the changes back to your git repository then

```bash
# Add your custom stack changes
git add -A

# Create a commit message
git commit -m "Updates to the collections"

# Push the changes to your repository.  For example:
git push -u my-org
```

To create a full release of the collections, create a git tag:

```bash
git tag 0.2.1-custom -m "Custom collections, version 0.2.1-custom"
git push --tags
```

Navigating back to your GitHub repo, you should see a new release available:

![Our own collection, version 0.2.1-custom](images/new-release-0.2.1-custom.png)

Clicking on the release name (0.2.1-custom) will allow you to edit the release.

![Our own collection, page for version 0.2.1-custom](images/new-release-main-0.2.1-custom.png)

Click on *Edit tag*. and then upload all the files in `collections/ci/release/` which were generated from the previous steps, by clicking on the *Attach binaries...* box.

![Our own collection, upload files to release](images/edit-release.png)

Once you have uploaded the files you can publish your new collection by clicking *Publish release*, at the bottom of the page

![Our own collection, published](images/new-release-published-0.2.1-custom.png)

You will note that the collection includes the `kabanero-index.yaml` file we edited earlier. The url to this index file is is what you will provide appsody as a link to your new custom repository (that is contained within the new collection. You normally obtain and copy the url (depending on your browser) by right (or secondary) clicking over the `kabanero-index.yaml` item in the list of files shown in the release. It should be of the form:

`https://github.com/<username>/collections/releases/download/0.2.1-custom/kabanero-index.yaml`

You will need this url for the next exercise, where we will create an appsody application based on your new collection.

**Congratulations!!** We've just created our own custom collection that included our own custom stack. Now we need to test our released collection using Appsody. On to the next exercise.
<file_sep># Summary

[Rewrite everything below this line]

### Getting Started

* [Pre-work](pre-work/README.md)

### Day 1

* [About Day 1](about-day-1/README.md)
    * [Lecture 1: What is Cloud Native?](https://ibm.box.com/s/3pvl4jdi3xifs1olzcl9np904zvk5ueo)
    * [Lecture 2: Kabanero Overview](https://ibm.box.com/s/6jl4b7sj8xqgh7rvxtea5ykpsjyu1siz)
    * [Exercise 1: Introduction to Appsody and Codewind](exercise-1/README.md)
    * [Exercise 2: Using Appsody CLI to develop, test, and debug applications](exercise-2/README.md)
    * [Exercise 3: Deploying to OpenShift with Appsody](exercise-3/README.md)
    * [Lecture 3: Adding value with IBM Cloud Pak for Applications](https://ibm.box.com/s/y4wh104vdos1vw5kdjwwuhebf8jgq580)
    * [Exercise 4: Use Tekton and Kabanero Pipelines to continuously deploy](exercise-4/README.md)

### Day 2

* [About Day 2](about-day-2/README.md)
    * [Lecture 4: Customizing Appsody and Kabanero](https://ibm.box.com/s/kbuympaqftxswyi1aoswdlqussmqf1ba)
    * [Exercise 5: Customizing an existing Appsody Stack](exercise-5/README.md)
    * [Exercise 6: Building a custom Collection repository](exercise-6/README.md)
    * [Exercise 7: Using a custom Collection with Appsody](exercise-7/README.md)
    * [Lecture 5: Tekton Overview](https://ibm.box.com/s/tg0f6nhs91trlzkb5pfnh5e1rdzg4wm6)
    * [Exercise 8: Create a custom Tekton Task and Pipleline](exercise-8/README.md)
    * [Exercise 9: Deploy an application with a custom Stack, custom Collection, and custom Pipeline](exercise-9/README.md)

### Workshop Resources

* [Instructor Guide](instructor-guide/README.md)

### Resources

* [IBM Developer](https://developer.ibm.com)
* [Appsody Documentation](https://appsody.dev)
* [Kabanero Documentation](https://kabanero.io)
* [Codewind Documentation](https://www.eclipse.org/codewind/)
* [Cloud Pak for Applications](https://www.ibm.com/cloud/cloud-pak-for-applications)
<file_sep># Exercise 7: Using a custom collection with Appsody

In this exercise, we will show how to add the custom collection to your Appsody cli and to create an example application. Hint, this is going to be a lot like one of the exercises from day 1, but this time with your custom collection.

When you have completed this exercise, you will understand how to

* update Appsody CLI to point to a new collection
* test out the custom stack in our custom collection

![Tools used during Exercise 7](images/ex7.png)

## Prerequisites

You should have already carried out the prerequisites defined in [Exercise 6](../exercise-6/README.md).

## Steps

1. [Get the collection URL](#1-get-the-collection-url)
2. [Test the new stack and collection](#2-test-the-new-stack-and-collection)
3. [Prepare the new application for deployment](#3-prepare-the-new-application-for-deployment)

### 1. Get the collection URL

Obtain the URL to the collection repository. If a Git release was created for the collections, generally, the URL format will be: `https://<git repository>/<organization>/collections/releases/download/<release>/kabanero-index.yaml`

In our workshop, it'll likely be:

`https://github.com/<username>/collections/releases/download/0.2.1-custom/kabanero-index.yaml`

* Replace `<username>` with your Github username

### 2. Test the new stack and collection

Now that we know the URL, let's add the repo to our local appsody

```bash
appsody repo add stevemar https://github.com/stevemar/collections/releases/download/0.2.1-custom/kabanero-index.yaml
```

Create a new directory to work in:

```bash
cd ~/appsody-apps
mkdir test-custom-stack
cd test-custom-stack
```

Initialize the appsody stack

```bash
appsody init stevemar/my-nodejs-express
```

You should see output similar to the following:

```bash
$ appsody init stevemar/my-nodejs-express
Running appsody init...
Downloading my-nodejs-express template project from https://github.com/stevemar/collections/releases/download/0.2.1-custom/incubator.my-nodejs-express.v0.2.8.templates.simple.tar.gz
Download complete. Extracting files from /Users/stevemar/appsody-apps/testo/my-nodejs-express.tar.gz
...
Pulling docker image docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.2
...
docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.2 -c find /project -type f -name .appsody-init.sh
Successfully initialized Appsody project
```

Run the stack:

```bash
appsody run
```

You should see output similar to the following:

```bash
$ appsody run
Running development environment...
...
Running docker command: docker run --rm -p 3000:3000 -p 9229:9229 --name testo-dev -v /Users/stevemar/appsody-apps/testo/:/project/user-app -v testo-deps:/project/user-app/node_modules -v /Users/stevemar/.appsody/appsody-controller:/appsody/appsody-controller -t --entrypoint /appsody/appsody-controller docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.2 --mode=run
[Container] Running APPSODY_PREP command: npm install --prefix user-app && npm audit fix --prefix user-app
added 170 packages from 578 contributors and audited 295 packages in 2.989s
[Container] found 0 vulnerabilities
...
Application Metrics 5.0.3.201908230949 (Agent Core 4.0.3)
[Container] [Tue Nov 12 03:13:32 2019] com.ibm.diagnostics.healthcenter.mqtt INFO: Connecting to broker localhost:1883
[Container] App started on PORT 3000
```

Test it out with `curl` and you'll see the helmet headers.

Use the `appsody stop` command in another terminal in the same directory to stop the running application.

### 3. Prepare the new application for deployment

Before you can deploy this application using the pipeline you will be building in the upcoming exercises, there needs to be a deployment file added to the application. The deployment file is called `app-deploy.yaml`. This file was created automatically for you in day 1 exercise 3 with the `appsody deploy` command.

It can also be created without deploying an application using the `--generate-only` flag. Run this command:

```bash
appsody deploy --generate-only
```

```bash
$ appsody deploy --generate-only
Extracting project from development environment
Pulling docker image docker-registry-default.timro-roks1-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.3
Running command: docker pull docker-registry-default.timro-roks1-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.3
0.3: Pulling from kabanero-noauth/my-nodejs-express
Digest: sha256:9145b3477ca00e2401d100380460a5d813a6f291ee149d10ac7d989f1923f1e3
Status: Image is up to date for docker-registry-default.timro-roks1-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.3
Running command: docker run --name test-custom-stack-extract -v C:/Users/TimRobinson/appsody-apps/test-custom-stack/:/project/user-app --entrypoint /bin/bash docker-registry-default.timro-roks1-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/kabanero-noauth/my-nodejs-express:0.3 -c cp -rfL /project /tmp/project
...
```

After the command completes, check the contents of the application directory with `ls`

```bash
app.js  app-deploy.yaml  node_modules/  package.json  package-lock.json  test/
```

**Congratulations!!** We've got a published custom stack and it can be used for local development.
<file_sep># Workshop Day 1

[Rewrite everything below this line]

## Day 1: Kabanero and Appsody for Developers and Operators

In this first day we'll learn how to use Appsody to run the *inner loop* of the development and test cycle for a developer, and how these tools can be integrated into your favorite IDE.

We'll also explore how to deploy an application to OpenShift, first manually with Appsody for dev/test purposes, and then using the standard Kabanero Tekton piplines with GitOps as part of a continual test/production cycle.

Our goal for the end of the day is represented below. But, don't worry about it right now, we'll get there together by following the exercises step-by-step.

![Our end state when Day 1 is done](images/architecture.png)

## Agenda

| Section | Description |
| - | - |
| **[Lecture 1: What is Cloud Native?](https://ibm.box.com/s/3pvl4jdi3xifs1olzcl9np904zvk5ueo)** | Learn about the technologies that underpin Cloud Native applications |
| **[Lecture 2: Kabanero Overview](https://ibm.box.com/s/6jl4b7sj8xqgh7rvxtea5ykpsjyu1siz)** | Learn about Kabanero. An open source project to rapidly create Cloud Native applications |
| **[Exercise 1: Introduction to Appsody and Codewind](../exercise-1/README.md)** | Install the Appsody component of Kabanero into the IDE with Codewind, Learn about the developer flow, building your first application with Appsody |
| **[Exercise 2: Using Appsody CLI to develop, test, and debug applications](../exercise-2/README.md)** | Use the Appsody CLI to quickly create frontend and backend applications for a sample application using two different technologies (Spring and nodejs express) |
| **[Exercise 3: Deploying to OpenShift with Appsody](../exercise-3/README.md)** | Deploy the built applications to IBM Managed OpenShift with Appsody for dev/test purposes |
| **[Lecture 3: Adding value with IBM Cloud Pak for Applications](https://ibm.box.com/s/y4wh104vdos1vw5kdjwwuhebf8jgq580)** | Learn about how IBM Cloud Pak for Applications bundles everything together |
| **[Exercise 4: Use Tekton and Kabanero Pipelines to continuously deploy](../exercise-4/README.md)** | Deploy the built applications to IBM Managed OpenShift using GitOps to trigger a Tekton pipeline |
<file_sep># Instructor guide

The content in this section is specifically made for instructors hosting this workshop.

## Logging into Tekton UI

To log into the Tekton UI, you need to authenticate with the OpenShift cluster is was deployed on. This means that if we spin up 1 or N clusters for every workshop participant, then they all need to be added to the a single IBM Cloud account beforehand, and consequently must have an IBM ID, too.

## Pushing to the OpenShift registry times out

It's possible that when deploying a large image to OpenShift's internal registry, the deployment times out, with an error message like this:

```bash
$ appsody deploy --tag $DOCKER_REGISTRY/insurance-quote/quote-backend:v1 --push --namespace insurance-quote
...
[Docker] Successfully built 89b220cafd4a
[Docker] Successfully tagged docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/insurance-quote/quote-backend:1
Built docker image docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/insurance-quote/quote-backend:1
Using applicationImage of: docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/insurance-quote/quote-backend:1
Pushing docker image docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/insurance-quote/quote-backend:1
[Error] Could not push the image: exit status 1 The push refers to repository [docker-registry-default.cp4apps-workshop-prop-5290c8c8e5797924dc1ad5d1b85b37c0-0001.us-east.containers.appdomain.cloud/insurance-quote/quote-backend]
9176e849956e: Preparing
25311323356d: Preparing
c7a362505ffd: Layer already exists
4487d7944a84: Layer already exists
9176e849956e: Pushed
34c263b82e37: Retrying in 5 seconds
```

To get around this issue, the IOPS (input/output operations per second) should be upgraded past the default (0.25). To do this, follow this guide (or see the [Official Documentation](https://cloud.ibm.com/docs/openshift?topic=openshift-file_storage#file_change_storage_configuration)).

First, find the OpenShift volume claim ID for `registry-backing`. To do this run:

```bash
oc get pvc --all-namespaces
```

You should see output similar to the snippet below, in this case the volume claim ID is `pvc-82769bfa-f694-11e9-bf77-faf40b1b6da7`.

```bash
$ oc get pvc --all-namespaces
NAMESPACE   NAME                                         STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS        AGE
default     registry-backing                             Bound     pvc-82769bfa-f694-11e9-bf77-faf40b1b6da7   20Gi       RWX            ibmc-file-bronze    4d
```

Use `oc describe pv` to find the volume ID (this is different than the volume claim ID), and can be found in the `Labels` section of the output. In this case it is `102411628`.

```bash
$ oc describe pv pvc-82769bfa-f694-11e9-bf77-faf40b1b6da7 --namespace default
Name:            pvc-82769bfa-f694-11e9-bf77-faf40b1b6da7
Labels:          CapacityGb=20
                 Datacenter=wdc04
                 Iops=2
                 StorageType=ENDURANCE
                 Username=IBM02SEV1323471_4322
                 billingType=hourly
                 failure-domain.beta.kubernetes.io/region=us-east
                 failure-domain.beta.kubernetes.io/zone=wdc04
                 path=IBM02SEV1323471_4322data01
                 server=fsf-wdc0401h-fz.service.softlayer.com
                 volumeId=102411628
Annotations:     ibmFileProvisionerIdentity=8d6ff3ed-f695-11e9-9ecf-66243bd50687
                 pv.kubernetes.io/provisioned-by=ibm.io/ibmc-file
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    ibmc-file-bronze
Status:          Bound
Claim:           default/registry-backing
Reclaim Policy:  Delete
Access Modes:    RWX
Capacity:        20Gi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    fsf-wdc0401h-fz.service.softlayer.com
    Path:      /IBM02SEV1323471_4322/data01
    ReadOnly:  false
Events:        <none>
```

Lastly, use the `ibmcloud sl file` command to modify the volume to have 10 IOPS. This may take several minutes to complete.

```bash
$ ibmcloud sl file volume-modify 102411628 --new-tier 10
This action will incur charges on your account. Continue?> y
```

Alternatively, should the CLI fail, modifying the volume can be done be accomplished from the IBM Cloud Dashboard. Navigate to <https://cloud.ibm.com/classic/storage/file/{id}>, where {id} is the volume ID. Choose to modify the volume, and choose an IOPS of 10.

![Modify the volume's IOPS](images/volume-iops.png)
<file_sep># Cloud Pak for Integration Workshop

[Rewrite everything below this line]

> The workshop is hosted at: [https://cloudpakforapplications.gitbook.io/workshop/](https://cloudpakforapplications.gitbook.io/workshop)

!["cp4apps"](workshop/.gitbook/images/cp4apps.png)
<file_sep># Pre-work

This section is broken up into the following steps:

1. [Accounts](#1-accounts)
1. [Install Docker](#2-install-docker)
1. [Install Appsody CLI](#3-install-appsody-cli)
1. [Install OpenShift CLI](#4-install-appsody-cli)
1. [Check access to OpenShift cluster](#5-check-access-to-openshift-cluster)
1. [Ensure Cloud Pak for Applications is installed](#6-ensure-cloud-pak-for-applications-is-installed)
1. [Install VS Code](#7-install-vs-code) (Optional)

## 1. Accounts

The following accounts are needed for this workshop:

* [Github](https://github.com)
* [IBM Cloud](https://cloud.ibm.com)

## 2. Install Docker

A key part of the Appsody development experience is Rapid Local Development Mode, where the code you develop is continuously being run in a local docker container. If you don't already have Docker installed on your machine, you should do so now:

* [Install Docker for macOS](https://docs.docker.com/docker-for-mac/install/)
* [Install Docker for Windows](https://docs.docker.com/docker-for-windows/install/)

If you are not familiar with Docker Desktop, you can learn more about the orientation and setup [here](https://docs.docker.com/get-started/#Install-Docker-Desktop).

```bash
docker version
```

You should see output similar to the following:

```bash
$ docker version
Client: Docker Engine - Community
 Version:           19.03.4
...
```

## 3. Install Appsody CLI

Appsody includes a CLI which allows you to manage you stack based development (this workshop requries version 0.5.0 or later). This should be installed on your development machine:

[Install the Appsody CLI](https://appsody.dev/docs/getting-started/installation)

You can check that you have the correct version installed by executing the command:

```bash
appsody version
```

You should see output similar to the following:

```bash
$ appsody version
appsody 0.5.3
```

## 4. Install OpenShift CLI

The OpenShift CLI allows you to manage OpenShift resources from a terminal (this workshop requries version 3.11 (**not 4.x**)). This should be installed on your development machine:

[Install the OpenShift CLI](https://www.okd.io/download.html)

You can check that you have the correct version installed by executing the command:

```bash
oc version
```

You should see output similar to the following:

```bash
$ oc version
oc v3.11.0+0cbc58b
...
```

## 5. Check access to OpenShift cluster

You should already have been provided with details of a managed OpenShift cluster that has been provisioned for you. Check you have access to this by using the `oc login` with the credentials the workshop administrator provides to you.

```bash
oc login <url> --token=<token>
```

## 6. Ensure Cloud Pak for Applications is installed

Cloud Pak for Applications should already have been installed in your managed OpenShift cluster. Check you have access to this by ensuring that *Cloud Pak for Applications* exists in the *Application Console* of your OpenShift cluster.

![Cloud Pak for Applications in your OpenShift cluster](images/oc-console-kabanero.png)

When clicked, the *Cloud Pak for Applications* landing page should appear:

![Cloud Pak for Applications Overview](images/cpa-console.png)

Clicking on the *Instance* menu link will show you information about Kabanero.

![Tools available in Kabanero](images/kabanero-console.png)

## 7. Install VS Code

VS Code and its Codewind extension are used in a number of steps in [Exercise 1](../exercise-1/README.md). Although it is possible to comlete the workshop without these steps, we recommend you install VSCode on your machine if you don't have it already, using the following link:

[Install VSCode](https://code.visualstudio.com/download)


text:
<repo_name>irheza/logkom1<file_sep>/TugasLogkom/src/logic/CNFMaker.java
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package logic;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

/**
 *
 * @author immanuel.rhesa
 */
public class CNFMaker {

    Graph graph;
    int jumlahvariabel;

    public CNFMaker(Graph graph, int jumlahvariabel) {
        this.graph = graph;
        this.jumlahvariabel = jumlahvariabel;
    }

    public int getJumlahVariabel() {
        return jumlahvariabel;
    }

    public ArrayList<int[]> makeCNF() {
        ArrayList<Edge> terhubung = graph.getEdgeList();
        ArrayList<int[]> listClause = new ArrayList<int[]>();
        HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();
        Set<Integer> set = new TreeSet<>();

        for (Edge e : terhubung) {
            set.add(e.r1);
            set.add(e.r2);
        }

        Iterator it = set.iterator();

        for (int k = 0; it.hasNext(); k++) {
            int key = (Integer) it.next();
            hm.put(key, k);
            System.out.println("Key: " + key + " value: " + k);
        }

        for (int i = 0; i < terhubung.size(); i++) {
            int a = terhubung.get(i).r1;
            int b = terhubung.get(i).r2;
            int a0 = 2*hm.get(a)+1;
            int a1 = a0+1;
            int b0 = 2*hm.get(b)+1;
            int b1 = b0+1;

            int[] clause1 = {-a0, -b0, -a1, -b1};
            int[] clause2 = {-a0, -b0, a1, b1};
            int[] clause3 = {a0, b0, -a1, -b1};
            int[] clause4 = {a0, b0, a1, b1};

            listClause.add(clause1);
            listClause.add(clause2);
            listClause.add(clause3);
            listClause.add(clause4);
        }
        return listClause;
    }
}
<file_sep>/TugasLogkom/src/main/Runner.java
package main;

import gui.MainGUI;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.sat4j.specs.ContradictionException;
import org.sat4j.specs.TimeoutException;

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author <NAME>
 */
public class Runner {
    public static String drivePilihan;
    public static void main(String[] args) throws ContradictionException, TimeoutException {
        Object[] possibilities = {"D:", "E:", "X:"};

        JFrame jFrame = new JFrame();
        drivePilihan = (String)JOptionPane.showInputDialog(
                    jFrame,
                    "Pilih lokasi penyimpanan file Minisat:\n",
                    "Customized Dialog",
                    JOptionPane.PLAIN_MESSAGE,
                    null,
                    possibilities,
                    "D:");
        if(drivePilihan==null)
        {
            System.exit(0);
        }

        jFrame.setTitle("Tugas 1 Logika Komputasional");
        jFrame.setSize(1000, 700);
        jFrame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        MainGUI mg = new MainGUI();
        jFrame.getContentPane().add(mg);
        jFrame.setLocationRelativeTo(null);
        jFrame.setVisible(true);
    }
}<file_sep>/TugasLogkom/nbproject/private/private.properties
compile.on.save=true
file.reference.org.sat4j.core-src.jar=C:\\Users\\<NAME>\\Documents\\GitHub\\logkom1\\org.sat4j.core-src.jar
file.reference.org.sat4j.core-src.jar-1=C:\\Users\\<NAME>\\Documents\\GitHub\\logkom1\\org.sat4j.core-src.jar
file.reference.org.sat4j.core.jar=C:\\Users\\<NAME>\\Documents\\GitHub\\logkom1\\org.sat4j.core.jar
file.reference.org.sat4j.core.jar-1=C:\\Users\\<NAME>\\Documents\\GitHub\\logkom1\\org.sat4j.core.jar
user.properties.file=C:\\Users\\<NAME>\\AppData\\Roaming\\NetBeans\\8.0\\build.properties
<file_sep>/TugasLogkom/src/logic/Minisat.java
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package logic;

import java.util.ArrayList;

/**
 *
 * @author <NAME>
 */
public class Minisat {
    CNFMaker cnf;
    ArrayList<int[]> clauses;

    public Minisat(CNFMaker cnf){
        this.cnf = cnf;
        clauses = cnf.makeCNF();
    }

    public void addClause(int[] clause){
        clauses.add(clause);
    }

    public int[] negated(int[] clause){
        int[] newClause = new int[clause.length-1];

        for (int i = 0; i < clause.length-1; i++){
                newClause[i] = clause[i] * -1;
        }
        return newClause;
    }

    public int[] solve() throws Exception{
          MinisatInputMaker minisat = new MinisatInputMaker();

          int[]value = minisat.runAllMinisat(clauses, cnf.getJumlahVariabel());
          if(!minisat.checkSAT())
          {
              throw new Exception("This problem is unsolvable.");
          }
        return value;
    }
}<file_sep>/TugasLogkom/src/gui/EncodedColor.java
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package gui;

import java.awt.Color;

/**
 *
 * @author <NAME>
 */
public class EncodedColor {
    int bit0;
    int bit1;

    public EncodedColor(int bit0, int bit1){
        this.bit0 = bit0;
        this.bit1 = bit1;
    }
    public Color getColor(){
        // 00 untuk merah
        if (bit0 < 0 && bit1 < 0){
            return Color.RED;
        }
        // 01 untuk kuning
        else if (bit0 < 0 && bit1 > 0){
            return Color.YELLOW;
        }
        // 10 untuk hijau
        else if (bit0 > 0 && bit1 < 0){
            return Color.GREEN;
        }
        // 11 untuk biru
        else{
            return Color.BLUE;
        }
    }
    @Override
    public String toString(){
        // 00 untuk merah
        if (bit0 < 0 && bit1 < 0){
            return "merah";
        }
        // 01 untuk kuning
        else if (bit0 < 0 && bit1 > 0){
            return "kuning";
        }
        // 10 untuk hijau
        else if (bit0 > 0 && bit1 < 0){
            return "hijau";
        }
        // 11 untuk biru
        else{
            return "biru";
        }
    }
}

branch_name: refs/heads/master

languages: Java INI

repo_language: Java