Skip to content
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
8 changes: 8 additions & 0 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ jobs:
pip install ansible-lint==24.12.2 yamllint==1.35.1 ansible-core==2.16.3
yamllint --config-file ./tests/yaml-lint.yml .
ansible-lint --config-file ./tests/ansible-lint.yml .

- name: Validate Helm chart
run: |
chmod +x ./tests/helm-validate.sh
./tests/helm-validate.sh
molecule:
strategy:
fail-fast: false
Expand All @@ -28,10 +33,12 @@ jobs:
- systemd-uninstall
- container-basic
- container-binary
- container-binary-service
- container-full
- container-uninstall
- install-basic
- install-uninstall
- k8s-basic
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -59,4 +66,5 @@ jobs:
sudo systemctl enable docker

- name: Run Molecule Test
timeout-minutes: 20
run: cd tests && molecule test -s ${{ matrix.scenario }}
8 changes: 6 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ cd tests
molecule test -s container-basic # or any scenario name below
```

**CI scenarios:** `systemd-basic`, `systemd-full`, `systemd-uninstall`, `container-basic`, `container-full`, `container-uninstall`, `install-basic`, `install-uninstall`.
**CI scenarios:** `systemd-basic`, `systemd-full`, `systemd-uninstall`, `container-basic`, `container-binary`, `container-binary-service`, `container-full`, `container-uninstall`, `install-basic`, `install-uninstall`, `k8s-basic`.

`container-binary` exercises bind-mount mechanics with a short-lived `jq` binary. `container-binary-service` deploys a long-running daemon from a downloaded tarball (Prometheus) with config, data, ports, and readiness checks. `k8s-basic` uses the delegated driver with a local [kind](https://kind.sigs.k8s.io/) cluster to test `setup_mode: k8s` end-to-end.

**Helm validation:** `./tests/helm-validate.sh` (also runs in the lint CI job).

### Cloud VM caveats

Expand All @@ -66,4 +70,4 @@ Exercises the same stack as `setup_mode: container` (the role does not set `cgro

## Kubernetes / Helm

`setup_mode: k8s` is not in CI. Requires a cluster, `KUBECONFIG`, `KUBE_CONTEXT`, Helm, and `kubernetes.core` (see `README.md`, `helm/README.md`).
`k8s-basic` provisions kind on the CI runner (Docker required) and sets `KUBECONFIG` for the role. Chart-only validation also runs via `./tests/helm-validate.sh`. Manual deploys still need `KUBECONFIG`, `KUBE_CONTEXT`, Helm, and `kubernetes.core` (see `README.md`, `helm/README.md`).
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,14 @@ export KUBE_CONTEXT=<context-within-the-kubeconfig-to-use>

| var | description | default |
| :-------------: | :--------------------------------------------------------: | :--------------: |
| _helm_chart_path_ | path to Helm chart to use for the service deployment/release | `../../helm` |
| _helm_namespace_ | Kubernetes namespace to deploy to | `default` |
| _helm_values_path_ | file to load Helm chart values (see [here](./helm/README.md) for available values) | `values.yaml` |
| _helm_chart_path_ | path to Helm chart to use for the service deployment/release | `helm` (resolved relative to the role) |
| _helm_namespace_ | Kubernetes namespace to deploy to (also rendered into chart values) | `default` |
| _helm_values_path_ | optional Helm values overlay file merged after rendered role values | `""` |
| _helm_render_values_from_role_ | map common role vars (`image`, `config`, `ports`, `cpus`, `memory`, etc.) into Helm values | `true` |
| _helm_create_namespace_ | create the target namespace during Helm install | `true` |
| _helm_wait_ / _helm_atomic_ / _helm_timeout_ | Helm install safety controls | `true` / `true` / `10m` |

With `setup_mode: k8s`, the role renders Helm values from the same variables used by `container`, `systemd`, and `install` modes, then deploys the bundled chart. Set `helm_render_values_from_role: false` to use only `helm_values_path`.

