@bluedynamics/cdk8s-plone 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.claude/claude.md +39 -0
  2. package/.claude/settings.local.json +31 -0
  3. package/.jsii +4 -4
  4. package/README.md +15 -2
  5. package/documentation/sources/how-to/deploy-classic-ui.md +322 -0
  6. package/documentation/sources/how-to/deploy-production-volto.md +319 -0
  7. package/documentation/sources/how-to/index.md +13 -0
  8. package/documentation/sources/reference/api/index.md +29 -0
  9. package/examples/classic-ui/.env.example +19 -0
  10. package/examples/classic-ui/README.md +343 -0
  11. package/examples/classic-ui/__snapshots__/main.test.ts.snap +1242 -0
  12. package/examples/classic-ui/cdk8s.yaml +6 -0
  13. package/examples/classic-ui/config/varnish.tpl.vcl +217 -0
  14. package/examples/classic-ui/ingress.ts +217 -0
  15. package/examples/classic-ui/jest.config.js +11 -0
  16. package/examples/classic-ui/main.test.ts +11 -0
  17. package/examples/classic-ui/main.ts +100 -0
  18. package/examples/classic-ui/package-lock.json +5719 -0
  19. package/examples/classic-ui/package.json +36 -0
  20. package/examples/classic-ui/postgres.bitnami.ts +49 -0
  21. package/examples/classic-ui/postgres.cloudnativepg.ts +63 -0
  22. package/examples/production-volto/.env.example +20 -0
  23. package/examples/production-volto/README.md +295 -0
  24. package/examples/production-volto/__snapshots__/main.test.ts.snap +1412 -0
  25. package/examples/production-volto/cdk8s.yaml +6 -0
  26. package/examples/production-volto/config/varnish.tpl.vcl +297 -0
  27. package/examples/production-volto/ingress.ts +229 -0
  28. package/examples/production-volto/jest.config.js +11 -0
  29. package/examples/production-volto/main.test.ts +11 -0
  30. package/examples/production-volto/main.ts +104 -0
  31. package/examples/production-volto/package-lock.json +5714 -0
  32. package/examples/production-volto/package.json +36 -0
  33. package/examples/production-volto/postgres.bitnami.ts +49 -0
  34. package/examples/production-volto/postgres.cloudnativepg.ts +63 -0
  35. package/lib/httpcache.js +1 -1
  36. package/lib/plone.js +1 -1
  37. package/package.json +3 -3
