Skip to content
This repository has been archived by the owner on Jun 1, 2022. It is now read-only.

Add Kind class job #20

Merged
merged 8 commits into from
Sep 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions out/index.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./github";
export * from "./kind";
1 change: 1 addition & 0 deletions out/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion out/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions out/kind.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Job } from "@brigadecore/brigadier";
export declare const kindJobImage = "radumatei/golang-kind:1.11-0.4";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still want to host this image (and repo containing Dockerfile) in bridagecore? I'd be fine with a follow-up ticket/PR, just wanted to make a note.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd like to gather all things in the same place, but I wouldn't hold this PR for it.

export declare class KindJob extends Job {
constructor(name: string, image?: string);
}
77 changes: 77 additions & 0 deletions out/kind.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions out/kind.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@brigadecore/brigade-utils",
"version": "0.2.0",
"version": "0.3.0",
"description": "Various utility classes and functions to use in brigade.js",
"main": "out/index.js",
"types": "out/index.d.ts",
Expand All @@ -24,7 +24,7 @@
"typescript": "^3.2.2"
},
"dependencies": {
"@brigadecore/brigadier": "^0.4.0"
"@brigadecore/brigadier": "^0.6.1"
},
"author": "The Brigade Authors",
"license": "MIT"
Expand Down
49 changes: 46 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ To add this package, [use the `brigade.json` file][brigade-json] in the reposito
```json
{
"dependencies": {
"@brigadecore/brigade-utils": "0.2.0"
"@brigadecore/brigade-utils": "0.3.0"
}
}
```
Expand All @@ -28,7 +28,7 @@ const { Check } = require("@brigadecore/brigade-utils");
const projectName = "brigade-utils";
const jsImg = "node:12.3.1-stretch";