## Containerized Apps
- [O1 Containers](https://github.com/O1labs/containers)
Expand Down
24 changes: 22 additions & 2 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,26 @@ systemd: {}
systemd_environment_directive: ""

### Kubernetes ###
helm_chart_path: ../../helm
helm_chart_path: helm
helm_namespace: default
helm_values_path: values.yaml
helm_values_path: ""
helm_render_values_from_role: true
# helm_rendered_values_path: /tmp/<name>-helm-values.yaml
helm_create_namespace: true
k8s_chart_create_namespace: false
helm_wait: true
helm_atomic: true
helm_timeout: 10m
k8s_common_name: ""
k8s_cluster_name: default-cluster
k8s_min_replicas: 1
k8s_max_replicas: 1
k8s_hpa_enabled: false
k8s_hpa_target_cpu: 50
k8s_deploy_stateful_set: false
k8s_ingress_enabled: false
k8s_service_monitor_enabled: false
k8s_health_check_path: /
k8s_service_monitor_path: /metrics
k8s_labels: {}
k8s_annotations: {}
2 changes: 2 additions & 0 deletions handlers/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,6 @@
name: "{{ name }}"
state: absent
release_namespace: "{{ helm_namespace }}"
kubeconfig: "{{ lookup('env', 'KUBECONFIG') | default(omit, true) }}"
context: "{{ lookup('env', 'KUBE_CONTEXT') | default(omit, true) }}"
when: setup_mode == 'k8s'
11 changes: 11 additions & 0 deletions helm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,14 @@ Helm chart that supports a basic, cloud-native containerized app in Kubernetes.
| persistentVolume | persistent volume that the pvc can use | `list(object)` | `[]` | no |
| storageClass | storage class that the pvc can use | `list(object)` | `[]` | no |
| dockercfgOverride | Docker config secret override reference (not including `-docker` suffix) | `string` | `` | no |
| hpa.enabled | Enable HorizontalPodAutoscaler (also auto-enabled when `maxReplicas` > `minReplicas`) | `bool` | `false` | no |
| hpa.targetCPUUtilizationPercentage | CPU utilization target for autoscaling v2 | `int` | `50` | no |
| hpa.targetMemoryUtilizationPercentage | Optional memory utilization target for autoscaling v2 | `int` | | no |
| livenessProbe | Container liveness probe definition | `dict` | `{}` | no |
| readinessProbe | Container readiness probe definition | `dict` | `{}` | no |
| startupProbe | Container startup probe definition | `dict` | | no |
| ingress.enabled | Whether to create an Ingress resource | `bool` | `false` | no |
| serviceMonitor.enabled | Whether to create a Prometheus Operator ServiceMonitor | `bool` | `false` | no |
| serviceMonitor.port | Service port name scraped by ServiceMonitor | `string` | `http` | no |
| serviceMonitor.path | Metrics path scraped by ServiceMonitor | `string` | `/metrics` | no |
| podDisruptionBudget | PodDisruptionBudget settings (`minAvailable` / `maxUnavailable`) | `dict` | | no |
125 changes: 125 additions & 0 deletions helm/templates/_container.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{{/*
Container environment variables from env.config, secrets, and extraEnv.
*/}}
{{- define "basic-service.containerEnv" -}}
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
{{- range $key, $value := ((.Values.env | default dict).config | default dict) }}
- name: {{ $key }}
value: {{ tpl ($value | toString) $ | quote }}
{{- end }}
{{- range $key, $value := .Values.secretEnv }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ include "basic-service.name" $ }}-env
key: {{ $key }}
{{- end }}
{{- if .Values.extraEnv }}
{{ tpl (toYaml .Values.extraEnv) . | nindent 0 }}
{{- end }}
{{- end -}}

{{/*
Primary application container specification.
*/}}
{{- define "basic-service.mainContainer" -}}
- name: {{ template "basic-service.name" . }}
image: {{ required "A valid .Values.image is required" .Values.image }}
{{- with .Values.command }}
command: {{ toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.args }}
args: {{ toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.containerSecurityContext }}
securityContext:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if or .Values.livenessProbe .Values.livenessProbeRPCDaemon }}
livenessProbe:
{{- toYaml (.Values.livenessProbe | default .Values.livenessProbeRPCDaemon) | nindent 4 }}
{{- end }}
{{- if or .Values.readinessProbe .Values.readinessProbeRPCDaemon }}
readinessProbe:
{{- toYaml (.Values.readinessProbe | default .Values.readinessProbeRPCDaemon) | nindent 4 }}
{{- end }}
{{- with .Values.startupProbe }}
startupProbe:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if or .Values.extraVolumeMounts .Values.deployStatefulSet }}
volumeMounts:
{{- if .Values.deployStatefulSet }}
- mountPath: {{ .Values.statefulSetOptions.mountPath }}
name: {{ tpl .Values.statefulSetOptions.name . }}
{{- end }}
{{- range $mountName, $mountValue := .Values.extraVolumeMounts }}
{{- toYaml $mountValue | nindent 4 }}
{{- end }}
{{- end }}
env:
{{- include "basic-service.containerEnv" . | nindent 4 }}
{{- end -}}