@@ -0,0 +1,6 @@
1
+ language: typescript
2
+ app: npx ts-node main.ts
3
+ imports:
4
+ - k8s
5
+ - https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.24/config/crd/bases/postgresql.cnpg.io_clusters.yaml
6
+ - https://raw.githubusercontent.com/traefik/traefik/v3.2/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml
@@ -0,0 +1,297 @@
1
+ vcl 4.0;
2
+
3
+ import std;
4
+ import directors;
5
+
6
+ probe ploneBackendProbe {
7
+ .url = "/ok";
8
+ .timeout = 5s;
9
+ .interval = 15s;
10
+ .window = 10;
11
+ .threshold = 8;
12
+ # .initial = 3;
13
+ }
14
+ backend ploneBackend {
15
+ .host = "{{ .Env.BACKEND_SERVICE_NAME }}";
16
+ .port = "{{ .Env.BACKEND_SERVICE_PORT }}";
17
+ .probe = ploneBackendProbe;
18
+ .connect_timeout = 0.5s;
19
+ .first_byte_timeout = 120s;
20
+ .between_bytes_timeout = 60s;
21
+ }
22
+
23
+ probe ploneFrontendProbe {
24
+ .url = "/ok";
25
+ .timeout = 5s;
26
+ .interval = 15s;
27
+ .window = 10;
28
+ .threshold = 8;
29
+ # .initial = 3;
30
+ }
31
+ backend ploneFrontend {
32
+ .host = "{{ .Env.FRONTEND_SERVICE_NAME }}";
33
+ .port = "{{ .Env.FRONTEND_SERVICE_PORT }}";
34
+ .probe = ploneFrontendProbe;
35
+ .connect_timeout = 0.5s;
36
+ .first_byte_timeout = 120s;
37
+ .between_bytes_timeout = 60s;
38
+ }
39
+
40
+ /* Only allow PURGE from kubernetes network */
41
+ acl purge {
42
+ "10.0.0.0/8";
43
+ }
44
+
45
+ sub detect_debug{
46
+ # Requests with X-Varnish-Debug will display additional
47
+ # information about requests
48
+ unset req.http.x-vcl-debug;
49
+ # Should be changed after switch to live
50
+ #if (req.http.x-varnish-debug) {
51
+ # set req.http.x-vcl-debug = false;
52
+ #}
53
+ set req.http.x-vcl-debug = true;
54
+ }
55
+
56
+ sub detect_auth{
57
+ unset req.http.x-auth;
58
+ if (
59
+ (req.http.Cookie && (
60
+ req.http.Cookie ~ "__ac(_(name|password|persistent))?=" || req.http.Cookie ~ "_ZopeId" || req.http.Cookie ~ "auth_token")) ||
61
+ (req.http.Authenticate) ||
62
+ (req.http.Authorization)
63
+ ) {
64
+ set req.http.x-auth = true;
65
+ }
66
+ }
67
+
68
+ sub detect_requesttype{
69
+ unset req.http.x-varnish-reqtype;
70
+ set req.http.x-varnish-reqtype = "Default";
71
+ if (req.http.x-auth){
72
+ set req.http.x-varnish-reqtype = "auth";
73
+ } elseif (req.url ~ "\/@@(images|download|)\/?(.*)?$"){
74
+ set req.http.x-varnish-reqtype = "blob";
75
+ } elseif (req.url ~ "\/\+\+api\+\+/?(.*)?$") {
76
+ set req.http.x-varnish-reqtype = "api";
77
+ } else {
78
+ set req.http.x-varnish-reqtype = "express";
79
+ }
80
+ }
81
+
82
+ sub process_redirects{
83
+ if (req.http.x-redirect-to) {
84
+ return (synth(301, req.http.x-redirect-to));
85
+ }
86
+ }
87
+
88
+ sub vcl_init {
89
+ new lbPloneBackend = directors.round_robin();
90
+ lbPloneBackend.add_backend(ploneBackend);
91
+
92
+ new lbPloneFrontend = directors.round_robin();
93
+ lbPloneFrontend.add_backend(ploneFrontend);
94
+ }
95
+
96
+ sub vcl_recv {
97
+ # Annotate request with x-vcl-debug
98
+ call detect_debug;
99
+
100
+ # Annotate request with x-auth indicating if request is authenticated or not
101
+ call detect_auth;
102
+
103
+ # Annotate request with x-varnish-reqtype with a classification for the request
104
+ call detect_requesttype;
105
+
106
+ # Process redirects
107
+ call process_redirects;
108
+
109
+ # Routing: set the current Varnish backend to the matching Plone backend
110
+ # Attention:
111
+ # Confusing wording, we have two possible Varnish backends: the Plone frontend and the Plone backend *sigh*
112
+ if ((req.url ~ "^/\+\+api\+\+") || (req.http.x-varnish-reqtype ~ "blob")) {
113
+ set req.http.x-vcl-plone = "Backend";
114
+ set req.backend_hint = lbPloneBackend.backend();
115
+ # here we need a rewrite to add the virtualhost part of the URL for the Plone Backend
116
+ set req.http.x-vcl-proto = "http";
117
+ if (req.http.X-Forwarded-Proto) {
118
+ set req.http.x-vcl-proto = req.http.X-Forwarded-Proto;
119
+ }
120
+ set req.url = "/VirtualHostBase/" + req.http.x-vcl-proto + "/" + req.http.host + "/Plone/VirtualHostRoot" + req.url;
121
+ } else {
122
+ set req.http.x-vcl-plone = "Frontend";
123
+ set req.backend_hint = lbPloneFrontend.backend();
124
+ }
125
+
126
+ # short cut authenticated requests to pass
127
+ if (req.http.x-auth) {
128
+ return(pass);
129
+ }
130
+
131
+ # Sanitize cookies so they do not needlessly destroy cacheability for anonymous pages
132
+ if (req.http.Cookie) {
133
+ set req.http.Cookie = ";" + req.http.Cookie;
134
+ set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
135
+ set req.http.Cookie = regsuball(req.http.Cookie, ";(sticky|I18N_LANGUAGE|statusmessages|__ac|_ZopeId|__cp|beaker\.session|authomatic|serverid|__rf|auth_token)=", "; \1=");
136
+ set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
137
+ set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
138
+
139
+ if (req.http.Cookie == "") {
140
+ unset req.http.Cookie;
141
+ }
142
+ }
143
+
144
+ # Handle the different request types
145
+ if (req.method == "PURGE") {
146
+ if (!client.ip ~ purge) {
147
+ return (synth(405, "Not allowed."));
148
+ } else {
149
+ ban("req.url == " + req.url);
150
+ return (synth(200, "Purged."));
151
+ }
152
+
153
+ } elseif (req.method == "BAN") {
154
+ # Same ACL check as above:
155
+ if (!client.ip ~ purge) {
156
+ return (synth(405, "Not allowed."));
157
+ }
158
+ ban("req.http.host == " + req.http.host + "&& req.url == " + req.url);
159
+ # Throw a synthetic page so the
160
+ # request won't go to the backend.
161
+ return (synth(200, "Ban added"));
162
+
163
+ } elseif (req.method != "GET" &&
164
+ req.method != "HEAD" &&
165
+ req.method != "PUT" &&
166
+ req.method != "POST" &&
167
+ req.method != "PATCH" &&
168
+ req.method != "TRACE" &&
169
+ req.method != "OPTIONS" &&
170
+ req.method != "DELETE") {
171
+ /* Non-RFC2616 or CONNECT which is weird. */
172
+ return (pipe);
173
+ } elseif (req.method != "GET" &&
174
+ req.method != "HEAD" &&
175
+ req.method != "OPTIONS") {
176
+ /* POST, PUT, PATCH will pass, always */
177
+ return(pass);
178
+ }
179
+
180
+ return(hash);
181
+ }
182
+
183
+ sub vcl_pipe {
184
+ /* This is not necessary if you do not do any request rewriting. */
185
+ set req.http.connection = "close";
186
+ }
187
+
188
+ sub vcl_purge {
189
+ return (synth(200, "PURGE: " + req.url + " - " + req.hash));
190
+ }
191
+
192
+ sub vcl_synth {
193
+ if (resp.status == 301) {
194
+ set resp.http.location = resp.reason;
195
+ set resp.reason = "Moved";
196
+ return (deliver);
197
+ }
198
+ }
199
+
200
+ sub vcl_hit {
201
+ if (obj.ttl >= 0s) {
202
+ // A pure unadulterated hit, deliver it
203
+ return (deliver);
204
+ } elsif (obj.ttl + obj.grace > 0s) {
205
+ // Object is in grace, deliver it
206
+ // Automatically triggers a background fetch
207
+ return (deliver);
208
+ } else {
209
+ return (restart);
210
+ }
211
+ }
212
+
213
+
214
+ sub vcl_backend_response {
215
+ # Annotate request with info about the backend used
216
+ set beresp.http.x-varnish-plone-part = bereq.http.x-vcl-plone;
217
+ # set beresp.http.x-varnish-plone-proto = bereq.http.x-vcl-proto;
218
+ # set beresp.http.x-varnish-plone-host = bereq.http.x-vcl-host;
219
+ # Don't allow static files to set cookies.
220
+ # (?i) denotes case insensitive in PCRE (perl compatible regular expressions).
221
+ # make sure you edit both and keep them equal.
222
+ if (bereq.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|png|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
223
+ unset beresp.http.set-cookie;
224
+ }
225
+ if (beresp.http.Set-Cookie) {
226
+ set beresp.http.x-varnish-action = "FETCH (pass - response sets cookie)";
227
+ set beresp.uncacheable = true;
228
+ set beresp.ttl = 120s;
229
+ return(deliver);
230
+ }
231
+ if (beresp.http.Cache-Control ~ "(private|no-cache|no-store)") {
232
+ set beresp.http.x-varnish-action = "FETCH (pass - cache control disallows)";
233
+ set beresp.uncacheable = true;
234
+ set beresp.ttl = 120s;
235
+ return(deliver);
236
+ }
237
+
238
+ # if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") {
239
+ # Do NOT cache if there is an "Authorization" header
240
+ # beresp never has an Authorization header in beresp, right?
241
+ if (beresp.http.Authorization) {
242
+ set beresp.http.x-varnish-action = "FETCH (pass - authorized and no public cache control)";
243
+ set beresp.uncacheable = true;
244
+ set beresp.ttl = 120s;
245
+ return(deliver);
246
+ }
247
+
248
+ # Use this rule IF no cache-control (SSR content)
249
+ if ((bereq.http.x-varnish-reqtype ~ "express") && (!beresp.http.Cache-Control)) {
250
+ set beresp.http.x-varnish-action = "INSERT (30s caching / 60s grace)";
251
+ set beresp.uncacheable = false;
252
+ set beresp.ttl = 30s;
253
+ set beresp.grace = 60s;
254
+ return(deliver);
255
+ }
256
+
257
+ if (!beresp.http.Cache-Control) {
258
+ set beresp.http.x-varnish-action = "FETCH (override - backend not setting cache control)";
259
+ set beresp.uncacheable = true;
260
+ set beresp.ttl = 120s;
261
+ return (deliver);
262
+ }
263
+
264
+ if (beresp.http.X-Anonymous && !beresp.http.Cache-Control) {
265
+ set beresp.http.x-varnish-action = "FETCH (override - anonymous backend not setting cache control)";
266
+ set beresp.ttl = 600s;
267
+ return (deliver);
268
+ }
269
+
270
+ set beresp.http.x-varnish-action = "FETCH (insert)";
271
+ return (deliver);
272
+ }
273
+
274
+ sub vcl_deliver {
275
+
276
+ if (req.http.x-vcl-debug) {
277
+ set resp.http.x-varnish-ttl = obj.ttl;
278
+ set resp.http.x-varnish-grace = obj.grace;
279
+ set resp.http.x-hits = obj.hits;
280
+ set resp.http.x-varnish-reqtype = req.http.x-varnish-reqtype;
281
+ if (req.http.x-auth) {
282
+ set resp.http.x-auth = "Logged-in";
283
+ } else {
284
+ set resp.http.x-auth = "Anon";
285
+ }
286
+ if (obj.hits > 0) {
287
+ set resp.http.x-cache = "HIT";
288
+ } else {
289
+ set resp.http.x-cache = "MISS";
290
+ }
291
+ } else {
292
+ unset resp.http.x-varnish-action;
293
+ unset resp.http.x-cache-operation;
294
+ unset resp.http.x-cache-rule;
295
+ unset resp.http.x-powered-by;
296
+ }
297
+ }
@@ -0,0 +1,229 @@
1
+ import { Construct } from 'constructs';
2
+ import * as k8s from './imports/k8s';
3
+ import * as traefik from './imports/traefik.io';
4
+
5
+ export interface IngressOptions {
6
+
7
+ /**
8
+ * type: konq or traefik
9
+ * @default - traefik
10
+ */
11
+ readonly ingressType?: string;
12
+
13
+ /**
14
+ * issuer: cert-manager/cluster-issuer
15
+ * @default none
16
+ */
17
+ readonly issuer: string;
18
+
19
+ /**
20
+ * domainCached is the Domain used for the cached setup and should be the main public domain
21
+ * @default - none
22
+ */
23
+ readonly domainCached: string;
24
+
25
+ /**
26
+ * domainUncached is the Domain used for the uncached setup and should be the internal for testing pruposes only
27
+ * @default - none
28
+ */
29
+ readonly domainUncached: string;
30
+
31
+ /**
32
+ * domainMaintenance is the Domain to access the Plone backend (API) server for maintenance
33
+ * @default - none
34
+ */
35
+
36
+ readonly domainMaintenance: string;
37
+
38
+ /**
39
+ * frontendServiceName is the K8S Service name of the Plone frontend (Express)
40
+ * @default - none
41
+ */
42
+ readonly frontendServiceName: string;
43
+
44
+ /**
45
+ * backendServiceName is the K8S Service name of the Plone backend (API + Blobs)
46
+ * @default - none
47
+ */
48
+ readonly backendServiceName: string;
49
+
50
+ /**
51
+ * httpcacheServiceName is the K8S Service name of the http-cache (Varnish)
52
+ * @default - none
53
+ */
54
+ readonly httpcacheServiceName: string;
55
+ }
56
+
57
+
58
+ export class IngressChart extends Construct {
59
+
60
+ readonly issuer: string;
61
+
62
+ constructor(scope: Construct, id: string, options: IngressOptions) {
63
+ super(scope, id);
64
+ this.issuer = options.issuer;
65
+ if (options.ingressType === 'traefik') {
66
+ this.traefikIngress(
67
+ 'main',
68
+ 'cached',
69
+ options.domainCached,
70
+ '/',
71
+ options.httpcacheServiceName,
72
+ 80,
73
+ );
74
+ this.traefikIngress(
75
+ 'main',
76
+ 'uncached',
77
+ options.domainUncached,
78
+ `/`,
79
+ options.frontendServiceName,
80
+ 3000,
81
+ );
82
+ this.traefikIngress(
83
+ 'main',
84
+ 'maintenance',
85
+ options.domainMaintenance,
86
+ `/`,
87
+ options.backendServiceName,
88
+ 8080,
89
+ `/VirtualHostBase/https/${options.domainMaintenance}/VirtualHostRoot/`
90
+ );
91
+ } else if (options.ingressType === 'kong') {
92
+
93
+ // Create the ingress for the cached (main) domain
94
+ this.kongIngress(
95
+ 'main',
96
+ 'cached',
97
+ options.domainCached,
98
+ '/',
99
+ options.httpcacheServiceName,
100
+ 80,
101
+ );
102
+
103
+ // Create the two ingresses for the uncached (test) domain
104
+ // - to frontend:
105
+ this.kongIngress(
106
+ 'uncached',
107
+ 'frontend',
108
+ options.domainUncached,
109
+ '/',
110
+ options.frontendServiceName,
111
+ 3000,
112
+ );
113
+ // - to backend:
114
+ this.kongIngress(
115
+ 'uncached',
116
+ 'backend',
117
+ options.domainUncached,
118
+ '/~/((?:(?:\\+\\+api\\+\\+)(?:.*))|(?:(?:.*)/(?:(?:@@images|@@downloads)/.*)))',
119
+ options.backendServiceName,
120
+ 8080,
121
+ `/VirtualHostBase/https/${options.domainUncached}/Plone/VirtualHostRoot/$1`,
122
+ );
123
+
124
+ // Create the ingress for the maintenance
125
+ this.kongIngress(
126
+ 'maintenance',
127
+ 'backend',
128
+ options.domainMaintenance,
129
+ '/~/(.*)',
130
+ options.backendServiceName,
131
+ 8080,
132
+ `/VirtualHostBase/https/${options.domainMaintenance}/VirtualHostRoot/$1`
133
+ );
134
+ } else {
135
+ throw new Error('Unknown ingress type');
136
+ }
137
+ }
138
+
139
+ traefikIngress(prefix: string, postfix: string, domain: string, path: string, backendServiceName: string, backendPort: number, rewrite?: string) {
140
+ var annotations: { [key: string]: string } = {
141
+ 'kubernetes.io/ingress.class': 'traefik',
142
+ 'cert-manager.io/cluster-issuer': this.issuer,
143
+ };
144
+ if (rewrite !== undefined) {
145
+ const rewritemw = new traefik.Middleware(this, `${prefix}-${postfix}-addprefix`,
146
+ {
147
+ metadata: {},
148
+ spec: {
149
+ addPrefix: {
150
+ prefix: rewrite,
151
+ },
152
+ },
153
+ }
154
+ );
155
+ annotations['traefik.ingress.kubernetes.io/router.middlewares'] = `plone-${rewritemw.name}@kubernetescrd`;
156
+ }
157
+
158
+ new k8s.KubeIngress(this, `${prefix}-${postfix}`, {
159
+ metadata: {
160
+ annotations: annotations,
161
+ },
162
+ spec: {
163
+ ingressClassName: 'traefik',
164
+ rules: [{
165
+ host: domain,
166
+ http: {
167
+ paths: [{
168
+ backend: {
169
+ service: {
170
+ name: backendServiceName,
171
+ port: { number: backendPort },
172
+ },
173
+ },
174
+ path: path,
175
+ pathType: 'Prefix',
176
+ }],
177
+ },
178
+ }],
179
+ },
180
+ });
181
+
182
+ }
183
+
184
+ kongIngress(prefix: string, postfix: string, domain: string, path: string, backendServiceName: string, backendPort: number, rewrite?: string) {
185
+ /*
186
+ Create a konq general ingress
187
+
188
+ Properties:
189
+ - prefix: prefix for the ingress name used for grouping tls secrets
190
+ - postfix: postfix for the ingress name, used concatenated with the prefix as identifer for the ingress
191
+ - domain: domain for the ingress
192
+ - path: path for the ingress
193
+ - backendServiceName: name of the backend service
194
+ - backendPort: port of the backend service
195
+ - rewrite (optional): rewrite path for the ingress
196
+ */
197
+ var annotations: { [key: string]: string } = {
198
+ 'cert-manager.io/cluster-issuer': 'sectigo-issuer',
199
+ 'konghq.com/https-redirect-status-code': '308',
200
+ 'konghq.com/protocols': 'https',
201
+ };
202
+ if (rewrite !== undefined) {
203
+ annotations['konghq.com/rewrite'] = rewrite;
204
+ }
205
+ new k8s.KubeIngress(this, `${prefix}-${postfix}`, {
206
+ metadata: {
207
+ annotations: annotations,
208
+ },
209
+ spec: {
210
+ ingressClassName: 'kong',
211
+ rules: [{
212
+ host: domain,
213
+ http: {
214
+ paths: [{
215
+ backend: {
216
+ service: {
217
+ name: backendServiceName,
218
+ port: { number: backendPort },
219
+ },
220
+ },
221
+ path: path,
222
+ pathType: 'Prefix',
223
+ }],
224
+ },
225
+ }],
226
+ },
227
+ });
228
+ }
229
+ }
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ roots: [
3
+ "<rootDir>"
4
+ ],
5
+ moduleFileExtensions: ["ts", "tsx", "js", "mjs", "cjs", "jsx", "json", "node"],
6
+ testMatch: ['**/*.test.ts'],
7
+ transform: {
8
+ "^.+\\.tsx?$": "ts-jest"
9
+ },
10
+ testEnvironment: 'node',
11
+ }
@@ -0,0 +1,11 @@
1
+ import { ExampleChart } from './main';
2
+ import { Testing } from 'cdk8s';
3
+
4
+ describe('Production Volto Example', () => {
5
+ test('Synthesizes correctly', () => {
6
+ const app = Testing.app();
7
+ const chart = new ExampleChart(app, 'test-chart');
8
+ const results = Testing.synth(chart);
9
+ expect(results).toMatchSnapshot();
10
+ });
11
+ });
@@ -0,0 +1,104 @@
1
+ import { Construct } from 'constructs';
2
+ import { App, Chart, ChartProps } from 'cdk8s';
3
+ import { Plone, PloneHttpcache } from '@bluedynamics/cdk8s-plone';
4
+ import * as kplus from 'cdk8s-plus-30';
5
+ import * as path from 'path';
6
+ import { IngressChart } from './ingress';
7
+ import { config } from 'dotenv';
8
+ import { PGBitnamiChart } from './postgres.bitnami';
9
+ import { PGCloudNativePGChart } from './postgres.cloudnativepg';
10
+
11
+
12
+ export class ExampleChart extends Chart {
13
+ constructor(scope: Construct, id: string, props: ChartProps = {}) {
14
+ super(scope, id, props);
15
+
16
+ config();
17
+
18
+ // ================================================================================================================
19
+ // Postgresql
20
+ let db: PGBitnamiChart | PGCloudNativePGChart;
21
+ let postgresql_username;
22
+ let postgresql_password;
23
+ if ((process.env.DATABASE ?? 'bitnami') == 'cloudnativepg') {
24
+ const cloudnativepgDb = new PGCloudNativePGChart(this, 'db');
25
+ db = cloudnativepgDb;
26
+ // CloudNativePG creates secrets with format: {cluster-name}-app
27
+ // Use the CDK8S-generated cluster name, never hard-code
28
+ const secretName = `${cloudnativepgDb.clusterName}-app`;
29
+ postgresql_username = { valueFrom: { secretKeyRef: { name: secretName, key: 'username' }}};
30
+ postgresql_password = { valueFrom: { secretKeyRef: { name: secretName, key: 'password' }}};
31
+ } else {
32
+ const bitnamiDb = new PGBitnamiChart(this, 'db');
33
+ db = bitnamiDb;
34
+ postgresql_username = { value: 'plone' };
35
+ postgresql_password = { valueFrom: { secretKeyRef: { name: `${bitnamiDb.dbServiceName}`, key: 'password' }}};
36
+ }
37
+
38
+ // ================================================================================================================
39
+ // Plone
40
+
41
+
42
+ // prepare the environment variables for the plone deployment
43
+ const dbMDName = db.dbServiceName
44
+ const env = new kplus.Env(
45
+ [],
46
+ {
47
+ SECRET_POSTGRESQL_USERNAME: postgresql_username,
48
+ SECRET_POSTGRESQL_PASSWORD: postgresql_password,
49
+ INSTANCE_db_storage: { value: `relstorage` },
50
+ INSTANCE_db_blob_mode: { value: `cache` },
51
+ INSTANCE_db_cache_size: { value: `5000` },
52
+ INSTANCE_db_cache_size_bytes: { value: `1500MB` },
53
+ INSTANCE_db_relstorage: { value: `postgresql` },
54
+ INSTANCE_db_relstorage_postgresql_dsn: { value: `host='${dbMDName}' dbname='plone' user='$(SECRET_POSTGRESQL_USERNAME)' password='$(SECRET_POSTGRESQL_PASSWORD)'` },
55
+ INSTANCE_db_relstorage_cache_local_mb: { value: `800` },
56
+ },
57
+ );
58
+
59
+ // create the plone deployment and related resources
60
+ const plone = new Plone(this, 'plone', {
61
+ version: 'test.version',
62
+ backend: {
63
+ image: process.env.PLONE_BACKEND_IMAGE ?? 'plone/plone-backend:6.1.3',
64
+ environment: env,
65
+ },
66
+ frontend: {
67
+ image: process.env.PLONE_FRONTEND_IMAGE ?? 'plone/plone-frontend:latest',
68
+ readinessEnabled: false,
69
+ },
70
+ })
71
+
72
+ // ================================================================================================================
73
+ // Varnish with kube-httpcache
74
+ const httpcache = new PloneHttpcache(
75
+ this,
76
+ 'httpcache',
77
+ {
78
+ plone: plone,
79
+ varnishVclFile: path.join(__dirname, 'config', 'varnish.tpl.vcl'),
80
+ }
81
+ )
82
+
83
+ // ================================================================================================================
84
+ // Ingress
85
+ new IngressChart(
86
+ this,
87
+ 'ingress',
88
+ {
89
+ ingressType: 'traefik',
90
+ issuer: process.env.CLUSTER_ISSUER ?? 'letsencrypt-prod',
91
+ domainCached: process.env.DOMAIN_CACHED ?? 'mxplone-cached.example.com',
92
+ domainUncached: process.env.DOMAIN_UNCACHED ?? 'mxplone-cached.example.com',
93
+ domainMaintenance: process.env.DOMAIN_MAINTENANCE ?? 'mxplone-maintenance.example.com',
94
+ backendServiceName: plone.backendServiceName,
95
+ frontendServiceName: plone.frontendServiceName ?? '',
96
+ httpcacheServiceName: httpcache.httpcacheServiceName,
97
+ });
98
+ }
99
+ }
100
+
101
+
102
+ const app = new App();
103
+ new ExampleChart(app, 'plone-example');
104
+ app.synth();