Skip to content
Open
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
2 changes: 1 addition & 1 deletion helm/latr/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
version: 0.1.0
version: 0.2.0

# This is the version number of the application being deployed.
appVersion: "1.0.0"
Expand Down
20 changes: 19 additions & 1 deletion helm/latr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ latr is an automation tool that manages and automatically rotates Linode API tok
- A valid Linode API token
- Container image available at `ghcr.io/linode-obs/latr`

## Modes

| Mode | When | Resources |
|------|------|-----------|
| **Single-instance** (default) | `accounts: []` | One Deployment (current / simple installs) |
| **Multi-account** | `accounts` non-empty | **One helm release** → **N Deployments**, one ConfigMap/Secret per account |

Multi-account keeps **one** helm release (and typically one Argo Application) while running a separate latr pod per Linode account you manage. Set `accounts[].name` yourself (e.g. `personal-account`, `work-account`). Names become Deployment/ConfigMap/Secret names, so they must be valid **DNS-1123 labels** (lowercase alphanumeric and `-`, start/end alphanumeric, ≤63 characters). There is no public “customer id” field you must look up.

See `examples/values-multi-account.yaml`.

```bash
helm template latr ./helm/latr -f helm/latr/examples/values-multi-account.yaml
```

Per account, prefer `configFiles` (map of filename → YAML text), e.g. `process.yaml` + `tokens.yaml`. latr is started with `-config /config/*` (Go `filepath.Glob`, no shell).

## Installing the Chart

### Basic Installation
Expand Down Expand Up @@ -45,7 +62,8 @@ config:
scopes: "*"
storage:
- type: "vault"
path: "secret/data/linode/tokens/production"
# path relative to vault.mount_path (no mount/data prefix)
path: "linode/tokens/production"

secrets:
linodeToken: "your-linode-token"
Expand Down
75 changes: 75 additions & 0 deletions helm/latr/examples/values-multi-account.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Example multi-account values for one helm release / one Argo Application.
# Renders two Deployments: personal-account and work-account.
#
# Names are operator-chosen labels for Linode accounts you manage — not a
# special "customer id" field from the public API.
#
# helm template latr ./helm/latr -f helm/latr/examples/values-multi-account.yaml

image:
repository: ghcr.io/linode-obs/latr
tag: "0.3.0"

resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi

accounts:
- name: personal-account
secrets:
linodeToken: "REDACTED-bootstrap-or-avp"
vaultRoleId: "REDACTED"
vaultSecretId: "REDACTED"
configFiles:
process.yaml: |
daemon:
mode: daemon
check_interval: 6h
dry_run: false
rotation:
threshold_percent: 10
vault:
address: https://vault.example.com:8200
mount_path: secret
role_id: ${VAULT_ROLE_ID}
secret_id: ${VAULT_SECRET_ID}
observability:
log_level: info
tokens.yaml: |
tokens:
- label: homelab-api
team: personal
validity: 180d
scopes: "*"
storage:
- type: vault
path: linode/tokens/homelab-api
key: token
action: replace

- name: work-account
secrets:
existingSecret: latr-work-account-creds
configFiles:
process.yaml: |
daemon:
mode: daemon
check_interval: 6h
vault:
address: https://vault.example.com:8200
mount_path: secret
role_id: ${VAULT_ROLE_ID}
secret_id: ${VAULT_SECRET_ID}
tokens.yaml: |
tokens:
- label: ci-readonly
team: platform
validity: 90d
scopes: "linodes:read_only"
storage:
- type: vault
path: linode/tokens/ci-readonly
44 changes: 22 additions & 22 deletions helm/latr/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ To learn more about the release, try:
$ helm status {{ .Release.Name }} -n {{ .Release.Namespace }}
$ helm get all {{ .Release.Name }} -n {{ .Release.Namespace }}

{{- if eq (include "latr.multiAccount" .) "true" }}

Multi-account mode: {{ len .Values.accounts }} account(s) → one Deployment each
(named from accounts[].name, e.g. personal-account, work-account).

{{- range .Values.accounts }}
- {{ include "latr.accountFullname" (dict "root" $ "account" .) }}
{{- end }}

$ kubectl get deploy -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }}

{{- else }}

{{- if not .Values.secrets.existingSecret }}
{{- if or (not .Values.secrets.linodeToken) (not .Values.secrets.vaultRoleId) (not .Values.secrets.vaultSecretId) }}

Expand All @@ -17,47 +30,34 @@ Please ensure you set the following values:
- secrets.vaultRoleId
- secrets.vaultSecretId

You can update the secrets using:

$ helm upgrade {{ .Release.Name }} {{ .Chart.Name }} \
--set secrets.linodeToken="your-linode-token" \
--set secrets.vaultRoleId="your-vault-role-id" \
--set secrets.vaultSecretId="your-vault-secret-id"

Alternatively, create a values file with your secrets and upgrade:

$ helm upgrade {{ .Release.Name }} {{ .Chart.Name }} -f your-secrets.yaml

{{- end }}
{{- end }}

{{- if not .Values.config.vault.address }}
{{- if not .Values.configFiles }}

WARNING: Vault address is not configured!

Please set config.vault.address to your Vault server URL:

$ helm upgrade {{ .Release.Name }} {{ .Chart.Name }} \
--set config.vault.address="https://vault.example.com:8200"
Please set config.vault.address to your Vault server URL.

{{- end }}
{{- end }}

{{- if eq (len .Values.config.tokens) 0 }}
{{- if and (eq (len .Values.config.tokens) 0) (not .Values.configFiles) }}

WARNING: No tokens are configured!

Please configure at least one token in config.tokens.
See the values.yaml file for an example configuration.
Please configure at least one token in config.tokens or use configFiles.

