Skip to content

Commit

Permalink
add cron for incoming email check via IMAP (#160)
Browse files Browse the repository at this point in the history
* add cron for incoming email check via IMAP

* add support for existing imap cred secret, specs

* Create moody-papayas-tie.md

---------

Co-authored-by: Oliver Günther <[email protected]>
  • Loading branch information
machisuji and oliverguenther authored Dec 12, 2024
1 parent 3eb05ea commit a5f14c9
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-papayas-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@openproject/helm-charts": minor
---

Add support for the cron-based service for incoming email check via IMAP
24 changes: 22 additions & 2 deletions charts/openproject/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ persistence:
s3:
enabled: true
accessKeyId:
# host:
# host:
# port:
```

Expand Down Expand Up @@ -283,6 +283,9 @@ type: Opaque
```

To add the actual content, you can simply add `stringData:` to the end of it and save it.
Alternatively you can create the secret in one line as well via the `--from-literal` option.

**Secret keys**

The keys which are looked up inside the secret data can be changed from their defaults in the values as well. This is the same in all cases where next to `existingSecret` you can also set `secretKeys`.

Expand All @@ -296,6 +299,15 @@ stringData:
password: userPassword
```
Here an example how to do the same using the `--from-literal` option.
We won't give these examples for the other sections below but it works just the same.

```bash
kubectl -n openproject create secret generic db-credentials \
--from-literal=postgres-password=postgresPassword \
--from-literal=password=userPassword
```

If you have an existing secret where the keys are not `postgres-password` and `password`, you can customize the used keys as mentioned above.

For instance:
Expand All @@ -307,7 +319,7 @@ helm upgrade --create-namespace --namespace openproject --install openproject \
--set postgresql.auth.secretKeys.userPasswordKey=userpw
```

This can be customized for the the credentials in the following sections too in the same fashion.
This can also be customized for the the credentials in the following sections in the same fashion.
You can look up the respective options in the [`values.yaml`](./values.yaml) file.

#### Default passwords
Expand Down Expand Up @@ -348,6 +360,14 @@ stringData:
secretAccessKey: zwH7t0H3bJQf/TvlQpE7/Y59k9hD+nYNRlKUBpuq
```
### Incoming E-Mails cron job (IMAP)
```yaml
stringData:
imapUsername: [email protected]
imapPassword: t*$SFdD*RfahVTnoDr&Caw96FJuU
```
## OpenShift
For OpenProject to work in OpenShift without further adjustments,
Expand Down
106 changes: 106 additions & 0 deletions charts/openproject/templates/cron-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }}
kind: Deployment
metadata:
name: {{ include "common.names.fullname" . }}-cron
labels:
{{- include "common.labels.standard" . | nindent 4 }}
openproject/process: cron
spec:
replicas: {{ if .Values.cron.enabled }}{{- 1 }}{{ else }}{{- 0 }}{{ end }}
strategy:
type: "Recreate"
selector:
matchLabels:
{{- include "common.labels.matchLabels" . | nindent 6 }}
openproject/process: cron
template:
metadata:
annotations:
{{- range $key, $val := .Values.podAnnotations }}
{{ $key }}: {{ $val | quote }}
{{- end }}
{{- include "openproject.envChecksums" . | nindent 8 }}
checksum/env-cron-environment: {{ include (print $.Template.BasePath "/secret_cron_environment.yaml") $ | sha256sum }}
labels:
{{- include "common.labels.standard" . | nindent 8 }}
openproject/process: cron
spec:
{{- include "openproject.imagePullSecrets" . | indent 6 }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | nindent 8 | trim }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | nindent 8 | trim }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | nindent 8 | trim }}
{{- end }}
{{- include "openproject.podSecurityContext" . | indent 6 }}
serviceAccountName: {{ include "common.names.fullname" . }}
volumes:
{{- include "openproject.tmpVolumeSpec" . | indent 8 }}
{{- if .Values.egress.tls.rootCA.fileName }}
- name: ca-pemstore
configMap:
name: "{{- .Values.egress.tls.rootCA.configMap }}"
{{- end }}
{{- if .Values.persistence.enabled }}
- name: "data"
persistentVolumeClaim:
claimName: {{ include "common.names.fullname" . }}
{{- end }}
{{- include "openproject.extraVolumes" . | indent 8 }}
initContainers:
- name: wait-for-db
{{- include "openproject.containerSecurityContext" . | indent 10 }}
image: {{ include "openproject.image" . }}
imagePullPolicy: {{ .Values.image.imagePullPolicy }}
envFrom:
{{- include "openproject.envFrom" . | nindent 12 }}
- secretRef:
name: {{ include "common.names.fullname" . }}-cron-environment
env:
{{- include "openproject.env" . | nindent 12 }}
command:
- bash
- /app/docker/prod/wait-for-db
resources:
{{- toYaml .Values.appInit.resources | nindent 12 }}
containers:
- name: "cron"
{{- include "openproject.containerSecurityContext" . | indent 10 }}
image: {{ include "openproject.image" . }}
imagePullPolicy: {{ .Values.image.imagePullPolicy }}
envFrom:
{{- include "openproject.envFrom" . | nindent 12 }}
- secretRef:
name: {{ include "common.names.fullname" . }}-cron-environment
command:
- bash
- /app/docker/prod/cron
env:
{{- include "openproject.env" . | nindent 12 }}
volumeMounts:
{{- include "openproject.tmpVolumeMounts" . | indent 12 }}
{{- if .Values.persistence.enabled }}
- name: "data"
mountPath: "/var/openproject/assets"
{{- end }}
{{- if .Values.egress.tls.rootCA.fileName }}
- name: ca-pemstore
mountPath: /etc/ssl/certs/custom-ca.pem
subPath: {{ .Values.egress.tls.rootCA.fileName }}
readOnly: false
{{- end }}
{{- include "openproject.extraVolumeMounts" . | indent 12 }}
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1"
23 changes: 23 additions & 0 deletions charts/openproject/templates/secret_cron_environment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{- if .Values.cron.environment }}
---
apiVersion: "v1"
kind: "Secret"
metadata:
name: "{{ include "common.names.fullname" . }}-cron-environment"
labels:
{{- include "common.labels.standard" . | nindent 4 }}
data: # reset data to make sure only keys defined below remain
stringData:
# Additional environment variables
{{- range $key, $value := omit .Values.cron.environment "IMAP_USERNAME" "IMAP_PASSWORD" }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{ $secret := (lookup "v1" "Secret" .Release.Namespace (default "_" .Values.cron.existingSecret)) | default (dict "data" dict) -}}
IMAP_USERNAME: {{
default .Values.cron.environment.IMAP_USERNAME (get $secret.data .Values.cron.secretKeys.imapUsername | b64dec) | quote
}}
IMAP_PASSWORD: {{
default .Values.cron.environment.IMAP_PASSWORD (get $secret.data .Values.cron.secretKeys.imapPassword | b64dec) | quote
}}
...
{{- end }}
21 changes: 21 additions & 0 deletions charts/openproject/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,27 @@ strategy:
# maxSurge: 30%
# maxUnavailable: 30%

## Cron job running the incoming email [1] task.
##
## Ref: https://www.openproject.org/docs/installation-and-operations/configuration/incoming-emails/
cron:
enabled: false
## See documentation referenced above for all variables.
environment:
IMAP_HOST:
IMAP_USERNAME:
IMAP_PASSWORD:
IMAP_PORT: 993

## To avoid having sensitive credentials in your values.yaml, the preferred way is to
## use an existing secret containing the IMAP credentials.
## Specify the name of this existing secret here.
existingSecret:
## In case your secret does not use the default keys in the secret, you can adjust them here.
secretKeys:
imapUsername: imapUsername
imapPassword: imapPassword

# Define the workers to run, their queues, replicas, strategy, and resources
workers:
default:
Expand Down
95 changes: 95 additions & 0 deletions spec/charts/openproject/cron_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# frozen_string_literal: true
require 'spec_helper'

describe 'oidc configuration' do
let(:default_values) { {} }
let(:template) { HelmTemplate.new(default_values) }

let(:cron_definition) do
{
'Deployment/optest-openproject-cron' => 'cron'
}
end

let(:cron_secret_name) { 'optest-openproject-cron-environment' }

let(:general_definitions) do
{
'Deployment/optest-openproject-web' => 'openproject',
'Deployment/optest-openproject-worker-default' => 'openproject',
/optest-openproject-seeder/ => 'seeder'
}
end

let(:replicas) do
template.dig 'Deployment/optest-openproject-cron', 'spec', 'replicas'
end

it 'adds a secret ref to the cron container' do
ref = template.secret_ref cron_definition.keys.first, cron_definition.values.first, cron_secret_name

expect(Hash(ref).dig('secretRef', 'name')).to eq cron_secret_name
end

it 'does not add a secret ref to the other containers' do
general_definitions.each do |item, container|
expect(template.secret_ref(item, container, cron_secret_name)).to be_nil
end
end

context 'with cron.enabled=false (default)' do
it 'does not schedule a cron container', :aggregate_failures do
expect(replicas).to eq 0
end
end

context 'with cron.enabled=true' do
let(:default_values) do
HelmTemplate.with_defaults('
cron:
enabled: true
')
end

it 'does schedule a cron container', :aggregate_failures do
expect(replicas).to eq 1
end
end

describe 'cron environment secret' do
let(:cron_secret) do
template.dig('Secret/optest-openproject-cron-environment', 'stringData')
end

let(:expected_keys) do
%w[IMAP_HOST IMAP_PORT IMAP_USERNAME IMAP_PASSWORD]
end

context 'without an existing secret for the credentials configured' do
let(:default_values) do
HelmTemplate.with_defaults('
cron:
enabled: true
')
end

it 'contains the correct env variables', :aggregate_failures do
expect(cron_secret.keys).to contain_exactly(*expected_keys)
end
end

context 'with an existing secret for the credentials configured' do
let(:default_values) do
HelmTemplate.with_defaults('
cron:
enabled: true
existingSecret: imap-credentials
')
end

it 'contains the correct env variables', :aggregate_failures do
expect(cron_secret.keys).to contain_exactly(*expected_keys)
end
end
end
end

0 comments on commit a5f14c9

Please sign in to comment.