Skip to content

Commit

Permalink
Doppler Kubernetes Operator (#2266)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergio-chainguard authored Mar 1, 2024
1 parent eb68682 commit 326a479
Show file tree
Hide file tree
Showing 12 changed files with 429 additions and 0 deletions.
46 changes: 46 additions & 0 deletions images/doppler-kubernetes-operator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!--monopod:start-->
# images/doppler-kubernetes-operator
| | |
| - | - |
| **OCI Reference** | `cgr.dev/chainguard/images/doppler-kubernetes-operator` |


* [View Image in Chainguard Academy](https://edu.chainguard.dev/chainguard/chainguard-images/reference/images/doppler-kubernetes-operator/overview/)
* [View Image Catalog](https://console.enforce.dev/images/catalog) for a full list of available tags.
* [Contact Chainguard](https://www.chainguard.dev/chainguard-images) for enterprise support, SLAs, and access to older tags.*

---
<!--monopod:end-->

<!--overview:start-->
Automatically sync secrets from Doppler to Kubernetes and auto-reload deployments when secrets change.
<!--overview:end-->

<!--getting:start-->
## Download this Image
The image is available on `cgr.dev`:

```
docker pull cgr.dev/chainguard/images/doppler-kubernetes-operator:latest
```
<!--getting:end-->

<!--body:start-->
doppler-kubernetes-operator is a Kubernetes operator, which can be deployed using helm. Refer to the [upstream repositories documentation](https://github.com/DopplerHQ/kubernetes-operator) for how to get started with doppler-kubernetes-operator.

```shell
helm repo add doppler https://helm.doppler.com
helm install doppler-kubernetes-operator doppler/doppler-kubernetes-operator

helm repo update
helm pull doppler/doppler-kubernetes-operator --untar
kubectl apply -f doppler-kubernetes-operator/crds/all.yaml

helm upgrade doppler-kubernetes-operator doppler/doppler-kubernetes-operator \
--set image.repository=cgr.dev/chainguard/doppler-kubernetes-operator \
--set image.tag=latest
}
```

As per [project documentation](https://github.com/DopplerHQ/kubernetes-operator/blob/main/README.md).
<!--body:end-->
131 changes: 131 additions & 0 deletions images/doppler-kubernetes-operator/TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# TESTING.md

## Create a local cluster with kind

Using kind you can create your local cluster

```shell
kind create cluster
```

## Using Helm
You can install the latest Helm chart with:
```shell
helm repo add doppler https://helm.doppler.com
helm install --generate-name doppler/doppler-kubernetes-operator
```

You can upgrade using the latest helm chart. It's important to note that the CRD is not automatically updated if you just perform a simple helm upgrade. As such, be sure you follow this process:

```shell
helm repo update
helm pull doppler/doppler-kubernetes-operator --untar
kubectl apply -f doppler-kubernetes-operator/crds/all.yaml
```

## Doppler Service Token

Generate a Doppler Service Token and use it in this command to create your Doppler token secret:

```shell
kubectl create secret generic doppler-token-secret \
--namespace doppler-operator-system \
--from-literal=serviceToken=dp.st.dev.XXXX
```

If you have the Doppler CLI installed, you can generate a Doppler Service Token from the CLI and create the Doppler token secret in one step:

Note: This command will generate a Personal Token, which has the same access permissions as your user. If you're on the Developer plan or just doing a quick test to see how this works, that should be fine. However, if you're on the Team plan or higher and this is for an actual deployment, we recommend using a Service Account token instead.

```shell
kubectl create secret generic doppler-token-secret \
--namespace doppler-operator-system \
--from-literal=serviceToken=$(doppler configs tokens create doppler-kubernetes-operator --plain)
```

## Doppler Token Secret
Next, we'll create a DopplerSecret that references your Doppler token secret and defines the location of the managed secret.

```yaml
# dopplersecret.yaml
apiVersion: secrets.doppler.com/v1alpha1
kind: DopplerSecret
metadata:
name: dopplersecret-test
namespace: doppler-operator-system
spec:
tokenSecret:
name: doppler-token-secret
managedSecret:
name: doppler-test-secret
namespace: default
type: Opaque
```
To add the secret to your cluster
```shell
kubectl apply -f dopplersecret.yaml
```

Check that the associated Kubernetes secret has been created:

```shell
kubectl describe secrets --selector=secrets.doppler.com/subtype=dopplerSecret
```

## Deployment

The envFrom field will populate a container's environment variables using the secret's Key-Value pairs:

```yaml
# deployment-envfrom.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: doppler-test-deployment-envfrom
annotations:
secrets.doppler.com/reload: 'true'
spec:
replicas: 2
selector:
matchLabels:
app: doppler-test
template:
metadata:
labels:
app: doppler-test
spec:
containers:
- name: doppler-test
image: alpine
command:
- /bin/sh
- -c
# Print all non-Kubernetes environment variables
- apk add --no-cache tini > /dev/null 2>&1 &&
echo "### This is a simple deployment running with this env:" &&
printenv | grep -v KUBERNETES_ &&
tini -s tail -f /dev/null
imagePullPolicy: Always
envFrom:
- secretRef:
name: doppler-test-secret # Kubernetes secret name
resources:
requests:
memory: '250Mi'
cpu: '250m'
limits:
memory: '500Mi'
cpu: '500m'

```

```shell
kubectl apply -f config/samples/deployment-envfrom.yaml
```

Once the Deployment has completed, you can view the logs of the test container:

```shell
kubectl logs -lapp=doppler-test --tail=-1
```
19 changes: 19 additions & 0 deletions images/doppler-kubernetes-operator/config/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
terraform {
required_providers {
apko = { source = "chainguard-dev/apko" }
}
}

variable "extra_packages" {
description = "The additional packages to install"
default = ["doppler-kubernetes-operator"]
}

data "apko_config" "this" {
config_contents = file("${path.module}/template.apko.yaml")
extra_packages = var.extra_packages
}

output "config" {
value = jsonencode(data.apko_config.this.config)
}
12 changes: 12 additions & 0 deletions images/doppler-kubernetes-operator/config/template.apko.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
accounts:
groups:
- groupname: nonroot
gid: 65532
users:
- username: nonroot
uid: 65532
gid: 65532
run-as: 65532

entrypoint:
command: manager
39 changes: 39 additions & 0 deletions images/doppler-kubernetes-operator/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
terraform {
required_providers {
oci = { source = "chainguard-dev/oci" }
}
}

variable "target_repository" {
description = "The docker repo into which the image and attestations should be published."
}

module "config" { source = "./config" }

module "doppler-kubernetes-operator" {
source = "../../tflib/publisher"
name = basename(path.module)
target_repository = var.target_repository
config = module.config.config

build-dev = true

}

module "test" {
source = "./tests"
digest = module.doppler-kubernetes-operator.image_ref
}

resource "oci_tag" "latest" {
depends_on = [module.test]
digest_ref = module.doppler-kubernetes-operator.image_ref
tag = "latest"
}

resource "oci_tag" "latest-dev" {
depends_on = [module.test]
digest_ref = module.doppler-kubernetes-operator.dev_ref
tag = "latest-dev"
}

10 changes: 10 additions & 0 deletions images/doppler-kubernetes-operator/metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: images/doppler-kubernetes-operator
image: cgr.dev/chainguard/images/doppler-kubernetes-operator
logo: https://storage.googleapis.com/chainguard-academy/logos/images/doppler-kubernetes-operator.svg
endoflife: ""
console_summary: ""
short_description: Automatically sync secrets from Doppler to Kubernetes and auto-reload deployments when secrets change.
compatibility_notes: ""
readme_file: README.md
upstream_url: https://github.com/DopplerHQ/kubernetes-operator
keywords: []
61 changes: 61 additions & 0 deletions images/doppler-kubernetes-operator/tests/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
terraform {
required_providers {
oci = { source = "chainguard-dev/oci" }
helm = { source = "hashicorp/helm" }
random = { source = "hashicorp/random" }
}
}

variable "digest" {
description = "The image digest to run tests over."
}

data "oci_string" "ref" { input = var.digest }

resource "random_id" "id" { byte_length = 4 }

resource "helm_release" "doppler-kubernetes-operator" {
name = "doppler-kubernetes-operator-${random_id.id.hex}"
namespace = "doppler-kubernetes-operator"
repository = "https://helm.doppler.com"
chart = "doppler-kubernetes-operator"
create_namespace = true

values = [
jsonencode({
image = {
registry = data.oci_string.ref.registry
repository = data.oci_string.ref.repo
tag = data.oci_string.ref.pseudo_tag
}
}),
]
}

data "oci_exec_test" "smoke" {
digest = var.digest
script = "./smoke_test.sh"
working_dir = path.module
env = [
{
name = "RELEASE_ID"
value = random_id.id.hex
},
{
name = "RELEASE_NAME"
value = helm_release.doppler-kubernetes-operator.name
},
{
name = "RELEASE_NAMESPACE"
value = helm_release.doppler-kubernetes-operator.namespace
}
]
depends_on = [helm_release.doppler-kubernetes-operator]
}

module "helm_cleanup" {
source = "../../../tflib/helm-cleanup"
name = helm_release.doppler-kubernetes-operator.id
namespace = helm_release.doppler-kubernetes-operator.namespace
depends_on = [data.oci_exec_test.smoke]
}
51 changes: 51 additions & 0 deletions images/doppler-kubernetes-operator/tests/smoke_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash

set -o errexit -o nounset -o errtrace -o pipefail -x

sleep 5

DELAY=5
NAMESPACE="doppler-operator-system"
CONTROLLER_POD=$(kubectl get pods -n "$NAMESPACE" -o=jsonpath='{.items[0].metadata.name}')
SECRET_NAME="doppler-token-secret"
DUMMY_SECRET_NAME="my-doppler-secret"
TIMER=0
TIMEOUT=60

echo "Step 1: Create a dummy secret in Kubernetes"
kubectl create secret generic "$SECRET_NAME" --from-literal=token="$DUMMY_SECRET_NAME" -n "$NAMESPACE"

echo "Step 2: Checking the operator status..."
kubectl wait --for=condition=Ready pod -n "$NAMESPACE" "$CONTROLLER_POD" --timeout="$TIMEOUT"s

echo "Step 3: Creating the DopplerHQ secret resource..."
cat <<EOF | kubectl apply -f -
apiVersion: secrets.doppler.com/v1alpha1
kind: DopplerSecret
metadata:
name: $SECRET_NAME
namespace: $NAMESPACE
spec:
tokenSecret:
name: "$SECRET_NAME"
managedSecret:
name: "$SECRET_NAME"
namespace: "$NAMESPACE"
EOF

# Loop until the secret is created or the time limit is reached
while [ $TIMER -lt $TIMEOUT ]; do
# Check if the secret exists in the namespace
SECRET=$(kubectl get secret -n "$NAMESPACE" "$SECRET_NAME" --no-headers 2>/dev/null)
if [ -n "$SECRET" ]; then
echo "¡The secret $SECRET_NAME was found in the namespace $NAMESPACE!"
exit 0
fi

# Wait the specified delay and then increment the timer
sleep $DELAY
TIMER=$(($TIMER + $DELAY))
done

echo "Could not create secret $SECRET_NAME within $TIMEOUT seconds!"
exit 1
1 change: 1 addition & 0 deletions images/pytorch-cuda12/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ As a quick intro, we will use pytorch to create a very simple deep learning mode

To run this script,
```bash

docker run --rm -it -v /home/srishihegde/quick.py:/tmp/model_builder.py --gpus all cgr.dev/chainguard/pytorch-cuda12:latest -c "python /tmp/model_builder.py"
```
A quickstart tutorial as outlined [here](https://pytorch.org/tutorials/beginner/basics/quickstart_tutorial.html) can also be run using the tests/quickstart.py script similar to the above run
Expand Down
1 change: 1 addition & 0 deletions images/pytorch-cuda12/config/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ terraform {
variable "extra_packages" {
description = "Additional packages to install."
type = list(string)

# torchvision is currently built on top of torch and should include all the packages we expect from it
default = ["torchvision-cuda12"]
}
Expand Down
Loading

0 comments on commit 326a479

Please sign in to comment.