{{/*
Shared pod specification for Deployments and StatefulSets.
*/}}
{{- define "basic-service.podSpec" -}}
{{- if or .Values.dockercfg .Values.dockercfgOverride }}
imagePullSecrets:
- name: {{ default (include "basic-service.name" .) .Values.dockercfgOverride }}-dockercfg
{{- end }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- if or .Values.extraVolumes .Values.volumesFromConfigMaps }}
volumes:
{{- range $volumeName, $volumeValue := .Values.extraVolumes }}
{{- toYaml $volumeValue | nindent 2 }}
{{- end }}
{{- range $volumeName, $volumeValue := .Values.volumesFromConfigMaps }}
- name: {{ $volumeName }}
configMap:
name: {{ template "basic-service.name" $ }}-{{ $volumeValue.configMapName }}
{{- end }}
{{- end }}
{{- if .Values.serviceAccountName }}
serviceAccountName: {{ .Values.serviceAccountName }}
{{- end }}
{{- with .Values.initContainers }}
initContainers:
{{- range $containerName, $containerValue := . }}
{{- toYaml $containerValue | nindent 2 }}
{{- end }}
{{- end }}
containers:
{{- include "basic-service.mainContainer" . | nindent 2 }}
{{- with .Values.extraContainers }}
{{- range $key, $value := . }}
{{- tpl (toYaml $value) $ | nindent 2 }}
{{- end }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.terminationGracePeriodSeconds }}
terminationGracePeriodSeconds: {{ . }}
{{- end }}
{{- end -}}
106 changes: 22 additions & 84 deletions helm/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -1,101 +1,39 @@
{{- if not .Values.deployStatefulSet}}
{{- if not .Values.deployStatefulSet }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{template "basic-service.name" .}}-deployment
namespace: {{.Values.namespace}}
name: {{ template "basic-service.name" . }}-deployment
namespace: {{ .Values.namespace }}
labels:
{{- include "basic-service.labels" $ | indent 8 }}
app: {{ template "basic-service.name" . }}
{{- with .Values.labels }}
{{- include "basic-service.labels" $ | nindent 4 }}
{{- end }}
{{- with .Values.annotations }}
annotations:
{{- tpl (toYaml .Values.annotations) . | nindent 4 }}
{{- tpl (toYaml .) $ | nindent 4 }}
{{- end }}
spec:
replicas: {{int .Values.minReplicas | default 0}}
replicas: {{ int .Values.minReplicas | default 1 }}
selector:
matchLabels:
app: {{template "basic-service.name" .}}
{{- if .Values.deployStrategy }}
{{- toYaml .Values.deployStrategy | nindent 2 }}
{{- include "basic-service.selectorLabels" . | nindent 6 }}
{{- with .Values.deployStrategy }}
strategy:
{{- toYaml . | nindent 4 }}
{{- end }}
template:
metadata:
labels:
app: {{template "basic-service.name" .}}
{{- if .Values.labels }}
{{- include "basic-service.labels" $ | indent 8 }}
{{- include "basic-service.selectorLabels" . | nindent 8 }}
{{- with .Values.labels }}
{{- include "basic-service.labels" $ | nindent 8 }}
{{- end }}
annotations:
checksum/secrets: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- tpl (toYaml .Values.annotations) . | nindent 4 }}
spec:
{{- if or .Values.dockercfg .Values.dockercfgOverride}}
imagePullSecrets:
- name: {{default (include "basic-service.name" .) .Values.dockercfgOverride}}-dockercfg
{{- end}}
volumes:
{{- range $volumeName, $volumeValue := .Values.extraVolumes }}
{{- toYaml $volumeValue | nindent 8}}
{{- end }}
{{- range $volumeName, $volumeValue := .Values.volumesFromConfigMaps }}
- name: {{$volumeName}}
configMap:
name: {{template "basic-service.name" $}}-{{$volumeValue.configMapName}}
{{- end }}
{{- if .Values.serviceAccountName }}
serviceAccountName: {{.Values.serviceAccountName }}
{{- end }}
{{- if .Values.initContainers }}
initContainers:
{{- range $containerName, $containerValue := .Values.initContainers }}
{{- toYaml $containerValue | nindent 6}}
{{- end }}
{{- end }}
containers:
- name: {{template "basic-service.name" .}}
image: {{.Values.image}}
{{- if .Values.command }}
command: {{ toYaml .Values.command | nindent 8 }}
{{- end }}
{{- if .Values.args }}
args: {{ toYaml .Values.args | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml .Values.containerSecurityContext | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
livenessProbe:
{{- toYaml .Values.livenessProbeRPCDaemon | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbeRPCDaemon | nindent 12 }}
volumeMounts:
{{- range $mountName, $mountValue := .Values.extraVolumeMounts }}
{{- toYaml $mountValue | nindent 10}}
{{- with .Values.annotations }}
{{- tpl (toYaml .) $ | nindent 8 }}
{{- end }}
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
{{- range $key, $value := .Values.secretEnv }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ include "basic-service.name" $ }}-env
key: {{ $key }}
{{- end }}
{{- if .Values.extraEnv }}
{{- toYaml .Values.extraEnv | nindent 12 }}
{{- end }}
{{- if .Values.extraContainers }}
{{- range $key, $value := .Values.extraContainers }}
{{- tpl (toYaml $value) $ | nindent 6}}
{{- end }}
{{- end }}
nodeSelector:
{{- toYaml .Values.nodeSelector | nindent 8 }}
affinity:
{{- toYaml .Values.affinity | nindent 8 }}
tolerations:
{{- toYaml .Values.tolerations | nindent 8 }}
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
{{- end}}
spec:
{{- include "basic-service.podSpec" . | nindent 6 }}
{{- end }}
Loading
Loading