Skip to content
This repository has been archived by the owner on Aug 4, 2021. It is now read-only.

Commit

Permalink
More docs improvements and added deploy-example script
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-blunden committed May 28, 2021
1 parent 47d83b7 commit 5f06083
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 88 deletions.
115 changes: 67 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,119 +1,138 @@
# Doppler Kubernetes Controller (experimental)

The Doppler Kubernetes controller automatically syncs secret changes from Doppler to Kubernetes secrets with the ability to reload deployments on secret change.
Automatically sync secrets from Doppler to Kubernetes with auto-reload of Deployments when secrets change.

## Installation
![Doppler Kubernetes Controller Diagram](https://user-images.githubusercontent.com/133014/119946348-e2465280-bfd9-11eb-8d72-34afebbb538c.png)

## Step 1. Deploying the Doppler Controller

Deploy the controller by running:

```bash
kubectl apply -f doppler-crd-controller.yml
kubectl rollout status -w deployment/doppler-controller --namespace doppler-controller
```

## Step 1. Creating the DopplerSecret Resoure
## Step 2. Creating a DopplerSecret

The first step is to create a custom `DopplerSecret` resource, consisting of a name and a Doppler Service Token.

The first step is to create a custom `DopplerSecret` resource that will be used by the controller to create a corresponding Kubernetes secret. The Kubernetes secret created contains a map of key value pairs.
Upon `DopplerSecret` creation, the controller creates an associated Kubernetes secret, populating it with the secrets fetched from the Doppler API in Key-Value format.

Save the following to `doppler-secret.yml`:
To follow along with an example, update the code below with a real Service Token and save as `doppler-secret.yml`:

```yaml
apiVersion: 'doppler.com/v1'
kind: DopplerSecret
apiVersion: doppler.com/v1
kind: DopplerSecret
metadata:
name: doppler-secret
name: dopplersecret-test # DopplerSecret resource name
spec:
serviceToken: 'dp.st.dev.Ix5TqVXsdOHq4FByFQbMadT3rotEHVZQ7v04NSbWT1I' # Doppler Service Token for config to sync
secretName: app-secret # Name of Kubernetes Secret controller will create
serviceToken: dp.st.dev.XXXX # Change to your Doppler Service Token
secretName: doppler-test-secret # Kubernetes Secret name
```
Create an example resource:
Then create the `DopplerSecret`:

```sh
kubectl apply -f doppler-secret.yml
```

Check that the Kubenertes secret has been created by the controller:
Check that the associated Kubernetes secret has been created:

```sh
# List all Kubernetes secrets created by the Doppler controller
kubectl describe secrets --selector=dopplerSecret=true
# Or to view secret values
./bin/get-secret.sh doppler-test-secret
```

## Step 2. Configuring a Deployment
The controller continuously watches for secret updates from Doppler and when detected, automatically and instantly updates the associated secret.

As the controller creates a Kuberentes secret containing your secrets from Doppler, simply use the value for `secretName` used in the `DopplerSecret` resource.
Next, we'll cover how to configure a deployment to use the Kubernetes secret and enable auto-reloading for Deployments.

If you'd like to enable the auto-reload functionality, then just add a single annotation to your deployment.
## Step 3. Configuring a Deployment

To use the secret created by the Controller, we'll use the `envFrom` field to populate a container's environment variables using the secrets's Key-Value pairs:

```yaml
envFrom:
- secretRef:
name: mysecret # Matches the DopplerSecret name
```

Adding automatic and instant reloading of a deployment requires just a single annotation on the Deployment:

```yaml
annotations:
dopplersecrets.doppler.com/reload: 'true'
```

To test using the example `DopplerSecret` from above, save the following to `doppler-deployment.yml`:
Let's look at a complete example that uses previously created `DopplerSecret`. Save the below as `doppler-deployment.yml`:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: doppler-secrets
name: doppler-test-deployment
annotations:
dopplersecrets.doppler.com/reload: 'true' # Add for auto-reloads
dopplersecrets.doppler.com/reload: 'true'
spec:
replicas: 1
replicas: 2
selector:
matchLabels:
app: doppler-secrets
app: doppler-test
template:
metadata:
labels:
app: doppler-secrets
app: doppler-test
spec:
containers:
- name: doppler-secrets
- name: doppler-test
image: alpine
command: ['/bin/sh', '-c', 'apk add --no-cache tini > /dev/null 2>&1 && printenv | grep -v KUBERNETES_ && tini -s tail -f /dev/null'] # Test by printing env var names
envFrom: # Only envFrom is currently supported for auto-reloads
command: ['/bin/sh', '-c', 'apk add --no-cache tini > /dev/null 2>&1 && printenv | grep -v KUBERNETES_ && tini -s tail -f /dev/null'] # Test by printing env var names
imagePullPolicy: Always
envFrom:
- secretRef:
name: app-secret # Should match DopplerSecret.spec.secretName
name: doppler-test-secret # Should match DopplerSecret.spec.secretName
resources:
requests:
memory: '250Mi'
cpu: '250m'
limits:
memory: '500Mi'
cpu: '500m'
```

Create the deployment:

```sh
kubectl apply -f doppler-deployment.yml
kubectl rollout status -w deployment/doppler-test-deployment
```

Once the Deployment has completed, you can view the output of environment variables inside the example container by running:
Once the Deployment has completed, you can view the logs of the test container, which lists the environment variables (minus those with the `KUBERNETES_` prefix):

```sh
kubectl logs -lapp=doppler-secrets
kubectl logs -lapp=doppler-test
```

# Debugging and Troubleshooting

> NOTE: The `watch` binary is used by the below commands and can be installed on macOS using homebrew with `brew install watch`.

This repo contains a couple of handy scripts that give greater visibility into the secret and deployment updating process.

To watch a Doppler owned secret for changes:
To watch a Doppler owned secret for updates:

```sh
# Replace `app-secrets` with the name of your Doppler created secret name
watch ./bin/get-secret.sh app-secrets
# Replace `doppler-test-secret` with your secret name
./bin/get-secret.sh doppler-test-secret
```

To watch the logs of a running Pod:

```sh
# Replace `app=doppler-secrets` with your deployment label selector
watch ./bin/pod-logs.sh app=doppler-secrets
```

## Cleaning up example resources

To clean up the example resources, run:

```sh
kubectl delete deployments/doppler-secrets
kubectl delete dopplersecrets.doppler.com/doppler-secret
kubectl delete secrets/app-secret
```

To remove the controller and associated resources, run:

```sh
kubectl delete -f doppler-crd-controller.yml
# Replace `app=doppler-test` with your deployment label selector
watch ./bin/pod-logs.sh app=doppler-test
```
6 changes: 6 additions & 0 deletions bin/cleanup-example.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#! /usr/bin/env bash

kubectl delete -f example/deployment.yml
kubectl delete -f example/doppler-secret.yml
kubectl delete secrets/doppler-test-secret
doppler projects delete -y k8s-controller
38 changes: 35 additions & 3 deletions bin/deploy-example.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,40 @@
#!/usr/bin/env bash

set -e

# NOTE: The `envsubst` binary is required and can be installed in macOS by running `brew install gettext`

doppler setup
export DOPPLER_TOKEN=$(doppler configs tokens create koppler-k8s-controller-demo --plain)
kubectl apply -f <(SERVICE_TOKEN="$DOPPLER_TOKEN" envsubst < example/dopplersecret.yml)
echo -e '\n[info]: check controller has been deployed\n'
kubectl apply -f doppler-crd-controller.yml
kubectl rollout status -w deployment/doppler-controller --namespace doppler-controller
sleep 3

echo -e '\n[info]: create example Doppler project\n'
doppler projects create k8s-controller
doppler setup --project k8s-controller --config dev
doppler secrets set API_KEY=123
doppler secrets set AUTH_TOKEN=abc
sleep 3

echo -e '\n[info]: generate Doppler service token\n'
export DOPPLER_TOKEN=$(doppler configs tokens create doppler-k8s --plain)
sleep 5

echo -e '\n[info]: create DopplerSecret from example/doppler-secret.yml\n'
kubectl apply -f <(SERVICE_TOKEN="$DOPPLER_TOKEN" envsubst < example/doppler-secret.yml)
sleep 2

echo -e '\n[info]: confirm associated Kubernetes secret created'
sleep 4
./bin/get-secret.sh doppler-test-secret

echo -e '\n[info]: create Deployment from example/deployment.yml\n'
kubectl apply -f example/deployment.yml
kubectl rollout status -w deployment/doppler-test-deployment
sleep 3

echo -e '\n[info]: confirm container environment variables set from secret'
./bin/pod-logs.sh app=doppler-test
sleep 1

echo -e '\n[info]: done!\n'
2 changes: 2 additions & 0 deletions bin/get-secret.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env bash

echo -e "\n### $1 ###\n"

kubectl get secret $1 -o go-template='{{range $k,$v := .data}}{{$k}}={{$v|base64decode}}{{"\n"}}{{end}}'
3 changes: 2 additions & 1 deletion bin/pod-logs.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env bash

POD_NAME=$(kubectl get pods --field-selector=status.phase=Running -l $1 -o jsonpath={.items[0].metadata.name})
kubectl logs $POD_NAME
echo -e "\n### $POD_NAME ###\n"
kubectl logs $(kubectl get pods --field-selector=status.phase=Running -l $1 -o jsonpath={.items[0].metadata.name})
3 changes: 3 additions & 0 deletions bin/tail-controller.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

kubectl logs deployments/doppler-controller --namespace doppler-controller --follow --tail 20
7 changes: 7 additions & 0 deletions doppler-secret.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: 'doppler.com/v1'
kind: DopplerSecret
metadata:
name: dopplersecret-test # DopplerSecret resource name
spec:
serviceToken: '$DOPPLER_TOKEN' # Change to your Doppler Service Token
secretName: doppler-test-secret # Kubernetes Secret name
11 changes: 5 additions & 6 deletions example/deployment.yml
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: doppler-secrets
name: doppler-test-deployment
annotations:
dopplersecrets.doppler.com/reload: 'true'
spec:
replicas: 2
selector:
matchLabels:
app: doppler-secrets
app: doppler-test
template:
metadata:
labels:
app: doppler-secrets
app: doppler-test
spec:
containers:
- name: doppler-secrets
- name: doppler-test
image: alpine
command: ['/bin/sh', '-c', 'apk add --no-cache tini > /dev/null 2>&1 && printenv | grep -v KUBERNETES_ && tini -s tail -f /dev/null'] # Test by printing env var names
imagePullPolicy: Always
envFrom:
- secretRef:
name: app-secret # Should match DopplerSecret.spec.secretName
name: doppler-test-secret # Should match DopplerSecret.spec.secretName
resources:
requests:
memory: '250Mi'
cpu: '250m'
limits:
memory: '500Mi'
cpu: '500m'
terminationGracePeriodSeconds: 0 # Testing purposes speed up the redepolyment process
7 changes: 7 additions & 0 deletions example/doppler-secret.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: doppler.com/v1
kind: DopplerSecret
metadata:
name: dopplersecret-test
spec:
serviceToken: $DOPPLER_TOKEN # Change to your Doppler Service Token
secretName: doppler-test-secret # Kubernetes Secret name
8 changes: 0 additions & 8 deletions example/dopplersecret.yml

This file was deleted.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
"build-docker": "docker image build -t dopplerhq/k8s-controller:latest .",
"start": "node dist/index.js",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --fix && prettier --write .",
"pretty": "prettier --write .",
"tail-controller": "kubectl logs deployments/doppler-controller --namespace doppler-controller --follow --tail 20"
"pretty": "prettier --write ."
},
"repository": {
"type": "git",
Expand Down
21 changes: 1 addition & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,25 +78,6 @@ const checkDeployments = async (
}
})
})

// Check volumes
deployment.spec.template.spec.containers.forEach((container) => {
container.envFrom.forEach(async (envFrom) => {
if (envFrom.secretRef?.name === dopplerSecret.spec.secretName) {
console.log(
`[info]: updated secret (${dopplerSecret.spec.secretName}) found in ${container.name}`
)
console.log(
`[info]: triggering redeploy for ${deployment.metadata.name}`
)
triggerDeployment(
deployment.metadata.name,
deployment.metadata.namespace,
secret
)
}
})
})
}
})
}
Expand Down Expand Up @@ -214,7 +195,7 @@ const secretsSync = async () => {
upsertKubeSecret(dopplerSecret, latestSecretLog)
} catch (error) {
console.log(
`[error]: unable to fetch secret logs from Doppler API for ${dopplerSecret.metadata.name} - ${error.response.data.messages}`
`[error]: failed to fetch secrets for ${dopplerSecret.metadata.name}: ${error.response.data.messages}`
)
}
})
Expand Down

0 comments on commit 5f06083

Please sign in to comment.