Skip to content

Commit

Permalink
Example of deploying the operator with Flux source controller (#339)
Browse files Browse the repository at this point in the history
* Document Flux+Pulumi example

This has two Pulumi projects:

 - the top directory (examples/flux-source) is for deploying the Pulumi
   Kubernetes operator along with the Flux source controller

 - the subdirectory app/ has an example a a Stack object refering to a
   Flux source.

* Include webhook receiver in app stack

 - include Flux's notification-controller when installing Flux
 - create a webhook receiver object

This parameterises the git URL used for the GitRepository source, so you
must supply your own. (People won't be able to install webhooks in my
repo!)

* Supply an example of deploying ngrok

You can use ngrok to tunnel webhooks through to a local or otherwise
private cluster. This gives an example of deploying it which is enough
to make the webhook in ../app work.

* Mention Flux example in changelog

Signed-off-by: Michael Bridgen <[email protected]>
  • Loading branch information
squaremo authored Oct 24, 2022
1 parent 8590ef9 commit b7f30cc
Show file tree
Hide file tree
Showing 13 changed files with 615 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ CHANGELOG

- Make .ContinueResyncOnCommitMatch apply to all sources (git, Flux sources, or Program objects)
[#346](https://github.com/pulumi/pulumi-kubernetes-operator/pull/346)
- Give an example of using this operator with a Flux GitRepository and webhooks, in
`examples/flux-source`.
[#339](https://github.com/pulumi/pulumi-kubernetes-operator/pull/339)

## 1.10.0-rc.1 (2022-10-18) (release candidate)

Expand Down
3 changes: 3 additions & 0 deletions examples/flux-source/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: flux-and-pulumi
runtime: nodejs
description: Deploy Pulumi and Flux together
136 changes: 136 additions & 0 deletions examples/flux-source/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Example of using the Pulumi Kubernetes operator with Flux

The Pulumi operator can use [Flux sources][] for getting Pulumi programs. This example shows how to
set the operator, and Flux source controller, up; and, how to create a `GitRepository` (a Flux
source) and a `Stack` object that points to it.

## Running the installation program

The project in this directory will:

- install the Flux source CRDs and controller
- install the Pulumi operator and its CRD(s)
- give the Pulumi operator permissions to see Flux source objects

You'll need to install the program dependencies:

```console
flux-source$ npm install
```

Now you can run the stack with the Pulumi command-line:

```console
flux-source$ pulumi up
```

## Running the example app Stack

You will need to install the dependencies for the program:

```console
app$ npm install
```

For the project in `app/`, you'll need a Pulumi access token. You can create one in the [Pulumi
service][] under "Access tokens". Assign it to the environment variable
`PULUMI_ACCESS_TOKEN`.

You will also need to supply a git repository URL. You can use my example:
`https://github.com/squaremo/pko-dev`, but you won't be able to install webhooks there, so it might
pay to fork that or create your own. (If you do create your own, you will likely want to change the
`dir` in the Stack spec, which gives the subdirectory of the repo where your Pulumi code is).

To run the program:

```console
app$ pulumi config set git-url https://github.com/squaremo/pko-dev
app$ pulumi config set stack-name $(pulumi whoami)/app/dev
app$ pulumi up
```

This creates a `GitRepository` object in the cluster, which you can examine:

```console
app$ kubectl get gitrepository
```

and a `Stack` object, pointing at the `GitRepository` object, which will run the program and create
a deployment. You watch the stack object until it has completed:

```console
app$ kubectl get stack -w
```

You should see the state become `succeeded` in a minute or so. The result of the stack is a
deployment:

```console
app$ kubectl get deployment
```

You can also look in the [Pulumi service][] to see what it creates.

## Webhooks

The program in `app/` also creates a webhook receiver, so you can get notifications from GitHub when
there are new commits pushed. You will need to expose the webhook service to the internet for this
to work: either use the guide in
https://fluxcd.io/flux/guides/webhook-receivers/#expose-the-webhook-receiver, or use something like
ngrok to tunnel the webhook into a private cluster. If you use the Flux guide, be aware that the
webhook receiver here is in `default` namespace, rather than `flux-system`. There's a working
example for ngrok in `ngrok/`.

### Using ngrok to tunnel webhooks

The directory `ngrok/` has a program that will run ngrok in such a way as to make the webhooks
work. It doesn't need any configuration; just:

```console
ngrok$ pulumi up
```

To see the public hostname that it's set up, access the ngrok console:

```console
ngrok$ NGROK_SERVICE=$(pulumi stack output service)
ngrok$ kubectl port-forward service/$NGROK_SERVICE 4040:80
```

The ngrok console will then be reachable on http://localhost:4040/.

Bear in mind that the tunnel hostname will change every time this restarts, so it's only really
useful for (shortlived) demos.

## Installing development versions

You can use the install program to run a locally-built image, with the following steps.

* install the CRD over the top of that installed by the stack

```console
pulumi-kubernetes-operator$ make install-crds
```

* build a development version of the image:

```console
pulumi-kubernetes-operator$ make build-image IMAGE_NAME=pulumi/pulumi-kubernetes-operator
```

(`IMAGE_NAME` means it won't name the image after `$(whoami)`)

> If you're using `kind` to run a local Kubernetes cluster, you will need to side-load the image:
>
> ```console
> kind load docker-image pulumi/pulumi-kubernetes-operator:$(git rev-parse --short HEAD)
> ```
* use that image's tag as the `operator-version` configuration item when running the stack:
```console
pulumi-kubernetes-operator/examples/flux-source$ pulumi up -c operator-version=$(git rev-parse --short HEAD)
```
[Flux source]: https://fluxcd.io/flux/components/source/api/
[Pulumi service]: https://app.pulumi.com/
2 changes: 2 additions & 0 deletions examples/flux-source/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin/
/node_modules/
2 changes: 2 additions & 0 deletions examples/flux-source/app/Pulumi.dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
config:
app:git-url: https://github.com/squaremo/pko-dev
3 changes: 3 additions & 0 deletions examples/flux-source/app/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: app
description: Application stack for Flux+Pulumi
runtime: nodejs
115 changes: 115 additions & 0 deletions examples/flux-source/app/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";
import * as random from "@pulumi/random";

const config = new pulumi.Config();
export const gitURL = config.require('git-url');
export const stackName = config.require('stack-name');

// A git repository source for our Pulumi program
const gitRepo = new k8s.apiextensions.CustomResource("pko-dev", {
apiVersion: 'source.toolkit.fluxcd.io/v1beta2',
kind: 'GitRepository',
metadata: {
'namespace': 'default',
},
spec: {
interval: '5m0s',
url: gitURL,
ref: { branch: 'main' },
},
});

const pulumiToken = process.env['PULUMI_ACCESS_TOKEN'];
if (!pulumiToken) {
throw new Error('This stack needs a Pulumi access token in the environment variable PULUMI_ACCESS_TOKEN')
}

// A secret with the Pulumi access token, taken from the environment.
const tokenSecret = new k8s.core.v1.Secret("pulumi-token", {
stringData: {
'PULUMI_ACCESS_TOKEN': pulumiToken,
},
});

const stack = new k8s.apiextensions.CustomResource("basic", {
apiVersion: 'pulumi.com/v1',
kind: 'Stack',
metadata: {
'namespace': 'default',
},
spec: {
stack: stackName,
envRefs: {
'PULUMI_ACCESS_TOKEN': {
'type': 'Secret',
'secret': {
key: 'PULUMI_ACCESS_TOKEN',
name: tokenSecret.metadata.name,
},
},
},
fluxSource: {
sourceRef: {
apiVersion: gitRepo.apiVersion,
kind: gitRepo.kind,
name: gitRepo.metadata.name,
},
dir: 'basic',
},
refresh: true,
},
});

// Using webhooks: this follows the guide at https://fluxcd.io/flux/guides/webhook-receivers/.

// This program will go as far as creating the receiver in the cluster. You will need to

// - expose that to the internet, either by creating an Ingress or LoadBalancer (explained in the
// Flux documentation linked above), or using something like ngrok
// (https://hub.docker.com/r/ngrok/ngrok).
//
// - install the webhook in the GitHub repository you're syncing from.

// GitHub webhooks need a shared secret.

export const token = pulumi.secret(new random.RandomString("webhook-token", {
length: 40,
special: false,
}).result);

const webhookSecret = new k8s.core.v1.Secret("webhook-token", {
metadata: {
namespace: 'default',
},
stringData: { token },
});

const receiver = new k8s.apiextensions.CustomResource("app-hook", {
apiVersion: 'notification.toolkit.fluxcd.io/v1beta1',
kind: 'Receiver',
metadata: {
namespace: 'default',
},
spec: {
type: 'github',
events: ["ping", "push"],
secretRef: {
name: webhookSecret.metadata.name,
},
resources: [{
kind: 'GitRepository',
name: gitRepo.metadata.name,
}],
},
});

export const readme = pulumi.unsecret(pulumi.interpolate `
The GitRepository, Stack and webhook receiver are set up. Get the webhook path with
kubectl get receiver/${receiver.metadata.name}
The token output has the secret for using as a webhook secret. Install the webhook at
${gitURL}/settings/hooks, using the public host you've set up, the path from the receiver status,
and the token.
`);
13 changes: 13 additions & 0 deletions examples/flux-source/app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "app",
"main": "index.ts",
"devDependencies": {
"@types/node": "^14"
},
"dependencies": {
"@pulumi/kubernetes": "^3.0.0",
"@pulumi/kubernetesx": "^0.1.5",
"@pulumi/pulumi": "^3.0.0",
"@pulumi/random": "^4.8.2"
}
}
18 changes: 18 additions & 0 deletions examples/flux-source/app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.ts"
]
}
Loading

0 comments on commit b7f30cc

Please sign in to comment.