function build(e, project) {
function build(event, project) {
var build = new Job(`${projectName}-build`, jsImg);

build.tasks = [
Expand Down Expand Up @@ -58,6 +58,47 @@ This script is one that can be used to build this repository.

> Note: `Check.handleIssueComment` currently only handles `/brig run` comments - to add your own, see implementation for this method.

## The Kind library

[Kind][kind] (Kubernetes in Docker) is a tool for creating a local Kubernetes cluster using Docker containers as nodes, and it is a very fast and convenient way of setting up a Kubernetes cluster for testing.

But while [setting it up locally is straightforward][kind-getting-started], running a Kind cluster inside your Kubernetes cluster (for various end-to-end testing scenarios) is rather difficult. This library abstracts all that, and creating and using a cluster can be easily achieved with Brigade:

```js
const { KindJob } = require("@brigadecore/brigade-utils");

function e2e(event, project) {
let kind = new KindJob("kind");
kind.tasks.push(
// add your end-to-end tests
"kubectl get pods --all-namespaces"
);

return kind;
}

events.on("exec", e2e);
```

The `KindJob` class already configures the environment for a 1-node Kind cluster through Brigade:

- marks the job as privileged
- mounts all necessary volumes for Kind to work properly (see [this Kind issue](https://github.com/kubernetes-sigs/kind/issues/303#issuecomment-518593664))
- starts Docker in Docker
- creates a 1-node Kind cluster
- exports the `KUBECONFIG` environment variable to point to the newly created cluster
- unsets the Kubernetes environment variables that point to the host cluster
- ensures the cluster deletion command is always executed as cleanup

Notes:

- in order to start properly, ensure the Brigade project allows privileged jobs and allows host mounts (see [this issue](https://github.com/kubernetes-sigs/kind/issues/303#issuecomment-518593664))
- this is an experimental configuration, which mounts host directories inside a privileged pod - before deploying this at scale on a production cluster, monitor your environment for any resource leaks. We do ensure to delete clusters created with the default configuration (using Linux traps), but overriding that can lead to damage to your cluster (see [this issue](https://github.com/kubernetes-sigs/kind/issues/759)).
- this job runs as a privileged pod in your cluster
- the default image used contains `docker`, `go`, `kind`, `git`, `wget`, `apk` - you can supply your own, or you can use `apk`, or download other tools you might need.
- if overriding the default configuration, ensure you are cleaning up clusters created to avoid resource leaks. See [how tasks are configured](./src/kind.ts).
- the default timeout for Brigade jobs is 15 minutes - by default, the `KindJob` sets the timeout to 30 minutes, and you can configure it by setting the `job.timeout` property - and keep in mind the value is in milliseconds.

# Contributing

This Brigade project accepts contributions via GitHub pull requests. Prerequisites to build and test this library:
Expand Down Expand Up @@ -91,4 +132,6 @@ for how this is done.
[local-deps]: https://docs.brigade.sh/topics/dependencies/#using-local-dependencies-from-the-project-repository
[npm]: https://www.npmjs.com/package/@brigadecore/brigade-utils
[checks-api]: https://developer.github.com/v3/checks/
[gh-app]: https://github.com/brigadecore/brigade-github-app
[gh-app]: https://github.com/brigadecore/brigade-github-app
[kind]: https://github.com/kubernetes-sigs/kind
[kind-getting-started]: https://kind.sigs.k8s.io/docs/user/quick-start/
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./github";
export * from "./kind";
79 changes: 79 additions & 0 deletions src/kind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Job } from "@brigadecore/brigadier";

export const kindJobImage = "radumatei/golang-kind:1.11-0.4";
vdice marked this conversation as resolved.
Show resolved Hide resolved

export class KindJob extends Job {
constructor(name: string, image?: string) {
if (image == undefined) {
image = kindJobImage;
}
super(name, image);

// kind needs to run as a privileged pod
this.privileged = true;
vdice marked this conversation as resolved.
Show resolved Hide resolved
vdice marked this conversation as resolved.
Show resolved Hide resolved

// since the cluster creation takes some time,
// set the timeout to 30 minutes
this.timeout = 180000;
radu-matei marked this conversation as resolved.
Show resolved Hide resolved

// kind needs to add the following volumeMounts to function properly
// the Brigade project must enable `allowHostMounts`
this.volumes = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm super curious as to what each volume mount enables -- could we add a note to each?

{
name: "modules",
hostPath: {
path: "/lib/modules",
type: "Directory"
}
},
{
name: "cgroup",
hostPath: {
path: "/sys/fs/cgroup",
type: "Directory"
}
},
{
name: "docker-graph-storage",
emptyDir: {}
}
];
this.volumeMounts = [
{
name: "modules",
mountPath: "/lib/modules",
readOnly: true
},
{
name: "cgroup",
mountPath: "/sys/fs/cgroup"
},
{
name: "docker-graph-storage",
mountPath: "/var/lib/docker"
}
];

// to add your own tasks to this job, use job.tasks.push()
this.tasks = [
vdice marked this conversation as resolved.
Show resolved Hide resolved
// when the pod finishes, regardless of the exit code, delete the cluster
// to avoid resource leaks
// see https://github.com/kubernetes-sigs/kind/issues/759
"trap 'kind delete cluster' EXIT",
"dockerd-entrypoint.sh &",
"sleep 20",
"kind create cluster --wait 300s",
`export KUBECONFIG="$(kind get kubeconfig-path)"`,
// this pod is running inside a Kubernetes cluster
// unset environment variables pointing to the host cluster
// even if the service account is limited, and KUBECONFIG is properly set,
// some operations might still point to the in-cluster configuration
"unset $(env | grep KUBERNETES_ | xargs)",
"kubectl cluster-info",
"kubectl get pods --all-namespaces",
// even though we're using the --wait flag for kind,
// some pods (the DNS pods, for example) are not ready yet.
"sleep 60"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kubectl wait -n kube-system --for=condition=Ready -l k8s-app=kube-dns pods

];
}
}
2 changes: 1 addition & 1 deletion test/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe("when creating a new GitHub notification", () => {
});
});

describe("when creating a new check", () => {
describe("when creating a new GitHub check", () => {
it("all properties are properly instantiated", () => {
let project = mock.mockProject();
let event = mock.mockEvent();
Expand Down
52 changes: 52 additions & 0 deletions test/kind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import "mocha";
import { assert } from "chai";
import { KindJob, kindJobImage } from "../src/kind";

describe("when creating a new Kind job", () => {
it("without an image for the job, the default image is used", () => {
let kind = new KindJob("kind");

assert.equal(kind.name, "kind");
assert.equal(kind.image, kindJobImage);
});

it("and an image is passed, the image is used", () => {
let kind = new KindJob("kind", "my-custom-kind-image");

assert.equal(kind.name, "kind");
assert.equal(kind.image, "my-custom-kind-image");
});

it("job is privilged, with right number of pre-defined tasks", () => {
let kind = new KindJob("kind");

assert.isTrue(kind.privileged);
assert.equal(kind.tasks.length, 9);
});

it("all volumes are properly set", () => {
let kind = new KindJob("kind");
assert.equal(kind.volumeMounts.length, 3);

assert.equal(kind.volumes[0].name, "modules");
assert.equal(kind.volumes[0].hostPath!.path, "/lib/modules");
assert.equal(kind.volumes[0].hostPath!.type, "Directory");
assert.equal(kind.volumes[1].name, "cgroup");
assert.equal(kind.volumes[1].hostPath!.path, "/sys/fs/cgroup");
assert.equal(kind.volumes[1].hostPath!.type, "Directory");
assert.equal(kind.volumes[2].name, "docker-graph-storage");
assert.deepEqual(kind.volumes[2].emptyDir!, {});
});

it("all volume mounts are properly set", () => {
let kind = new KindJob("kind");

assert.equal(kind.volumeMounts.length, 3);
assert.equal(kind.volumeMounts[0].name, "modules");
assert.equal(kind.volumeMounts[0].mountPath, "/lib/modules");
assert.equal(kind.volumeMounts[1].name, "cgroup");
assert.equal(kind.volumeMounts[1].mountPath, "/sys/fs/cgroup");
assert.equal(kind.volumeMounts[2].name, "docker-graph-storage");
assert.equal(kind.volumeMounts[2].mountPath, "/var/lib/docker");
});
});
Loading