Skip to content

Commit a5f14c9

Browse files
add cron for incoming email check via IMAP (#160)
* 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]>
1 parent 3eb05ea commit a5f14c9

File tree

6 files changed

+272
-2
lines changed

6 files changed

+272
-2
lines changed

.changeset/moody-papayas-tie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@openproject/helm-charts": minor
3+
---
4+
5+
Add support for the cron-based service for incoming email check via IMAP

charts/openproject/README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ persistence:
126126
s3:
127127
enabled: true
128128
accessKeyId:
129-
# host:
129+
# host:
130130
# port:
131131
```
132132

@@ -283,6 +283,9 @@ type: Opaque
283283
```
284284

285285
To add the actual content, you can simply add `stringData:` to the end of it and save it.
286+
Alternatively you can create the secret in one line as well via the `--from-literal` option.
287+
288+
**Secret keys**
286289

287290
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`.
288291

@@ -296,6 +299,15 @@ stringData:
296299
password: userPassword
297300
```
298301
302+
Here an example how to do the same using the `--from-literal` option.
303+
We won't give these examples for the other sections below but it works just the same.
304+
305+
```bash
306+
kubectl -n openproject create secret generic db-credentials \
307+
--from-literal=postgres-password=postgresPassword \
308+
--from-literal=password=userPassword
309+
```
310+
299311
If you have an existing secret where the keys are not `postgres-password` and `password`, you can customize the used keys as mentioned above.
300312

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

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