{{- end }}

To view the logs:
$ kubectl get deployment {{ include "latr.fullname" . }} -n {{ .Release.Namespace }}

$ kubectl logs -n {{ .Release.Namespace }} -l app.kubernetes.io/name={{ include "latr.name" . }} -f
{{- end }}

To check the deployment status:
To view logs:

$ kubectl get deployment {{ include "latr.fullname" . }} -n {{ .Release.Namespace }}
$ kubectl logs -n {{ .Release.Namespace }} -l app.kubernetes.io/name={{ include "latr.name" . }} -f

For more information about latr, visit:
https://github.com/linode-obs/latr
90 changes: 89 additions & 1 deletion helm/latr/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Create the name of the service account to use
{{- end }}

{{/*
Create the name of the secret to use
Create the name of the secret to use (single-instance mode)
*/}}
{{- define "latr.secretName" -}}
{{- if .Values.secrets.existingSecret }}
Expand All @@ -79,3 +79,91 @@ Image name
{{- $tag := .Values.image.tag | default .Chart.AppVersion }}
{{- printf "%s:%s" .Values.image.repository $tag }}
{{- end }}

{{/*
Multi-account mode: true when accounts list is non-empty.
*/}}
{{- define "latr.multiAccount" -}}
{{- if and .Values.accounts (gt (len .Values.accounts) 0) }}true{{- else }}false{{- end }}
{{- end }}

{{/*
Account workload name. Context: dict "root" $ "account" $account

Prefer accounts[].name (e.g. personal-account, work-account). Optional
accounts[].id is only used when name is omitted (resource name latr-<id>).

Names are used as Kubernetes resource names (Deployment/ConfigMap/Secret) and
must be valid DNS-1123 labels: lowercase alphanumeric, '-' allowed, must start
and end with alphanumeric, max 63 characters.
*/}}
{{- define "latr.accountFullname" -}}
{{- $account := .account -}}
{{- $name := "" -}}
{{- if $account.name -}}
{{- $name = $account.name | trunc 63 | trimSuffix "-" -}}
{{- else if $account.id -}}
{{- $name = printf "latr-%s" ($account.id | toString) | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- fail "accounts[] entries require .name (recommended) or .id" -}}
{{- end -}}
{{- if not (regexMatch "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" $name) -}}
{{- fail (printf "accounts[] name %q is not a valid DNS-1123 label (lowercase alphanumeric and '-', max 63, start/end alphanumeric)" $name) -}}
{{- end -}}
{{- $name -}}
{{- end }}

{{/*
Checksum material for one account's config only (avoids rolling all pods when
another account's config changes). Context: dict "root" $ "account" $account
*/}}
{{- define "latr.accountConfigChecksum" -}}
{{- $account := .account -}}
{{- if $account.configFiles -}}
{{- toYaml $account.configFiles -}}
{{- else if $account.config -}}
{{- toYaml $account.config -}}
{{- end -}}
{{- end }}

{{/*
Checksum material for one account's secrets only.
Context: dict "root" $ "account" $account
*/}}
{{- define "latr.accountSecretChecksum" -}}
{{- toYaml (.account.secrets | default dict) -}}
{{- end }}

{{/*
Account selector labels. Context: dict "root" $ "account" $account
*/}}
{{- define "latr.accountSelectorLabels" -}}
app.kubernetes.io/name: {{ include "latr.name" .root }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
app.kubernetes.io/component: {{ include "latr.accountFullname" . }}
{{- end }}

{{/*
Account common labels. Context: dict "root" $ "account" $account
*/}}
{{- define "latr.accountLabels" -}}
helm.sh/chart: {{ include "latr.chart" .root }}
{{ include "latr.accountSelectorLabels" . }}
{{- if .root.Chart.AppVersion }}
app.kubernetes.io/version: {{ .root.Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
{{- end }}

{{/*
Account secret name. Context: dict "root" $ "account" $account
*/}}
{{- define "latr.accountSecretName" -}}
{{- $account := .account -}}
{{- $secrets := $account.secrets | default dict -}}
{{- if $secrets.existingSecret -}}
{{- $secrets.existingSecret -}}
{{- else -}}
{{- include "latr.accountFullname" . -}}
{{- end -}}
{{- end }}
34 changes: 34 additions & 0 deletions helm/latr/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
{{- if ne (include "latr.multiAccount" .) "true" }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "latr.fullname" . }}
labels:
{{- include "latr.labels" . | nindent 4 }}
data:
{{- if .Values.configFiles }}
{{- range $name, $content := .Values.configFiles }}
{{ $name }}: |
{{- $content | nindent 4 }}
{{- end }}
{{- else }}
config.yaml: |
daemon:
mode: {{ .Values.config.daemon.mode }}
Expand All @@ -29,3 +36,30 @@ data:

tokens:
{{- toYaml .Values.config.tokens | nindent 6 }}
{{- end }}
{{- end }}
{{- if eq (include "latr.multiAccount" .) "true" }}
{{- range $account := .Values.accounts }}
{{- $ctx := dict "root" $ "account" $account }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "latr.accountFullname" $ctx }}
labels:
{{- include "latr.accountLabels" $ctx | nindent 4 }}
data:
{{- if $account.configFiles }}
{{- range $name, $content := $account.configFiles }}
{{ $name }}: |
{{- $content | nindent 4 }}
{{- end }}
{{- else if $account.config }}
{{/* Expect latr-native YAML keys (snake_case). Prefer configFiles for multi-file layouts. */}}
config.yaml: |
{{- toYaml $account.config | nindent 4 }}
{{- else }}
{{- fail (printf "account %s: set configFiles or config" (include "latr.accountFullname" $ctx)) }}
{{- end }}
{{- end }}
{{- end }}
Loading