Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of deploying the operator with Flux source controller #339

Merged
merged 11 commits into from
Oct 24, 2022
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' },
Copy link
Contributor

Choose a reason for hiding this comment

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

Worth showing off any of the value add here? (Features we get via Flux that aren’t available directly?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, somewhere. This example is a "how to", to show people how to get it working at all, and (secondarily) for them to adapt to their needs. Putting fancy stuff in would obscure that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would cool to do more examples that show off e.g.,

  • using OCI images with cosign verification
  • following a semver tag
  • using "ignore" to filter down the files made available
  • using webhooks for notifications (at least once watching sources is implemented in the operator)

},
});

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