313325
#### Default passwords
@@ -348,6 +360,14 @@ stringData:
348360
secretAccessKey: zwH7t0H3bJQf/TvlQpE7/Y59k9hD+nYNRlKUBpuq
349361
```
350362
363+
### Incoming E-Mails cron job (IMAP)
364+
365+
```yaml
366+
stringData:
367+
imapUsername: [email protected]
368+
imapPassword: t*$SFdD*RfahVTnoDr&Caw96FJuU
369+
```
370+
351371
## OpenShift
352372
353373
For OpenProject to work in OpenShift without further adjustments,
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
---
2+
apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }}
3+
kind: Deployment
4+
metadata:
5+
name: {{ include "common.names.fullname" . }}-cron
6+
labels:
7+
{{- include "common.labels.standard" . | nindent 4 }}
8+
openproject/process: cron
9+
spec:
10+
replicas: {{ if .Values.cron.enabled }}{{- 1 }}{{ else }}{{- 0 }}{{ end }}
11+
strategy:
12+
type: "Recreate"
13+
selector:
14+
matchLabels:
15+
{{- include "common.labels.matchLabels" . | nindent 6 }}
16+
openproject/process: cron
17+
template:
18+
metadata:
19+
annotations:
20+
{{- range $key, $val := .Values.podAnnotations }}
21+
{{ $key }}: {{ $val | quote }}
22+
{{- end }}
23+
{{- include "openproject.envChecksums" . | nindent 8 }}
24+
checksum/env-cron-environment: {{ include (print $.Template.BasePath "/secret_cron_environment.yaml") $ | sha256sum }}
25+
labels:
26+
{{- include "common.labels.standard" . | nindent 8 }}
27+
openproject/process: cron
28+
spec:
29+
{{- include "openproject.imagePullSecrets" . | indent 6 }}
30+
{{- with .Values.affinity }}
31+
affinity:
32+
{{ toYaml . | nindent 8 | trim }}
33+
{{- end }}
34+
{{- with .Values.tolerations }}
35+
tolerations:
36+
{{ toYaml . | nindent 8 | trim }}
37+
{{- end }}
38+
{{- with .Values.nodeSelector }}
39+
nodeSelector:
40+
{{ toYaml . | nindent 8 | trim }}
41+
{{- end }}
42+
{{- include "openproject.podSecurityContext" . | indent 6 }}
43+
serviceAccountName: {{ include "common.names.fullname" . }}
44+
volumes:
45+
{{- include "openproject.tmpVolumeSpec" . | indent 8 }}
46+
{{- if .Values.egress.tls.rootCA.fileName }}
47+
- name: ca-pemstore
48+
configMap:
49+
name: "{{- .Values.egress.tls.rootCA.configMap }}"
50+
{{- end }}
51+
{{- if .Values.persistence.enabled }}
52+
- name: "data"
53+
persistentVolumeClaim:
54+
claimName: {{ include "common.names.fullname" . }}
55+
{{- end }}
56+
{{- include "openproject.extraVolumes" . | indent 8 }}
57+
initContainers:
58+
- name: wait-for-db
59+
{{- include "openproject.containerSecurityContext" . | indent 10 }}
60+
image: {{ include "openproject.image" . }}
61+
imagePullPolicy: {{ .Values.image.imagePullPolicy }}
62+
envFrom:
63+
{{- include "openproject.envFrom" . | nindent 12 }}
64+
- secretRef:
65+
name: {{ include "common.names.fullname" . }}-cron-environment
66+
env:
67+
{{- include "openproject.env" . | nindent 12 }}
68+
command:
69+
- bash
70+
- /app/docker/prod/wait-for-db
71+
resources:
72+
{{- toYaml .Values.appInit.resources | nindent 12 }}
73+
containers:
74+
- name: "cron"
75+
{{- include "openproject.containerSecurityContext" . | indent 10 }}
76+
image: {{ include "openproject.image" . }}
77+
imagePullPolicy: {{ .Values.image.imagePullPolicy }}
78+
envFrom:
79+
{{- include "openproject.envFrom" . | nindent 12 }}
80+
- secretRef:
81+
name: {{ include "common.names.fullname" . }}-cron-environment
82+
command:
83+
- bash
84+
- /app/docker/prod/cron
85+
env:
86+
{{- include "openproject.env" . | nindent 12 }}
87+
volumeMounts:
88+
{{- include "openproject.tmpVolumeMounts" . | indent 12 }}
89+
{{- if .Values.persistence.enabled }}
90+
- name: "data"
91+
mountPath: "/var/openproject/assets"
92+
{{- end }}
93+
{{- if .Values.egress.tls.rootCA.fileName }}
94+
- name: ca-pemstore
95+
mountPath: /etc/ssl/certs/custom-ca.pem
96+
subPath: {{ .Values.egress.tls.rootCA.fileName }}
97+
readOnly: false
98+
{{- end }}
99+
{{- include "openproject.extraVolumeMounts" . | indent 12 }}
100+
resources:
101+
requests:
102+
memory: "256Mi"
103+
cpu: "250m"
104+
limits:
105+
memory: "1Gi"
106+
cpu: "1"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{{- if .Values.cron.environment }}
2+
---
3+
apiVersion: "v1"
4+
kind: "Secret"
5+
metadata:
6+
name: "{{ include "common.names.fullname" . }}-cron-environment"
7+
labels:
8+
{{- include "common.labels.standard" . | nindent 4 }}
9+
data: # reset data to make sure only keys defined below remain
10+
stringData:
11+
# Additional environment variables
12+
{{- range $key, $value := omit .Values.cron.environment "IMAP_USERNAME" "IMAP_PASSWORD" }}
13+
{{ $key }}: {{ $value | quote }}
14+
{{- end }}
15+
{{ $secret := (lookup "v1" "Secret" .Release.Namespace (default "_" .Values.cron.existingSecret)) | default (dict "data" dict) -}}
16+
IMAP_USERNAME: {{
17+
default .Values.cron.environment.IMAP_USERNAME (get $secret.data .Values.cron.secretKeys.imapUsername | b64dec) | quote
18+
}}
19+
IMAP_PASSWORD: {{
20+
default .Values.cron.environment.IMAP_PASSWORD (get $secret.data .Values.cron.secretKeys.imapPassword | b64dec) | quote
21+
}}
22+
...
23+
{{- end }}

charts/openproject/values.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,27 @@ strategy:
266266
# maxSurge: 30%
267267
# maxUnavailable: 30%
268268

269+
## Cron job running the incoming email [1] task.
270+
##
271+
## Ref: https://www.openproject.org/docs/installation-and-operations/configuration/incoming-emails/
272+
cron:
273+
enabled: false
274+
## See documentation referenced above for all variables.
275+
environment:
276+
IMAP_HOST:
277+
IMAP_USERNAME:
278+
IMAP_PASSWORD:
279+
IMAP_PORT: 993
280+
281+
## To avoid having sensitive credentials in your values.yaml, the preferred way is to
282+
## use an existing secret containing the IMAP credentials.
283+
## Specify the name of this existing secret here.
284+
existingSecret:
285+
## In case your secret does not use the default keys in the secret, you can adjust them here.
286+
secretKeys:
287+
imapUsername: imapUsername
288+
imapPassword: imapPassword
289+
269290
# Define the workers to run, their queues, replicas, strategy, and resources
270291
workers:
271292
default:

spec/charts/openproject/cron_spec.rb

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# frozen_string_literal: true
2+
require 'spec_helper'
3+
4+
describe 'oidc configuration' do
5+
let(:default_values) { {} }
6+
let(:template) { HelmTemplate.new(default_values) }
7+
8+
let(:cron_definition) do
9+
{
10+
'Deployment/optest-openproject-cron' => 'cron'
11+
}
12+
end
13+
14+
let(:cron_secret_name) { 'optest-openproject-cron-environment' }
15+
16+
let(:general_definitions) do
17+
{
18+
'Deployment/optest-openproject-web' => 'openproject',
19+
'Deployment/optest-openproject-worker-default' => 'openproject',
20+
/optest-openproject-seeder/ => 'seeder'
21+
}
22+
end
23+
24+
let(:replicas) do
25+
template.dig 'Deployment/optest-openproject-cron', 'spec', 'replicas'
26+
end
27+
28+
it 'adds a secret ref to the cron container' do
29+
ref = template.secret_ref cron_definition.keys.first, cron_definition.values.first, cron_secret_name
30+
31+
expect(Hash(ref).dig('secretRef', 'name')).to eq cron_secret_name
32+
end
33+
34+
it 'does not add a secret ref to the other containers' do
35+
general_definitions.each do |item, container|
36+
expect(template.secret_ref(item, container, cron_secret_name)).to be_nil
37+
end
38+
end
39+
40+
context 'with cron.enabled=false (default)' do
41+
it 'does not schedule a cron container', :aggregate_failures do
42+
expect(replicas).to eq 0
43+
end
44+
end
45+
46+
context 'with cron.enabled=true' do
47+
let(:default_values) do
48+
HelmTemplate.with_defaults('
49+
cron:
50+
enabled: true
51+
')
52+
end
53+
54+
it 'does schedule a cron container', :aggregate_failures do
55+
expect(replicas).to eq 1
56+
end
57+
end
58+
59+
describe 'cron environment secret' do
60+
let(:cron_secret) do
61+
template.dig('Secret/optest-openproject-cron-environment', 'stringData')
62+
end
63+
64+
let(:expected_keys) do
65+
%w[IMAP_HOST IMAP_PORT IMAP_USERNAME IMAP_PASSWORD]
66+
end
67+
68+
context 'without an existing secret for the credentials configured' do
69+
let(:default_values) do
70+
HelmTemplate.with_defaults('
71+
cron:
72+
enabled: true
73+
')
74+
end
75+
76+
it 'contains the correct env variables', :aggregate_failures do
77+
expect(cron_secret.keys).to contain_exactly(*expected_keys)
78+
end
79+
end
80+
81+
context 'with an existing secret for the credentials configured' do
82+
let(:default_values) do
83+
HelmTemplate.with_defaults('
84+
cron:
85+
enabled: true
86+
existingSecret: imap-credentials
87+
')
88+
end
89+
90+
it 'contains the correct env variables', :aggregate_failures do
91+
expect(cron_secret.keys).to contain_exactly(*expected_keys)
92+
end
93+
end
94+
end
95+
end

0 commit comments

Comments
 (0)