Skip to content

Commit

Permalink
feat(ckan): add api creation and perist secrets (#1238) (#1249)
Browse files Browse the repository at this point in the history
Co-authored-by: SyeKlu <[email protected]>
  • Loading branch information
marvinWolff and SyeKlu authored Nov 29, 2024
1 parent e3910ca commit a42d2f6
Show file tree
Hide file tree
Showing 7 changed files with 383 additions and 18 deletions.
76 changes: 76 additions & 0 deletions charts/ckan/templates/ckan/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{{- $name := (printf "%s-%s-configmap" (include "common.names.fullname" .) "ckan") -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ $name }}
labels: {{- include "common.labels.standard" . | nindent 4 }}
app.kubernetes.io/component: {{ $name }}
namespace: {{ .Release.Namespace | quote }}
data:
ckan-init.sh: |-
#!/bin/bash
echo "initiate ckan"
if [[ -z "$DATAPUSHER_API_TOKEN" || -z "$EMAIL_API_KEY" ]]; then
ckan config-tool $CKAN_INI "SECRET_KEY=${SECRET_KEY}"
ckan config-tool $CKAN_INI "api_token.jwt.encode.secret=string:${JWT_SECRET}"
ckan config-tool $CKAN_INI "api_token.jwt.decode.secret=string:${JWT_SECRET}"
db_command="ckan -c $CKAN_INI db init"
max_retries=3
attempt=0
echo "[prerun] Initializing or upgrading db - start"
while [ $attempt -lt $max_retries ]; do
$db_command 2>&1
if [ $? -eq 0 ]; then
echo "[prerun] Initializing or upgrading db - end"
break
else
if grep -q "OperationalError" <<< "$($db_command 2>&1)"; then
echo "[prerun] Database not ready, retrying in 5 seconds..."
sleep 5
attempt=$((attempt + 1))
else
echo "[prerun] Error occurred: $(tail -n 1 <(echo $?))"
break
fi
fi
attempt=$((attempt + 1))
done
if [ $attempt -ge $max_retries ]; then
echo "[prerun] Failed to initialize or upgrade db after $max_retries attempts, exiting..."
exit 1
fi
if [[ -z "$CKAN_SYSADMIN_NAME" || -z "$CKAN_SYSADMIN_PASSWORD" || -z "$CKAN_SYSADMIN_EMAIL" ]]; then
echo "[prerun] Missing required environment variables: CKAN_SYSADMIN_NAME, CKAN_SYSADMIN_PASSWORD, or CKAN_SYSADMIN_EMAIL"
exit 1
fi
EXISTING_USER=$(ckan -c "$CKAN_INI" user show "$CKAN_SYSADMIN_NAME" 2>/dev/null)
if [[ "$EXISTING_USER" == *"User: None"* ]]; then
echo "[prerun] Creating sysadmin user $CKAN_SYSADMIN_NAME"
ckan -c "$CKAN_INI" user add "$CKAN_SYSADMIN_NAME" "password=$CKAN_SYSADMIN_PASSWORD" "email=$CKAN_SYSADMIN_EMAIL"
echo "[prerun] Created user $CKAN_SYSADMIN_NAME"
ckan -c "$CKAN_INI" sysadmin add "$CKAN_SYSADMIN_NAME"
echo "[prerun] Made user $CKAN_SYSADMIN_NAME a sysadmin"
else
echo "[prerun] Sysadmin user $CKAN_SYSADMIN_NAME exists, skipping creation"
fi
if [[ -z "$DATAPUSHER_API_TOKEN" ]]; then
ckan -c $CKAN_INI user token add ckan_admin datapusherApiKey | tail -n 1 | tr -d '\t' > /api-tokens/datapusherApiKey;
fi
if [[ -z "$EMAIL_API_KEY" ]]; then
ckan -c $CKAN_INI user token add ckan_admin emailApiKey | tail -n 1 | tr -d '\t' > /api-tokens/emailApiKey;
fi
else
echo "ckan already initiated"
fi
51 changes: 37 additions & 14 deletions charts/ckan/templates/ckan/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,22 @@ spec:
labels: {{- include "common.labels.matchLabels" . | nindent 8 }}
app.kubernetes.io/component: {{ $name }}
annotations:
checksum/secret: {{ include "common.utils.checksumTemplate" (dict "path" "/postgresql/secret.yaml" "context" $) }}-{{ include "common.utils.checksumTemplate" (dict "path" "/postgresql/secret.yaml" "context" $) }}-{{ include "common.utils.checksumTemplate" (dict "path" "/solr/secret.yaml" "context" $) }}
checksum/secret: {{ include "common.utils.checksumTemplate" (dict "path" "/ckan/secret.yaml" "context" $) }}-{{ include "common.utils.checksumTemplate" (dict "path" "/postgresql/secret.yaml" "context" $) }}-{{ include "common.utils.checksumTemplate" (dict "path" "/solr/secret.yaml" "context" $) }}
spec:
{{- include "common.images.renderPullSecrets" ( dict "images" (list .Values.ckan.image) "context" $) | indent 6 }}
automountServiceAccountToken: false
serviceAccountName: {{ printf "%s-%s-serviceaccount" (include "common.names.fullname" $) $name | quote }}
volumes:
- name: "ckan"
persistentVolumeClaim:
claimName: {{ $claimName }}
- name: configmap-volume
configMap:
defaultMode: 0777
name: my-configmap
- name: api-tokens-volume
emptyDir: {}
securityContext:
{{- toYaml .Values.ckan.podSecurityContext | default dict | nindent 8 }}
{{ if .Values.ckan.persistence -}}
initContainers:
- name: set-volume-ownsership
image: {{ printf "%s/busybox" ($.Values.global.imageRegistry | default (include "ckan.defaultRegistry" (dict))) }}:1.36
command: ["sh", "-c", "chown -R 92:92 /var/lib/ckan"] # 92 is the uid and gid of ckan user/group
volumeMounts:
- name: ckan
mountPath: /var/lib/ckan
readOnly: false
{{ end }}
containers:
- name: {{ printf "%s-%s" .Chart.Name $name }}
env:
Expand Down Expand Up @@ -80,6 +76,10 @@ spec:
- name: CKAN_SMTP_STARTTLS
value: {{ .Values.ckan.smtp.starttls | quote}}
{{- end }}
- name: CKAN__LOCALE_DEFAULT
value: {{ .Values.ckan.locales.default | quote }}
- name: CKAN__LOCALES_OFFERED
value: {{ .Values.ckan.locales.offered | quote }}
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
Expand Down Expand Up @@ -147,10 +147,35 @@ spec:
value: "http://{{ printf "%s-%s" (include "common.names.fullname" $) "datapusher" }}:{{ include "ckan.datapusher.service.port" $ }}"
- name: CKAN_DATAPUSHER_FORMATS
value: {{ .Values.ckan.datapusher.formats | join " " | quote }}
- name: CKAN__DATAPUSHER__API_TOKEN
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: datapusherApiKey
- name: CKAN__DATAPUSHER__CALLBACK_URL_BASE
value: "http://{{ printf "%s-%s" (include "common.names.fullname" $) "ckan" }}:{{ include "ckan.ckan.service.port" $ }}/"
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: secretKey
- name: WTF_CSRF_SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: wtfCsrfSecretKey
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: jwtSecret
- name: CKAN__PLUGINS
value: {{ .Values.ckan.plugins | join " " | quote }}
- name: CKAN__VIEWS__DEFAULT_VIEWS
value: {{ .Values.ckan.defaultViews | join " " | quote }}
{{- if .Values.ckan.extraEnvVars }}
{{- include "common.tplvalues.render" (dict "value" .Values.ckan.extraEnvVars "context" $) | nindent 12 }}
{{- end }}
securityContext:
{{- toYaml .Values.ckan.securityContext | default dict | nindent 12 }}
readinessProbe:
Expand Down Expand Up @@ -182,6 +207,4 @@ spec:
resources:
{{- toYaml .Values.ckan.resources | nindent 12 }}

#TODO Email notification
#TODO persist datapusher token else allway regen if ckan restart
#TODO support CKAN HA
181 changes: 181 additions & 0 deletions charts/ckan/templates/ckan/post-install.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
{{- $name := "ckan" -}}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ printf "%s-%s-post-install" (include "common.names.fullname" $) $name | quote }}
labels: {{- include "common.labels.standard" . | nindent 4 }}
app.kubernetes.io/component: {{ $name }}
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
namespace: {{ .Release.Namespace | quote }}
spec:
template:
spec:
serviceAccountName: {{ printf "%s-%s-serviceaccount" (include "common.names.fullname" $) $name | quote }}
restartPolicy: Never
initContainers:
- name: wait-for-postgresql
image: docker.io/postgres:17.1-alpine
command: [ 'sh', '-c', 'until pg_isready -U $CKAN_DB_USER -d $CKAN_DB -h {{ printf "%s-%s" (include "ckan.postgresql.fullname" . ) "primary" }} -p 5432; do echo waiting for database; sleep 2; done;' ]
env:
- name: CKAN_DB_USER
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: ckanDatabaseUsername
- name: CKAN_DB
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: ckanDatabase
- name: ckan-initiate
image: {{ include "common.images.image" (dict "imageRoot" .Values.ckan.image "global" .Values.global) }}
command: ["sh","-c","/srv/app/ckan-init.sh"]
env:
- name: CKAN_SYSADMIN_NAME
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: sysAdminUsername
- name: CKAN_SYSADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: sysAdminPassword
- name: CKAN_SYSADMIN_EMAIL
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: sysAdminEmail
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: postgresPassword
- name: POSTGRES_DB
value: postgres
- name: POSTGRES_HOST
value: {{ printf "%s-%s" (include "ckan.postgresql.fullname" . ) "primary" }}
- name: CKAN_DB_USER
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: ckanDatabaseUsername
- name: CKAN_DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: ckanDatabasePassword
- name: CKAN_DB
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: ckanDatabase
- name: DATASTORE_READONLY_USER
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: datastoreUsername
- name: DATASTORE_READONLY_PASSWORD
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: datastorePassword
- name: DATASTORE_DB
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.postgresql.fullname" . ) }}
key: datastoreDatabase
- name: CKAN_SQLALCHEMY_URL
value: "postgresql://$(CKAN_DB_USER):$(CKAN_DB_PASSWORD)@{{ printf "%s-%s" (include "ckan.postgresql.fullname" . ) "primary" }}/$(CKAN_DB)"
- name: CKAN_DATASTORE_WRITE_URL
value: "postgresql://$(CKAN_DB_USER):$(CKAN_DB_PASSWORD)@{{ printf "%s-%s" (include "ckan.postgresql.fullname" . ) "primary" }}/$(DATASTORE_DB)"
- name: CKAN_DATASTORE_READ_URL
value: "postgresql://$(DATASTORE_READONLY_USER):$(DATASTORE_READONLY_PASSWORD)@{{ printf "%s-%s" (include "ckan.postgresql.fullname" . ) "read" }}/$(DATASTORE_DB)"
- name: CKAN_SOLR_URL
value: "http://{{ printf "%s-%s" (include "ckan.solr.fullname" . ) "headless" }}:{{ include "ckan.solr.service.port" $ }}/solr/ckan"
{{- if .Values.solr.auth.enabled }}
- name: CKAN_SOLR_USER
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.solr.fullname" . ) }}
key: solrUsername
- name: CKAN_SOLR_PASSWORD
valueFrom:
secretKeyRef:
name: {{ printf "%s-config" (include "ckan.solr.fullname" . ) }}
key: solrPassword
{{- end }}
- name: CKAN_REDIS_URL
value: "redis://{{ printf "%s-%s" (include "ckan.redis.fullname" . ) "headless" }}:{{ include "ckan.redis.service.port" $}}/0"
- name: DATAPUSHER_API_TOKEN
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: datapusherApiKey
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: secretKey
- name: WTF_CSRF_SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: wtfCsrfSecretKey
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }}
key: jwtSecret
- name: EMAIL_API_KEY
valueFrom:
secretKeyRef:
name: {{ printf "%s-%s-config" (include "common.names.fullname" $) "ckan" }}
key: emailApiKey
volumeMounts:
- name: configmap-volume
mountPath: /srv/app/ckan-init.sh
readOnly: true
subPath: ckan-init.sh
- mountPath: /api-tokens
name: api-tokens-volume
- name: update-secret
image: docker.io/bitnami/kubectl
command:
- "/bin/sh"
- "-c"
- |
if [ "$(ls -A /api-tokens)" ]; then
if [ -f "/api-tokens/datapusherApiKey" ]; then
DATAPUSHER_API_TOKEN=$(cat /api-tokens/datapusherApiKey | tr -d '\n[:space:]' | base64 -w 0 ) &&
PATCH='[{"op": "replace", "path": "/data/datapusherApiKey", "value": "'"$DATAPUSHER_API_TOKEN"'"}]' &&
if [ $(kubectl get secret {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }} -o jsonpath='{.data.datapusherApiKey}' | tr -d '\n[:space:]' | wc -m) -eq 0 ];
then kubectl patch secrets {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }} --type json -p="$PATCH"; fi
fi
if [ -f "/api-tokens/emailApiKey" ]; then
EMAIL_API_KEY=$(cat /api-tokens/emailApiKey | tr -d '\n[:space:]' | base64 -w 0 ) &&
PATCH='[{"op": "replace", "path": "/data/emailApiKey", "value": "'"$EMAIL_API_KEY"'"}]' &&
if [ $(kubectl get secret {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }} -o jsonpath='{.data.emailApiKey}' | tr -d '\n[:space:]' | wc -m) -eq 0 ];
then kubectl patch secrets {{ printf "%s-%s-config" (include "common.names.fullname" $) $name }} --type json -p="$PATCH"; fi
fi
kubectl rollout restart deployment/{{ printf "%s-%s" (include "common.names.fullname" $) $name | quote }}
fi
volumeMounts:
- mountPath: /api-tokens
name: api-tokens-volume
containers:
- name: postinstall-config
image: docker.io/busybox:1.28
command: ["sh", "-c", "echo upgrade ready!"] # 92 is the uid and gid of ckan user/group
volumes:
- name: configmap-volume
configMap:
defaultMode: 0777
name: {{ printf "%s-%s-configmap" (include "common.names.fullname" .) "ckan" }}
- name: api-tokens-volume
emptyDir: {}
10 changes: 9 additions & 1 deletion charts/ckan/templates/ckan/secret.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{{- $name := (printf "%s-%s-config" (include "common.names.fullname" .) "ckan") -}}
{{- $sysAdminPassword := include "common.secrets.passwords.manage" (dict "secret" $name "length" 42 "strong" false "key" "sysAdminPassword" "providedValues" (list "ckan.sysadmin.password") "skipB64enc" true "context" (dict "Values" .Values "Release" ((dict "IsUpgrade" false "IsInstall" true "Namespace" .Release.Namespace) | mergeOverwrite (deepCopy .Release)))) }}
{{- $secretKey := include "common.secrets.passwords.manage" (dict "secret" $name "length" 42 "strong" false "key" "secretKey" "providedValues" (list "ckan.secretKey") "skipB64enc" true "context" (dict "Values" .Values "Release" ((dict "IsUpgrade" false "IsInstall" true "Namespace" .Release.Namespace) | mergeOverwrite (deepCopy .Release)))) }}
{{- $wtfCsrfSecretKey := include "common.secrets.passwords.manage" (dict "secret" $name "length" 42 "strong" false "key" "wtfCsrfSecretKey" "providedValues" (list "ckan.wtfCsrfSecretKey") "skipB64enc" true "context" (dict "Values" .Values "Release" ((dict "IsUpgrade" false "IsInstall" true "Namespace" .Release.Namespace) | mergeOverwrite (deepCopy .Release)))) }}
{{- $jwtSecret := include "common.secrets.passwords.manage" (dict "secret" $name "length" 42 "strong" false "key" "jwtSecret" "providedValues" (list "ckan.jwtSecret") "skipB64enc" true "context" (dict "Values" .Values "Release" ((dict "IsUpgrade" false "IsInstall" true "Namespace" .Release.Namespace) | mergeOverwrite (deepCopy .Release)))) }}
apiVersion: v1
kind: Secret
metadata:
Expand All @@ -11,6 +14,11 @@ stringData:
sysAdminUsername: {{ .Values.ckan.sysadmin.name | default "ckan_admin" | quote }}
sysAdminPassword: {{ $sysAdminPassword }}
sysAdminEmail: {{ .Values.ckan.sysadmin.email | default "[email protected]" | quote }}
secretKey: {{ $secretKey }}
wtfCsrfSecretKey: {{ $wtfCsrfSecretKey }}
jwtSecret: {{ $jwtSecret }}
datapusherApiKey: {{ "" }}
emailApiKey: {{ "" }}
{{- if .Values.ckan.smtp }}
smtpPassword: {{ .Values.ckan.smtp.password | quote }}
{{- end }}
{{- end }}
Loading

0 comments on commit a42d2f6

Please sign in to comment.