@bluedynamics/cdk8s-monitoring 0.0.1

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 (47) hide show
  1. package/CLAUDE.md +76 -0
  2. package/LICENSE +202 -0
  3. package/README.md +87 -0
  4. package/examples/synth.ts +17 -0
  5. package/lib/constructs/alloy.d.ts +29 -0
  6. package/lib/constructs/alloy.js +214 -0
  7. package/lib/constructs/grafana-password-secret.d.ts +57 -0
  8. package/lib/constructs/grafana-password-secret.js +99 -0
  9. package/lib/constructs/loki-s3-bucket.d.ts +24 -0
  10. package/lib/constructs/loki-s3-bucket.js +147 -0
  11. package/lib/constructs/loki-s3-credentials.d.ts +27 -0
  12. package/lib/constructs/loki-s3-credentials.js +71 -0
  13. package/lib/constructs/loki.d.ts +30 -0
  14. package/lib/constructs/loki.js +185 -0
  15. package/lib/constructs/namespace.d.ts +13 -0
  16. package/lib/constructs/namespace.js +34 -0
  17. package/lib/constructs/priority-class.d.ts +12 -0
  18. package/lib/constructs/priority-class.js +38 -0
  19. package/lib/constructs/prometheus-stack.d.ts +30 -0
  20. package/lib/constructs/prometheus-stack.js +477 -0
  21. package/lib/constructs/thanos-compactor.d.ts +35 -0
  22. package/lib/constructs/thanos-compactor.js +198 -0
  23. package/lib/constructs/thanos-query.d.ts +30 -0
  24. package/lib/constructs/thanos-query.js +205 -0
  25. package/lib/constructs/thanos-s3-bucket.d.ts +24 -0
  26. package/lib/constructs/thanos-s3-bucket.js +147 -0
  27. package/lib/constructs/thanos-s3-credentials.d.ts +27 -0
  28. package/lib/constructs/thanos-s3-credentials.js +93 -0
  29. package/lib/constructs/thanos-store.d.ts +30 -0
  30. package/lib/constructs/thanos-store.js +239 -0
  31. package/lib/default-config.d.ts +12 -0
  32. package/lib/default-config.js +77 -0
  33. package/lib/imports/external-secrets.io.d.ts +39243 -0
  34. package/lib/imports/external-secrets.io.js +23112 -0
  35. package/lib/imports/generators.external-secrets.io.d.ts +7831 -0
  36. package/lib/imports/generators.external-secrets.io.js +4895 -0
  37. package/lib/imports/k8s.d.ts +24537 -0
  38. package/lib/imports/k8s.js +18268 -0
  39. package/lib/imports/s3.aws.upbound.io.d.ts +2882 -0
  40. package/lib/imports/s3.aws.upbound.io.js +1828 -0
  41. package/lib/index.d.ts +17 -0
  42. package/lib/index.js +38 -0
  43. package/lib/monitoring-chart.d.ts +39 -0
  44. package/lib/monitoring-chart.js +127 -0
  45. package/lib/types.d.ts +142 -0
  46. package/lib/types.js +3 -0
  47. package/package.json +117 -0
@@ -0,0 +1,57 @@
1
+ import { Construct } from 'constructs';
2
+ import { ExternalSecret } from '../imports/external-secrets.io';
3
+ export interface GrafanaPasswordSecretProps {
4
+ readonly namespace: string;
5
+ }
6
+ /**
7
+ * Creates ExternalSecret for stable Grafana admin credentials.
8
+ *
9
+ * This construct creates an ExternalSecret that replicates the Grafana admin password
10
+ * from the application-secrets namespace to the monitoring namespace, following the
11
+ * centralized secret management pattern.
12
+ *
13
+ * Prerequisites:
14
+ * - External Secrets Operator installed
15
+ * - ClusterSecretStore 'monitoring-app-secrets-store' exists (points to application-secrets)
16
+ * - Secret 'grafana-admin' exists in application-secrets namespace with keys:
17
+ * - admin-user
18
+ * - admin-password
19
+ *
20
+ * Security Model:
21
+ * - Source secret stored in centralized application-secrets namespace
22
+ * - ExternalSecret CR (this) committed to git (GitOps-safe, no password in git)
23
+ * - ESO automatically replicates secret to monitoring namespace
24
+ * - Password persists across Helm upgrades (no auto-regeneration)
25
+ *
26
+ * Benefits:
27
+ * - Centralized secret management (all app secrets in application-secrets namespace)
28
+ * - GitOps-safe (ExternalSecret in git, actual password not in git)
29
+ * - Password rotation (update source → auto-sync to monitoring)
30
+ * - Follows GitLab BDA security model (see documentation/sources/deployments/gitlabbda/explanation/security-model.md)
31
+ *
32
+ * Usage in prometheus-stack.ts:
33
+ * ```yaml
34
+ * grafana:
35
+ * admin:
36
+ * existingSecret: "grafana-admin-credentials"
37
+ * userKey: "admin-user"
38
+ * passwordKey: "admin-password"
39
+ * ```
40
+ *
41
+ * Manual Setup Required:
42
+ * 1. Create source secret in application-secrets namespace:
43
+ * ```bash
44
+ * kubectl create secret generic grafana-admin -n application-secrets \
45
+ * --from-literal=admin-user=admin \
46
+ * --from-literal=admin-password='YourSecurePassword123!'
47
+ * ```
48
+ *
49
+ * 2. Create ClusterSecretStore (if not exists):
50
+ * See: documentation/sources/deployments/monitoring/how-to/setup-grafana-password.md
51
+ *
52
+ * 3. Deploy monitoring stack (this ExternalSecret will sync the secret)
53
+ */
54
+ export declare class GrafanaPasswordSecret extends Construct {
55
+ readonly externalSecret: ExternalSecret;
56
+ constructor(scope: Construct, id: string, props: GrafanaPasswordSecretProps);
57
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GrafanaPasswordSecret = void 0;
4
+ const constructs_1 = require("constructs");
5
+ const external_secrets_io_1 = require("../imports/external-secrets.io");
6
+ /**
7
+ * Creates ExternalSecret for stable Grafana admin credentials.
8
+ *
9
+ * This construct creates an ExternalSecret that replicates the Grafana admin password
10
+ * from the application-secrets namespace to the monitoring namespace, following the
11
+ * centralized secret management pattern.
12
+ *
13
+ * Prerequisites:
14
+ * - External Secrets Operator installed
15
+ * - ClusterSecretStore 'monitoring-app-secrets-store' exists (points to application-secrets)
16
+ * - Secret 'grafana-admin' exists in application-secrets namespace with keys:
17
+ * - admin-user
18
+ * - admin-password
19
+ *
20
+ * Security Model:
21
+ * - Source secret stored in centralized application-secrets namespace
22
+ * - ExternalSecret CR (this) committed to git (GitOps-safe, no password in git)
23
+ * - ESO automatically replicates secret to monitoring namespace
24
+ * - Password persists across Helm upgrades (no auto-regeneration)
25
+ *
26
+ * Benefits:
27
+ * - Centralized secret management (all app secrets in application-secrets namespace)
28
+ * - GitOps-safe (ExternalSecret in git, actual password not in git)
29
+ * - Password rotation (update source → auto-sync to monitoring)
30
+ * - Follows GitLab BDA security model (see documentation/sources/deployments/gitlabbda/explanation/security-model.md)
31
+ *
32
+ * Usage in prometheus-stack.ts:
33
+ * ```yaml
34
+ * grafana:
35
+ * admin:
36
+ * existingSecret: "grafana-admin-credentials"
37
+ * userKey: "admin-user"
38
+ * passwordKey: "admin-password"
39
+ * ```
40
+ *
41
+ * Manual Setup Required:
42
+ * 1. Create source secret in application-secrets namespace:
43
+ * ```bash
44
+ * kubectl create secret generic grafana-admin -n application-secrets \
45
+ * --from-literal=admin-user=admin \
46
+ * --from-literal=admin-password='YourSecurePassword123!'
47
+ * ```
48
+ *
49
+ * 2. Create ClusterSecretStore (if not exists):
50
+ * See: documentation/sources/deployments/monitoring/how-to/setup-grafana-password.md
51
+ *
52
+ * 3. Deploy monitoring stack (this ExternalSecret will sync the secret)
53
+ */
54
+ class GrafanaPasswordSecret extends constructs_1.Construct {
55
+ constructor(scope, id, props) {
56
+ super(scope, id);
57
+ const { namespace } = props;
58
+ // Create ExternalSecret to replicate Grafana credentials from application-secrets
59
+ // Wave 1: Credentials must exist before Grafana starts (Wave 2)
60
+ this.externalSecret = new external_secrets_io_1.ExternalSecret(this, 'external-secret', {
61
+ metadata: {
62
+ name: 'grafana-admin-credentials-es',
63
+ namespace,
64
+ labels: {
65
+ 'app.kubernetes.io/name': 'grafana',
66
+ 'app.kubernetes.io/component': 'credentials',
67
+ 'app.kubernetes.io/managed-by': 'external-secrets',
68
+ },
69
+ annotations: {
70
+ 'argocd.argoproj.io/sync-wave': '1',
71
+ },
72
+ },
73
+ spec: {
74
+ // Sync every hour to detect password rotation
75
+ refreshInterval: '1h',
76
+ // Reference to ClusterSecretStore (application-secrets → monitoring)
77
+ secretStoreRef: {
78
+ name: 'monitoring-app-secrets-store',
79
+ kind: external_secrets_io_1.ExternalSecretSpecSecretStoreRefKind.CLUSTER_SECRET_STORE,
80
+ },
81
+ // Target secret name that Grafana will use
82
+ target: {
83
+ name: 'grafana-admin-credentials',
84
+ creationPolicy: external_secrets_io_1.ExternalSecretSpecTargetCreationPolicy.OWNER,
85
+ },
86
+ // Copy all data from source secret (admin-user, admin-password)
87
+ dataFrom: [
88
+ {
89
+ extract: {
90
+ key: 'grafana-admin',
91
+ },
92
+ },
93
+ ],
94
+ },
95
+ });
96
+ }
97
+ }
98
+ exports.GrafanaPasswordSecret = GrafanaPasswordSecret;
99
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JhZmFuYS1wYXNzd29yZC1zZWNyZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29uc3RydWN0cy9ncmFmYW5hLXBhc3N3b3JkLXNlY3JldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwyQ0FBdUM7QUFDdkMsd0VBSXdDO0FBTXhDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQStDRztBQUNILE1BQWEscUJBQXNCLFNBQVEsc0JBQVM7SUFHbEQsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUFpQztRQUN6RSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRWpCLE1BQU0sRUFBRSxTQUFTLEVBQUUsR0FBRyxLQUFLLENBQUM7UUFFNUIsa0ZBQWtGO1FBQ2xGLGdFQUFnRTtRQUNoRSxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksb0NBQWMsQ0FBQyxJQUFJLEVBQUUsaUJBQWlCLEVBQUU7WUFDaEUsUUFBUSxFQUFFO2dCQUNSLElBQUksRUFBRSw4QkFBOEI7Z0JBQ3BDLFNBQVM7Z0JBQ1QsTUFBTSxFQUFFO29CQUNOLHdCQUF3QixFQUFFLFNBQVM7b0JBQ25DLDZCQUE2QixFQUFFLGFBQWE7b0JBQzVDLDhCQUE4QixFQUFFLGtCQUFrQjtpQkFDbkQ7Z0JBQ0QsV0FBVyxFQUFFO29CQUNYLDhCQUE4QixFQUFFLEdBQUc7aUJBQ3BDO2FBQ0Y7WUFDRCxJQUFJLEVBQUU7Z0JBQ0osOENBQThDO2dCQUM5QyxlQUFlLEVBQUUsSUFBSTtnQkFFckIscUVBQXFFO2dCQUNyRSxjQUFjLEVBQUU7b0JBQ2QsSUFBSSxFQUFFLDhCQUE4QjtvQkFDcEMsSUFBSSxFQUFFLDBEQUFvQyxDQUFDLG9CQUFvQjtpQkFDaEU7Z0JBRUQsMkNBQTJDO2dCQUMzQyxNQUFNLEVBQUU7b0JBQ04sSUFBSSxFQUFFLDJCQUEyQjtvQkFDakMsY0FBYyxFQUFFLDREQUFzQyxDQUFDLEtBQUs7aUJBQzdEO2dCQUVELGdFQUFnRTtnQkFDaEUsUUFBUSxFQUFFO29CQUNSO3dCQUNFLE9BQU8sRUFBRTs0QkFDUCxHQUFHLEVBQUUsZUFBZTt5QkFDckI7cUJBQ0Y7aUJBQ0Y7YUFDRjtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQWxERCxzREFrREMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7XG4gIEV4dGVybmFsU2VjcmV0LFxuICBFeHRlcm5hbFNlY3JldFNwZWNUYXJnZXRDcmVhdGlvblBvbGljeSxcbiAgRXh0ZXJuYWxTZWNyZXRTcGVjU2VjcmV0U3RvcmVSZWZLaW5kLFxufSBmcm9tICcuLi9pbXBvcnRzL2V4dGVybmFsLXNlY3JldHMuaW8nO1xuXG5leHBvcnQgaW50ZXJmYWNlIEdyYWZhbmFQYXNzd29yZFNlY3JldFByb3BzIHtcbiAgcmVhZG9ubHkgbmFtZXNwYWNlOiBzdHJpbmc7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBFeHRlcm5hbFNlY3JldCBmb3Igc3RhYmxlIEdyYWZhbmEgYWRtaW4gY3JlZGVudGlhbHMuXG4gKlxuICogVGhpcyBjb25zdHJ1Y3QgY3JlYXRlcyBhbiBFeHRlcm5hbFNlY3JldCB0aGF0IHJlcGxpY2F0ZXMgdGhlIEdyYWZhbmEgYWRtaW4gcGFzc3dvcmRcbiAqIGZyb20gdGhlIGFwcGxpY2F0aW9uLXNlY3JldHMgbmFtZXNwYWNlIHRvIHRoZSBtb25pdG9yaW5nIG5hbWVzcGFjZSwgZm9sbG93aW5nIHRoZVxuICogY2VudHJhbGl6ZWQgc2VjcmV0IG1hbmFnZW1lbnQgcGF0dGVybi5cbiAqXG4gKiBQcmVyZXF1aXNpdGVzOlxuICogLSBFeHRlcm5hbCBTZWNyZXRzIE9wZXJhdG9yIGluc3RhbGxlZFxuICogLSBDbHVzdGVyU2VjcmV0U3RvcmUgJ21vbml0b3JpbmctYXBwLXNlY3JldHMtc3RvcmUnIGV4aXN0cyAocG9pbnRzIHRvIGFwcGxpY2F0aW9uLXNlY3JldHMpXG4gKiAtIFNlY3JldCAnZ3JhZmFuYS1hZG1pbicgZXhpc3RzIGluIGFwcGxpY2F0aW9uLXNlY3JldHMgbmFtZXNwYWNlIHdpdGgga2V5czpcbiAqICAgLSBhZG1pbi11c2VyXG4gKiAgIC0gYWRtaW4tcGFzc3dvcmRcbiAqXG4gKiBTZWN1cml0eSBNb2RlbDpcbiAqIC0gU291cmNlIHNlY3JldCBzdG9yZWQgaW4gY2VudHJhbGl6ZWQgYXBwbGljYXRpb24tc2VjcmV0cyBuYW1lc3BhY2VcbiAqIC0gRXh0ZXJuYWxTZWNyZXQgQ1IgKHRoaXMpIGNvbW1pdHRlZCB0byBnaXQgKEdpdE9wcy1zYWZlLCBubyBwYXNzd29yZCBpbiBnaXQpXG4gKiAtIEVTTyBhdXRvbWF0aWNhbGx5IHJlcGxpY2F0ZXMgc2VjcmV0IHRvIG1vbml0b3JpbmcgbmFtZXNwYWNlXG4gKiAtIFBhc3N3b3JkIHBlcnNpc3RzIGFjcm9zcyBIZWxtIHVwZ3JhZGVzIChubyBhdXRvLXJlZ2VuZXJhdGlvbilcbiAqXG4gKiBCZW5lZml0czpcbiAqIC0gQ2VudHJhbGl6ZWQgc2VjcmV0IG1hbmFnZW1lbnQgKGFsbCBhcHAgc2VjcmV0cyBpbiBhcHBsaWNhdGlvbi1zZWNyZXRzIG5hbWVzcGFjZSlcbiAqIC0gR2l0T3BzLXNhZmUgKEV4dGVybmFsU2VjcmV0IGluIGdpdCwgYWN0dWFsIHBhc3N3b3JkIG5vdCBpbiBnaXQpXG4gKiAtIFBhc3N3b3JkIHJvdGF0aW9uICh1cGRhdGUgc291cmNlIOKGkiBhdXRvLXN5bmMgdG8gbW9uaXRvcmluZylcbiAqIC0gRm9sbG93cyBHaXRMYWIgQkRBIHNlY3VyaXR5IG1vZGVsIChzZWUgZG9jdW1lbnRhdGlvbi9zb3VyY2VzL2RlcGxveW1lbnRzL2dpdGxhYmJkYS9leHBsYW5hdGlvbi9zZWN1cml0eS1tb2RlbC5tZClcbiAqXG4gKiBVc2FnZSBpbiBwcm9tZXRoZXVzLXN0YWNrLnRzOlxuICogYGBgeWFtbFxuICogZ3JhZmFuYTpcbiAqICAgYWRtaW46XG4gKiAgICAgZXhpc3RpbmdTZWNyZXQ6IFwiZ3JhZmFuYS1hZG1pbi1jcmVkZW50aWFsc1wiXG4gKiAgICAgdXNlcktleTogXCJhZG1pbi11c2VyXCJcbiAqICAgICBwYXNzd29yZEtleTogXCJhZG1pbi1wYXNzd29yZFwiXG4gKiBgYGBcbiAqXG4gKiBNYW51YWwgU2V0dXAgUmVxdWlyZWQ6XG4gKiAxLiBDcmVhdGUgc291cmNlIHNlY3JldCBpbiBhcHBsaWNhdGlvbi1zZWNyZXRzIG5hbWVzcGFjZTpcbiAqICAgIGBgYGJhc2hcbiAqICAgIGt1YmVjdGwgY3JlYXRlIHNlY3JldCBnZW5lcmljIGdyYWZhbmEtYWRtaW4gLW4gYXBwbGljYXRpb24tc2VjcmV0cyBcXFxuICogICAgICAtLWZyb20tbGl0ZXJhbD1hZG1pbi11c2VyPWFkbWluIFxcXG4gKiAgICAgIC0tZnJvbS1saXRlcmFsPWFkbWluLXBhc3N3b3JkPSdZb3VyU2VjdXJlUGFzc3dvcmQxMjMhJ1xuICogICAgYGBgXG4gKlxuICogMi4gQ3JlYXRlIENsdXN0ZXJTZWNyZXRTdG9yZSAoaWYgbm90IGV4aXN0cyk6XG4gKiAgICBTZWU6IGRvY3VtZW50YXRpb24vc291cmNlcy9kZXBsb3ltZW50cy9tb25pdG9yaW5nL2hvdy10by9zZXR1cC1ncmFmYW5hLXBhc3N3b3JkLm1kXG4gKlxuICogMy4gRGVwbG95IG1vbml0b3Jpbmcgc3RhY2sgKHRoaXMgRXh0ZXJuYWxTZWNyZXQgd2lsbCBzeW5jIHRoZSBzZWNyZXQpXG4gKi9cbmV4cG9ydCBjbGFzcyBHcmFmYW5hUGFzc3dvcmRTZWNyZXQgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICBwdWJsaWMgcmVhZG9ubHkgZXh0ZXJuYWxTZWNyZXQ6IEV4dGVybmFsU2VjcmV0O1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBHcmFmYW5hUGFzc3dvcmRTZWNyZXRQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCB7IG5hbWVzcGFjZSB9ID0gcHJvcHM7XG5cbiAgICAvLyBDcmVhdGUgRXh0ZXJuYWxTZWNyZXQgdG8gcmVwbGljYXRlIEdyYWZhbmEgY3JlZGVudGlhbHMgZnJvbSBhcHBsaWNhdGlvbi1zZWNyZXRzXG4gICAgLy8gV2F2ZSAxOiBDcmVkZW50aWFscyBtdXN0IGV4aXN0IGJlZm9yZSBHcmFmYW5hIHN0YXJ0cyAoV2F2ZSAyKVxuICAgIHRoaXMuZXh0ZXJuYWxTZWNyZXQgPSBuZXcgRXh0ZXJuYWxTZWNyZXQodGhpcywgJ2V4dGVybmFsLXNlY3JldCcsIHtcbiAgICAgIG1ldGFkYXRhOiB7XG4gICAgICAgIG5hbWU6ICdncmFmYW5hLWFkbWluLWNyZWRlbnRpYWxzLWVzJyxcbiAgICAgICAgbmFtZXNwYWNlLFxuICAgICAgICBsYWJlbHM6IHtcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbmFtZSc6ICdncmFmYW5hJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vY29tcG9uZW50JzogJ2NyZWRlbnRpYWxzJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbWFuYWdlZC1ieSc6ICdleHRlcm5hbC1zZWNyZXRzJyxcbiAgICAgICAgfSxcbiAgICAgICAgYW5ub3RhdGlvbnM6IHtcbiAgICAgICAgICAnYXJnb2NkLmFyZ29wcm9qLmlvL3N5bmMtd2F2ZSc6ICcxJyxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICBzcGVjOiB7XG4gICAgICAgIC8vIFN5bmMgZXZlcnkgaG91ciB0byBkZXRlY3QgcGFzc3dvcmQgcm90YXRpb25cbiAgICAgICAgcmVmcmVzaEludGVydmFsOiAnMWgnLFxuXG4gICAgICAgIC8vIFJlZmVyZW5jZSB0byBDbHVzdGVyU2VjcmV0U3RvcmUgKGFwcGxpY2F0aW9uLXNlY3JldHMg4oaSIG1vbml0b3JpbmcpXG4gICAgICAgIHNlY3JldFN0b3JlUmVmOiB7XG4gICAgICAgICAgbmFtZTogJ21vbml0b3JpbmctYXBwLXNlY3JldHMtc3RvcmUnLFxuICAgICAgICAgIGtpbmQ6IEV4dGVybmFsU2VjcmV0U3BlY1NlY3JldFN0b3JlUmVmS2luZC5DTFVTVEVSX1NFQ1JFVF9TVE9SRSxcbiAgICAgICAgfSxcblxuICAgICAgICAvLyBUYXJnZXQgc2VjcmV0IG5hbWUgdGhhdCBHcmFmYW5hIHdpbGwgdXNlXG4gICAgICAgIHRhcmdldDoge1xuICAgICAgICAgIG5hbWU6ICdncmFmYW5hLWFkbWluLWNyZWRlbnRpYWxzJyxcbiAgICAgICAgICBjcmVhdGlvblBvbGljeTogRXh0ZXJuYWxTZWNyZXRTcGVjVGFyZ2V0Q3JlYXRpb25Qb2xpY3kuT1dORVIsXG4gICAgICAgIH0sXG5cbiAgICAgICAgLy8gQ29weSBhbGwgZGF0YSBmcm9tIHNvdXJjZSBzZWNyZXQgKGFkbWluLXVzZXIsIGFkbWluLXBhc3N3b3JkKVxuICAgICAgICBkYXRhRnJvbTogW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIGV4dHJhY3Q6IHtcbiAgICAgICAgICAgICAga2V5OiAnZ3JhZmFuYS1hZG1pbicsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgIF0sXG4gICAgICB9LFxuICAgIH0pO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,24 @@
1
+ import { Construct } from 'constructs';
2
+ import { MonitoringConfig } from '../types';
3
+ export interface LokiS3BucketProps {
4
+ config: MonitoringConfig;
5
+ }
6
+ /**
7
+ * Creates Crossplane Bucket resources for Loki log storage.
8
+ *
9
+ * This construct generates:
10
+ * - S3 Bucket (via Crossplane Upbound provider-aws-s3)
11
+ * - BucketVersioning (for recovery of accidentally deleted logs)
12
+ * - BucketLifecycleConfiguration (90-day retention)
13
+ *
14
+ * Prerequisites:
15
+ * - Crossplane operator installed
16
+ * - Upbound provider-aws-s3 installed
17
+ * - ProviderConfig 'hetzner-s3' configured (points to Hetzner S3)
18
+ * - Hetzner S3 credentials secret exists in crossplane-system
19
+ *
20
+ * Sync Wave: 1 (external dependencies - must exist before pods access S3)
21
+ */
22
+ export declare class LokiS3BucketConstruct extends Construct {
23
+ constructor(scope: Construct, id: string, props: LokiS3BucketProps);
24
+ }
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LokiS3BucketConstruct = void 0;
4
+ const cdk8s_1 = require("cdk8s");
5
+ const constructs_1 = require("constructs");
6
+ const s3_aws_upbound_io_1 = require("../imports/s3.aws.upbound.io");
7
+ /**
8
+ * Creates Crossplane Bucket resources for Loki log storage.
9
+ *
10
+ * This construct generates:
11
+ * - S3 Bucket (via Crossplane Upbound provider-aws-s3)
12
+ * - BucketVersioning (for recovery of accidentally deleted logs)
13
+ * - BucketLifecycleConfiguration (90-day retention)
14
+ *
15
+ * Prerequisites:
16
+ * - Crossplane operator installed
17
+ * - Upbound provider-aws-s3 installed
18
+ * - ProviderConfig 'hetzner-s3' configured (points to Hetzner S3)
19
+ * - Hetzner S3 credentials secret exists in crossplane-system
20
+ *
21
+ * Sync Wave: 1 (external dependencies - must exist before pods access S3)
22
+ */
23
+ class LokiS3BucketConstruct extends constructs_1.Construct {
24
+ constructor(scope, id, props) {
25
+ super(scope, id);
26
+ const { config } = props;
27
+ const bucketName = config.s3.buckets.loki;
28
+ const region = config.s3.region;
29
+ // Create Bucket
30
+ // Wave 1: External Dependencies - S3 bucket provisioned by Crossplane
31
+ // Note: Using ApiObject because cdk8s import limitations prevent importing Bucket type
32
+ new cdk8s_1.ApiObject(this, 'bucket', {
33
+ apiVersion: 's3.aws.upbound.io/v1beta2',
34
+ kind: 'Bucket',
35
+ metadata: {
36
+ name: bucketName,
37
+ namespace: 'crossplane-system',
38
+ labels: {
39
+ 'app.kubernetes.io/name': 'loki',
40
+ 'app.kubernetes.io/component': 'storage',
41
+ 'app.kubernetes.io/managed-by': 'cdk8s',
42
+ 'app.kubernetes.io/part-of': 'monitoring',
43
+ },
44
+ annotations: {
45
+ 'argocd.argoproj.io/sync-wave': '1',
46
+ 'crossplane.io/external-name': bucketName,
47
+ 'description': 'Loki log storage (90-day retention)',
48
+ },
49
+ },
50
+ spec: {
51
+ deletionPolicy: 'Delete',
52
+ managementPolicies: [
53
+ 'Observe',
54
+ 'Create',
55
+ 'Delete',
56
+ // Skip Update to avoid tagging operations (not supported by Hetzner S3)
57
+ ],
58
+ forProvider: {
59
+ region: region, // Production region (endpoint determines actual location)
60
+ },
61
+ providerConfigRef: {
62
+ name: 'hetzner-s3',
63
+ },
64
+ },
65
+ });
66
+ // Create BucketVersioning
67
+ // Enable versioning for recovery of accidentally deleted logs
68
+ // Note: Using ApiObject because cdk8s import limitations prevent importing BucketVersioning type
69
+ new cdk8s_1.ApiObject(this, 'versioning', {
70
+ apiVersion: 's3.aws.upbound.io/v1beta2',
71
+ kind: 'BucketVersioning',
72
+ metadata: {
73
+ name: 'loki-logs-versioning',
74
+ namespace: 'crossplane-system',
75
+ labels: {
76
+ 'app.kubernetes.io/name': 'loki',
77
+ 'app.kubernetes.io/component': 'storage',
78
+ 'app.kubernetes.io/managed-by': 'cdk8s',
79
+ 'app.kubernetes.io/part-of': 'monitoring',
80
+ },
81
+ annotations: {
82
+ 'argocd.argoproj.io/sync-wave': '1',
83
+ },
84
+ },
85
+ spec: {
86
+ forProvider: {
87
+ region: region, // Production region (same as bucket)
88
+ bucketRef: {
89
+ name: bucketName,
90
+ },
91
+ versioningConfiguration: {
92
+ status: 'Enabled',
93
+ },
94
+ },
95
+ providerConfigRef: {
96
+ name: 'hetzner-s3',
97
+ },
98
+ },
99
+ });
100
+ // Create BucketLifecycleConfiguration
101
+ // Lifecycle policy: automatically delete logs older than 90 days
102
+ new s3_aws_upbound_io_1.BucketLifecycleConfiguration(this, 'lifecycle', {
103
+ metadata: {
104
+ name: 'loki-logs-lifecycle',
105
+ namespace: 'crossplane-system',
106
+ labels: {
107
+ 'app.kubernetes.io/name': 'loki',
108
+ 'app.kubernetes.io/component': 'storage',
109
+ 'app.kubernetes.io/managed-by': 'cdk8s',
110
+ 'app.kubernetes.io/part-of': 'monitoring',
111
+ },
112
+ annotations: {
113
+ 'argocd.argoproj.io/sync-wave': '1',
114
+ },
115
+ },
116
+ spec: {
117
+ managementPolicies: [
118
+ s3_aws_upbound_io_1.BucketLifecycleConfigurationSpecManagementPolicies.OBSERVE,
119
+ s3_aws_upbound_io_1.BucketLifecycleConfigurationSpecManagementPolicies.CREATE,
120
+ s3_aws_upbound_io_1.BucketLifecycleConfigurationSpecManagementPolicies.DELETE,
121
+ ],
122
+ forProvider: {
123
+ region: region, // Production region (same as bucket)
124
+ bucketRef: {
125
+ name: bucketName,
126
+ },
127
+ rule: [
128
+ {
129
+ id: 'delete-old-logs',
130
+ status: 'Enabled',
131
+ expiration: [
132
+ {
133
+ days: 90, // 90-day retention (matches 31d Loki retention + buffer)
134
+ },
135
+ ],
136
+ },
137
+ ],
138
+ },
139
+ providerConfigRef: {
140
+ name: 'hetzner-s3',
141
+ },
142
+ },
143
+ });
144
+ }
145
+ }
146
+ exports.LokiS3BucketConstruct = LokiS3BucketConstruct;
147
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9raS1zMy1idWNrZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29uc3RydWN0cy9sb2tpLXMzLWJ1Y2tldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxpQ0FBa0M7QUFDbEMsMkNBQXVDO0FBQ3ZDLG9FQUFnSTtBQU9oSTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFDSCxNQUFhLHFCQUFzQixTQUFRLHNCQUFTO0lBQ2xELFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBd0I7UUFDaEUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUVqQixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBSyxDQUFDO1FBQ3pCLE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztRQUMxQyxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQztRQUVoQyxnQkFBZ0I7UUFDaEIsc0VBQXNFO1FBQ3RFLHVGQUF1RjtRQUN2RixJQUFJLGlCQUFTLENBQUMsSUFBSSxFQUFFLFFBQVEsRUFBRTtZQUM1QixVQUFVLEVBQUUsMkJBQTJCO1lBQ3ZDLElBQUksRUFBRSxRQUFRO1lBQ2QsUUFBUSxFQUFFO2dCQUNSLElBQUksRUFBRSxVQUFVO2dCQUNoQixTQUFTLEVBQUUsbUJBQW1CO2dCQUM5QixNQUFNLEVBQUU7b0JBQ04sd0JBQXdCLEVBQUUsTUFBTTtvQkFDaEMsNkJBQTZCLEVBQUUsU0FBUztvQkFDeEMsOEJBQThCLEVBQUUsT0FBTztvQkFDdkMsMkJBQTJCLEVBQUUsWUFBWTtpQkFDMUM7Z0JBQ0QsV0FBVyxFQUFFO29CQUNYLDhCQUE4QixFQUFFLEdBQUc7b0JBQ25DLDZCQUE2QixFQUFFLFVBQVU7b0JBQ3pDLGFBQWEsRUFBRSxxQ0FBcUM7aUJBQ3JEO2FBQ0Y7WUFDRCxJQUFJLEVBQUU7Z0JBQ0osY0FBYyxFQUFFLFFBQVE7Z0JBQ3hCLGtCQUFrQixFQUFFO29CQUNsQixTQUFTO29CQUNULFFBQVE7b0JBQ1IsUUFBUTtvQkFDUix3RUFBd0U7aUJBQ3pFO2dCQUNELFdBQVcsRUFBRTtvQkFDWCxNQUFNLEVBQUUsTUFBTSxFQUFFLDBEQUEwRDtpQkFDM0U7Z0JBQ0QsaUJBQWlCLEVBQUU7b0JBQ2pCLElBQUksRUFBRSxZQUFZO2lCQUNuQjthQUNGO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsMEJBQTBCO1FBQzFCLDhEQUE4RDtRQUM5RCxpR0FBaUc7UUFDakcsSUFBSSxpQkFBUyxDQUFDLElBQUksRUFBRSxZQUFZLEVBQUU7WUFDaEMsVUFBVSxFQUFFLDJCQUEyQjtZQUN2QyxJQUFJLEVBQUUsa0JBQWtCO1lBQ3hCLFFBQVEsRUFBRTtnQkFDUixJQUFJLEVBQUUsc0JBQXNCO2dCQUM1QixTQUFTLEVBQUUsbUJBQW1CO2dCQUM5QixNQUFNLEVBQUU7b0JBQ04sd0JBQXdCLEVBQUUsTUFBTTtvQkFDaEMsNkJBQTZCLEVBQUUsU0FBUztvQkFDeEMsOEJBQThCLEVBQUUsT0FBTztvQkFDdkMsMkJBQTJCLEVBQUUsWUFBWTtpQkFDMUM7Z0JBQ0QsV0FBVyxFQUFFO29CQUNYLDhCQUE4QixFQUFFLEdBQUc7aUJBQ3BDO2FBQ0Y7WUFDRCxJQUFJLEVBQUU7Z0JBQ0osV0FBVyxFQUFFO29CQUNYLE1BQU0sRUFBRSxNQUFNLEVBQUUscUNBQXFDO29CQUNyRCxTQUFTLEVBQUU7d0JBQ1QsSUFBSSxFQUFFLFVBQVU7cUJBQ2pCO29CQUNELHVCQUF1QixFQUFFO3dCQUN2QixNQUFNLEVBQUUsU0FBUztxQkFDbEI7aUJBQ0Y7Z0JBQ0QsaUJBQWlCLEVBQUU7b0JBQ2pCLElBQUksRUFBRSxZQUFZO2lCQUNuQjthQUNGO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsc0NBQXNDO1FBQ3RDLGlFQUFpRTtRQUNqRSxJQUFJLGdEQUE0QixDQUFDLElBQUksRUFBRSxXQUFXLEVBQUU7WUFDbEQsUUFBUSxFQUFFO2dCQUNSLElBQUksRUFBRSxxQkFBcUI7Z0JBQzNCLFNBQVMsRUFBRSxtQkFBbUI7Z0JBQzlCLE1BQU0sRUFBRTtvQkFDTix3QkFBd0IsRUFBRSxNQUFNO29CQUNoQyw2QkFBNkIsRUFBRSxTQUFTO29CQUN4Qyw4QkFBOEIsRUFBRSxPQUFPO29CQUN2QywyQkFBMkIsRUFBRSxZQUFZO2lCQUMxQztnQkFDRCxXQUFXLEVBQUU7b0JBQ1gsOEJBQThCLEVBQUUsR0FBRztpQkFDcEM7YUFDRjtZQUNELElBQUksRUFBRTtnQkFDSixrQkFBa0IsRUFBRTtvQkFDbEIsc0VBQWtELENBQUMsT0FBTztvQkFDMUQsc0VBQWtELENBQUMsTUFBTTtvQkFDekQsc0VBQWtELENBQUMsTUFBTTtpQkFDMUQ7Z0JBQ0QsV0FBVyxFQUFFO29CQUNYLE1BQU0sRUFBRSxNQUFNLEVBQUUscUNBQXFDO29CQUNyRCxTQUFTLEVBQUU7d0JBQ1QsSUFBSSxFQUFFLFVBQVU7cUJBQ2pCO29CQUNELElBQUksRUFBRTt3QkFDSjs0QkFDRSxFQUFFLEVBQUUsaUJBQWlCOzRCQUNyQixNQUFNLEVBQUUsU0FBUzs0QkFDakIsVUFBVSxFQUFFO2dDQUNWO29DQUNFLElBQUksRUFBRSxFQUFFLEVBQUUseURBQXlEO2lDQUNwRTs2QkFDRjt5QkFDRjtxQkFDRjtpQkFDRjtnQkFDRCxpQkFBaUIsRUFBRTtvQkFDakIsSUFBSSxFQUFFLFlBQVk7aUJBQ25CO2FBQ0Y7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUE5SEQsc0RBOEhDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQXBpT2JqZWN0IH0gZnJvbSAnY2RrOHMnO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgeyBCdWNrZXRMaWZlY3ljbGVDb25maWd1cmF0aW9uLCBCdWNrZXRMaWZlY3ljbGVDb25maWd1cmF0aW9uU3BlY01hbmFnZW1lbnRQb2xpY2llcyB9IGZyb20gJy4uL2ltcG9ydHMvczMuYXdzLnVwYm91bmQuaW8nO1xuaW1wb3J0IHsgTW9uaXRvcmluZ0NvbmZpZyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGludGVyZmFjZSBMb2tpUzNCdWNrZXRQcm9wcyB7XG4gIGNvbmZpZzogTW9uaXRvcmluZ0NvbmZpZztcbn1cblxuLyoqXG4gKiBDcmVhdGVzIENyb3NzcGxhbmUgQnVja2V0IHJlc291cmNlcyBmb3IgTG9raSBsb2cgc3RvcmFnZS5cbiAqXG4gKiBUaGlzIGNvbnN0cnVjdCBnZW5lcmF0ZXM6XG4gKiAtIFMzIEJ1Y2tldCAodmlhIENyb3NzcGxhbmUgVXBib3VuZCBwcm92aWRlci1hd3MtczMpXG4gKiAtIEJ1Y2tldFZlcnNpb25pbmcgKGZvciByZWNvdmVyeSBvZiBhY2NpZGVudGFsbHkgZGVsZXRlZCBsb2dzKVxuICogLSBCdWNrZXRMaWZlY3ljbGVDb25maWd1cmF0aW9uICg5MC1kYXkgcmV0ZW50aW9uKVxuICpcbiAqIFByZXJlcXVpc2l0ZXM6XG4gKiAtIENyb3NzcGxhbmUgb3BlcmF0b3IgaW5zdGFsbGVkXG4gKiAtIFVwYm91bmQgcHJvdmlkZXItYXdzLXMzIGluc3RhbGxlZFxuICogLSBQcm92aWRlckNvbmZpZyAnaGV0em5lci1zMycgY29uZmlndXJlZCAocG9pbnRzIHRvIEhldHpuZXIgUzMpXG4gKiAtIEhldHpuZXIgUzMgY3JlZGVudGlhbHMgc2VjcmV0IGV4aXN0cyBpbiBjcm9zc3BsYW5lLXN5c3RlbVxuICpcbiAqIFN5bmMgV2F2ZTogMSAoZXh0ZXJuYWwgZGVwZW5kZW5jaWVzIC0gbXVzdCBleGlzdCBiZWZvcmUgcG9kcyBhY2Nlc3MgUzMpXG4gKi9cbmV4cG9ydCBjbGFzcyBMb2tpUzNCdWNrZXRDb25zdHJ1Y3QgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogTG9raVMzQnVja2V0UHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgY29uc3QgeyBjb25maWcgfSA9IHByb3BzO1xuICAgIGNvbnN0IGJ1Y2tldE5hbWUgPSBjb25maWcuczMuYnVja2V0cy5sb2tpO1xuICAgIGNvbnN0IHJlZ2lvbiA9IGNvbmZpZy5zMy5yZWdpb247XG5cbiAgICAvLyBDcmVhdGUgQnVja2V0XG4gICAgLy8gV2F2ZSAxOiBFeHRlcm5hbCBEZXBlbmRlbmNpZXMgLSBTMyBidWNrZXQgcHJvdmlzaW9uZWQgYnkgQ3Jvc3NwbGFuZVxuICAgIC8vIE5vdGU6IFVzaW5nIEFwaU9iamVjdCBiZWNhdXNlIGNkazhzIGltcG9ydCBsaW1pdGF0aW9ucyBwcmV2ZW50IGltcG9ydGluZyBCdWNrZXQgdHlwZVxuICAgIG5ldyBBcGlPYmplY3QodGhpcywgJ2J1Y2tldCcsIHtcbiAgICAgIGFwaVZlcnNpb246ICdzMy5hd3MudXBib3VuZC5pby92MWJldGEyJyxcbiAgICAgIGtpbmQ6ICdCdWNrZXQnLFxuICAgICAgbWV0YWRhdGE6IHtcbiAgICAgICAgbmFtZTogYnVja2V0TmFtZSxcbiAgICAgICAgbmFtZXNwYWNlOiAnY3Jvc3NwbGFuZS1zeXN0ZW0nLFxuICAgICAgICBsYWJlbHM6IHtcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbmFtZSc6ICdsb2tpJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vY29tcG9uZW50JzogJ3N0b3JhZ2UnLFxuICAgICAgICAgICdhcHAua3ViZXJuZXRlcy5pby9tYW5hZ2VkLWJ5JzogJ2NkazhzJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vcGFydC1vZic6ICdtb25pdG9yaW5nJyxcbiAgICAgICAgfSxcbiAgICAgICAgYW5ub3RhdGlvbnM6IHtcbiAgICAgICAgICAnYXJnb2NkLmFyZ29wcm9qLmlvL3N5bmMtd2F2ZSc6ICcxJyxcbiAgICAgICAgICAnY3Jvc3NwbGFuZS5pby9leHRlcm5hbC1uYW1lJzogYnVja2V0TmFtZSxcbiAgICAgICAgICAnZGVzY3JpcHRpb24nOiAnTG9raSBsb2cgc3RvcmFnZSAoOTAtZGF5IHJldGVudGlvbiknLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIHNwZWM6IHtcbiAgICAgICAgZGVsZXRpb25Qb2xpY3k6ICdEZWxldGUnLFxuICAgICAgICBtYW5hZ2VtZW50UG9saWNpZXM6IFtcbiAgICAgICAgICAnT2JzZXJ2ZScsXG4gICAgICAgICAgJ0NyZWF0ZScsXG4gICAgICAgICAgJ0RlbGV0ZScsXG4gICAgICAgICAgLy8gU2tpcCBVcGRhdGUgdG8gYXZvaWQgdGFnZ2luZyBvcGVyYXRpb25zIChub3Qgc3VwcG9ydGVkIGJ5IEhldHpuZXIgUzMpXG4gICAgICAgIF0sXG4gICAgICAgIGZvclByb3ZpZGVyOiB7XG4gICAgICAgICAgcmVnaW9uOiByZWdpb24sIC8vIFByb2R1Y3Rpb24gcmVnaW9uIChlbmRwb2ludCBkZXRlcm1pbmVzIGFjdHVhbCBsb2NhdGlvbilcbiAgICAgICAgfSxcbiAgICAgICAgcHJvdmlkZXJDb25maWdSZWY6IHtcbiAgICAgICAgICBuYW1lOiAnaGV0em5lci1zMycsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gQ3JlYXRlIEJ1Y2tldFZlcnNpb25pbmdcbiAgICAvLyBFbmFibGUgdmVyc2lvbmluZyBmb3IgcmVjb3Zlcnkgb2YgYWNjaWRlbnRhbGx5IGRlbGV0ZWQgbG9nc1xuICAgIC8vIE5vdGU6IFVzaW5nIEFwaU9iamVjdCBiZWNhdXNlIGNkazhzIGltcG9ydCBsaW1pdGF0aW9ucyBwcmV2ZW50IGltcG9ydGluZyBCdWNrZXRWZXJzaW9uaW5nIHR5cGVcbiAgICBuZXcgQXBpT2JqZWN0KHRoaXMsICd2ZXJzaW9uaW5nJywge1xuICAgICAgYXBpVmVyc2lvbjogJ3MzLmF3cy51cGJvdW5kLmlvL3YxYmV0YTInLFxuICAgICAga2luZDogJ0J1Y2tldFZlcnNpb25pbmcnLFxuICAgICAgbWV0YWRhdGE6IHtcbiAgICAgICAgbmFtZTogJ2xva2ktbG9ncy12ZXJzaW9uaW5nJyxcbiAgICAgICAgbmFtZXNwYWNlOiAnY3Jvc3NwbGFuZS1zeXN0ZW0nLFxuICAgICAgICBsYWJlbHM6IHtcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbmFtZSc6ICdsb2tpJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vY29tcG9uZW50JzogJ3N0b3JhZ2UnLFxuICAgICAgICAgICdhcHAua3ViZXJuZXRlcy5pby9tYW5hZ2VkLWJ5JzogJ2NkazhzJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vcGFydC1vZic6ICdtb25pdG9yaW5nJyxcbiAgICAgICAgfSxcbiAgICAgICAgYW5ub3RhdGlvbnM6IHtcbiAgICAgICAgICAnYXJnb2NkLmFyZ29wcm9qLmlvL3N5bmMtd2F2ZSc6ICcxJyxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICBzcGVjOiB7XG4gICAgICAgIGZvclByb3ZpZGVyOiB7XG4gICAgICAgICAgcmVnaW9uOiByZWdpb24sIC8vIFByb2R1Y3Rpb24gcmVnaW9uIChzYW1lIGFzIGJ1Y2tldClcbiAgICAgICAgICBidWNrZXRSZWY6IHtcbiAgICAgICAgICAgIG5hbWU6IGJ1Y2tldE5hbWUsXG4gICAgICAgICAgfSxcbiAgICAgICAgICB2ZXJzaW9uaW5nQ29uZmlndXJhdGlvbjoge1xuICAgICAgICAgICAgc3RhdHVzOiAnRW5hYmxlZCcsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgICAgcHJvdmlkZXJDb25maWdSZWY6IHtcbiAgICAgICAgICBuYW1lOiAnaGV0em5lci1zMycsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gQ3JlYXRlIEJ1Y2tldExpZmVjeWNsZUNvbmZpZ3VyYXRpb25cbiAgICAvLyBMaWZlY3ljbGUgcG9saWN5OiBhdXRvbWF0aWNhbGx5IGRlbGV0ZSBsb2dzIG9sZGVyIHRoYW4gOTAgZGF5c1xuICAgIG5ldyBCdWNrZXRMaWZlY3ljbGVDb25maWd1cmF0aW9uKHRoaXMsICdsaWZlY3ljbGUnLCB7XG4gICAgICBtZXRhZGF0YToge1xuICAgICAgICBuYW1lOiAnbG9raS1sb2dzLWxpZmVjeWNsZScsXG4gICAgICAgIG5hbWVzcGFjZTogJ2Nyb3NzcGxhbmUtc3lzdGVtJyxcbiAgICAgICAgbGFiZWxzOiB7XG4gICAgICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL25hbWUnOiAnbG9raScsXG4gICAgICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL2NvbXBvbmVudCc6ICdzdG9yYWdlJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbWFuYWdlZC1ieSc6ICdjZGs4cycsXG4gICAgICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL3BhcnQtb2YnOiAnbW9uaXRvcmluZycsXG4gICAgICAgIH0sXG4gICAgICAgIGFubm90YXRpb25zOiB7XG4gICAgICAgICAgJ2FyZ29jZC5hcmdvcHJvai5pby9zeW5jLXdhdmUnOiAnMScsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAgc3BlYzoge1xuICAgICAgICBtYW5hZ2VtZW50UG9saWNpZXM6IFtcbiAgICAgICAgICBCdWNrZXRMaWZlY3ljbGVDb25maWd1cmF0aW9uU3BlY01hbmFnZW1lbnRQb2xpY2llcy5PQlNFUlZFLFxuICAgICAgICAgIEJ1Y2tldExpZmVjeWNsZUNvbmZpZ3VyYXRpb25TcGVjTWFuYWdlbWVudFBvbGljaWVzLkNSRUFURSxcbiAgICAgICAgICBCdWNrZXRMaWZlY3ljbGVDb25maWd1cmF0aW9uU3BlY01hbmFnZW1lbnRQb2xpY2llcy5ERUxFVEUsXG4gICAgICAgIF0sXG4gICAgICAgIGZvclByb3ZpZGVyOiB7XG4gICAgICAgICAgcmVnaW9uOiByZWdpb24sIC8vIFByb2R1Y3Rpb24gcmVnaW9uIChzYW1lIGFzIGJ1Y2tldClcbiAgICAgICAgICBidWNrZXRSZWY6IHtcbiAgICAgICAgICAgIG5hbWU6IGJ1Y2tldE5hbWUsXG4gICAgICAgICAgfSxcbiAgICAgICAgICBydWxlOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGlkOiAnZGVsZXRlLW9sZC1sb2dzJyxcbiAgICAgICAgICAgICAgc3RhdHVzOiAnRW5hYmxlZCcsXG4gICAgICAgICAgICAgIGV4cGlyYXRpb246IFtcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBkYXlzOiA5MCwgLy8gOTAtZGF5IHJldGVudGlvbiAobWF0Y2hlcyAzMWQgTG9raSByZXRlbnRpb24gKyBidWZmZXIpXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgXSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgcHJvdmlkZXJDb25maWdSZWY6IHtcbiAgICAgICAgICBuYW1lOiAnaGV0em5lci1zMycsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0pO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,27 @@
1
+ import { Construct } from 'constructs';
2
+ import { MonitoringConfig } from '../types';
3
+ export interface LokiS3CredentialsProps {
4
+ namespace: string;
5
+ config: MonitoringConfig;
6
+ }
7
+ /**
8
+ * Creates ExternalSecret for Loki S3 credentials.
9
+ *
10
+ * This construct replicates Hetzner S3 credentials from crossplane-system
11
+ * namespace to monitoring namespace for Loki's object storage.
12
+ *
13
+ * Prerequisites:
14
+ * - External Secrets Operator installed
15
+ * - ClusterSecretStore 'hetzner-s3-cluster-store' exists (points to crossplane-system)
16
+ * - Secret 'hetzner-s3-creds-standard' exists in crossplane-system namespace
17
+ *
18
+ * Generated Secret:
19
+ * - Name: loki-s3-credentials
20
+ * - Keys: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
21
+ *
22
+ * Sync Wave: 1 (external dependencies - must exist before Loki starts)
23
+ */
24
+ export declare class LokiS3CredentialsConstruct extends Construct {
25
+ readonly secretName = "loki-s3-credentials";
26
+ constructor(scope: Construct, id: string, props: LokiS3CredentialsProps);
27
+ }
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LokiS3CredentialsConstruct = void 0;
4
+ const constructs_1 = require("constructs");
5
+ const external_secrets_io_1 = require("../imports/external-secrets.io");
6
+ /**
7
+ * Creates ExternalSecret for Loki S3 credentials.
8
+ *
9
+ * This construct replicates Hetzner S3 credentials from crossplane-system
10
+ * namespace to monitoring namespace for Loki's object storage.
11
+ *
12
+ * Prerequisites:
13
+ * - External Secrets Operator installed
14
+ * - ClusterSecretStore 'hetzner-s3-cluster-store' exists (points to crossplane-system)
15
+ * - Secret 'hetzner-s3-creds-standard' exists in crossplane-system namespace
16
+ *
17
+ * Generated Secret:
18
+ * - Name: loki-s3-credentials
19
+ * - Keys: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
20
+ *
21
+ * Sync Wave: 1 (external dependencies - must exist before Loki starts)
22
+ */
23
+ class LokiS3CredentialsConstruct extends constructs_1.Construct {
24
+ constructor(scope, id, props) {
25
+ super(scope, id);
26
+ this.secretName = 'loki-s3-credentials';
27
+ const { namespace } = props;
28
+ // Create ExternalSecret that replicates S3 credentials
29
+ // Wave 1: External Dependencies - credentials must exist before Loki components
30
+ new external_secrets_io_1.ExternalSecret(this, 'loki-s3-external-secret', {
31
+ metadata: {
32
+ name: 'loki-s3-credentials-es',
33
+ namespace: namespace,
34
+ labels: {
35
+ 'app.kubernetes.io/name': 'loki',
36
+ 'app.kubernetes.io/component': 's3-credentials',
37
+ 'app.kubernetes.io/managed-by': 'cdk8s',
38
+ 'app.kubernetes.io/part-of': 'monitoring',
39
+ },
40
+ annotations: {
41
+ 'argocd.argoproj.io/sync-wave': '1',
42
+ },
43
+ },
44
+ spec: {
45
+ // Refresh credentials every hour (automatic rotation support)
46
+ // Use full format to match Kubernetes normalization and avoid ArgoCD drift
47
+ refreshInterval: '1h0m0s',
48
+ // Use existing ClusterSecretStore for Hetzner S3 (infrastructure-level secrets)
49
+ secretStoreRef: {
50
+ name: 'hetzner-s3-cluster-store',
51
+ kind: external_secrets_io_1.ExternalSecretSpecSecretStoreRefKind.CLUSTER_SECRET_STORE,
52
+ },
53
+ // Target secret that Loki will consume via extraEnvFrom
54
+ target: {
55
+ name: this.secretName,
56
+ creationPolicy: external_secrets_io_1.ExternalSecretSpecTargetCreationPolicy.OWNER,
57
+ },
58
+ // Fetch S3 credentials from crossplane-system/hetzner-s3-creds-standard
59
+ dataFrom: [
60
+ {
61
+ extract: {
62
+ key: 'hetzner-s3-creds-standard',
63
+ },
64
+ },
65
+ ],
66
+ },
67
+ });
68
+ }
69
+ }
70
+ exports.LokiS3CredentialsConstruct = LokiS3CredentialsConstruct;
71
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9raS1zMy1jcmVkZW50aWFscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25zdHJ1Y3RzL2xva2ktczMtY3JlZGVudGlhbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkNBQXVDO0FBQ3ZDLHdFQUl3QztBQVF4Qzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQWEsMEJBQTJCLFNBQVEsc0JBQVM7SUFHdkQsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUE2QjtRQUNyRSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBSEgsZUFBVSxHQUFHLHFCQUFxQixDQUFDO1FBS2pELE1BQU0sRUFBRSxTQUFTLEVBQUUsR0FBRyxLQUFLLENBQUM7UUFFNUIsdURBQXVEO1FBQ3ZELGdGQUFnRjtRQUNoRixJQUFJLG9DQUFjLENBQUMsSUFBSSxFQUFFLHlCQUF5QixFQUFFO1lBQ2xELFFBQVEsRUFBRTtnQkFDUixJQUFJLEVBQUUsd0JBQXdCO2dCQUM5QixTQUFTLEVBQUUsU0FBUztnQkFDcEIsTUFBTSxFQUFFO29CQUNOLHdCQUF3QixFQUFFLE1BQU07b0JBQ2hDLDZCQUE2QixFQUFFLGdCQUFnQjtvQkFDL0MsOEJBQThCLEVBQUUsT0FBTztvQkFDdkMsMkJBQTJCLEVBQUUsWUFBWTtpQkFDMUM7Z0JBQ0QsV0FBVyxFQUFFO29CQUNYLDhCQUE4QixFQUFFLEdBQUc7aUJBQ3BDO2FBQ0Y7WUFDRCxJQUFJLEVBQUU7Z0JBQ0osOERBQThEO2dCQUM5RCwyRUFBMkU7Z0JBQzNFLGVBQWUsRUFBRSxRQUFRO2dCQUV6QixnRkFBZ0Y7Z0JBQ2hGLGNBQWMsRUFBRTtvQkFDZCxJQUFJLEVBQUUsMEJBQTBCO29CQUNoQyxJQUFJLEVBQUUsMERBQW9DLENBQUMsb0JBQW9CO2lCQUNoRTtnQkFFRCx3REFBd0Q7Z0JBQ3hELE1BQU0sRUFBRTtvQkFDTixJQUFJLEVBQUUsSUFBSSxDQUFDLFVBQVU7b0JBQ3JCLGNBQWMsRUFBRSw0REFBc0MsQ0FBQyxLQUFLO2lCQUM3RDtnQkFFRCx3RUFBd0U7Z0JBQ3hFLFFBQVEsRUFBRTtvQkFDUjt3QkFDRSxPQUFPLEVBQUU7NEJBQ1AsR0FBRyxFQUFFLDJCQUEyQjt5QkFDakM7cUJBQ0Y7aUJBQ0Y7YUFDRjtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQXBERCxnRUFvREMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7XG4gIEV4dGVybmFsU2VjcmV0LFxuICBFeHRlcm5hbFNlY3JldFNwZWNUYXJnZXRDcmVhdGlvblBvbGljeSxcbiAgRXh0ZXJuYWxTZWNyZXRTcGVjU2VjcmV0U3RvcmVSZWZLaW5kLFxufSBmcm9tICcuLi9pbXBvcnRzL2V4dGVybmFsLXNlY3JldHMuaW8nO1xuaW1wb3J0IHsgTW9uaXRvcmluZ0NvbmZpZyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGludGVyZmFjZSBMb2tpUzNDcmVkZW50aWFsc1Byb3BzIHtcbiAgbmFtZXNwYWNlOiBzdHJpbmc7XG4gIGNvbmZpZzogTW9uaXRvcmluZ0NvbmZpZztcbn1cblxuLyoqXG4gKiBDcmVhdGVzIEV4dGVybmFsU2VjcmV0IGZvciBMb2tpIFMzIGNyZWRlbnRpYWxzLlxuICpcbiAqIFRoaXMgY29uc3RydWN0IHJlcGxpY2F0ZXMgSGV0em5lciBTMyBjcmVkZW50aWFscyBmcm9tIGNyb3NzcGxhbmUtc3lzdGVtXG4gKiBuYW1lc3BhY2UgdG8gbW9uaXRvcmluZyBuYW1lc3BhY2UgZm9yIExva2kncyBvYmplY3Qgc3RvcmFnZS5cbiAqXG4gKiBQcmVyZXF1aXNpdGVzOlxuICogLSBFeHRlcm5hbCBTZWNyZXRzIE9wZXJhdG9yIGluc3RhbGxlZFxuICogLSBDbHVzdGVyU2VjcmV0U3RvcmUgJ2hldHpuZXItczMtY2x1c3Rlci1zdG9yZScgZXhpc3RzIChwb2ludHMgdG8gY3Jvc3NwbGFuZS1zeXN0ZW0pXG4gKiAtIFNlY3JldCAnaGV0em5lci1zMy1jcmVkcy1zdGFuZGFyZCcgZXhpc3RzIGluIGNyb3NzcGxhbmUtc3lzdGVtIG5hbWVzcGFjZVxuICpcbiAqIEdlbmVyYXRlZCBTZWNyZXQ6XG4gKiAtIE5hbWU6IGxva2ktczMtY3JlZGVudGlhbHNcbiAqIC0gS2V5czogQVdTX0FDQ0VTU19LRVlfSUQsIEFXU19TRUNSRVRfQUNDRVNTX0tFWVxuICpcbiAqIFN5bmMgV2F2ZTogMSAoZXh0ZXJuYWwgZGVwZW5kZW5jaWVzIC0gbXVzdCBleGlzdCBiZWZvcmUgTG9raSBzdGFydHMpXG4gKi9cbmV4cG9ydCBjbGFzcyBMb2tpUzNDcmVkZW50aWFsc0NvbnN0cnVjdCBleHRlbmRzIENvbnN0cnVjdCB7XG4gIHB1YmxpYyByZWFkb25seSBzZWNyZXROYW1lID0gJ2xva2ktczMtY3JlZGVudGlhbHMnO1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBMb2tpUzNDcmVkZW50aWFsc1Byb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcblxuICAgIGNvbnN0IHsgbmFtZXNwYWNlIH0gPSBwcm9wcztcblxuICAgIC8vIENyZWF0ZSBFeHRlcm5hbFNlY3JldCB0aGF0IHJlcGxpY2F0ZXMgUzMgY3JlZGVudGlhbHNcbiAgICAvLyBXYXZlIDE6IEV4dGVybmFsIERlcGVuZGVuY2llcyAtIGNyZWRlbnRpYWxzIG11c3QgZXhpc3QgYmVmb3JlIExva2kgY29tcG9uZW50c1xuICAgIG5ldyBFeHRlcm5hbFNlY3JldCh0aGlzLCAnbG9raS1zMy1leHRlcm5hbC1zZWNyZXQnLCB7XG4gICAgICBtZXRhZGF0YToge1xuICAgICAgICBuYW1lOiAnbG9raS1zMy1jcmVkZW50aWFscy1lcycsXG4gICAgICAgIG5hbWVzcGFjZTogbmFtZXNwYWNlLFxuICAgICAgICBsYWJlbHM6IHtcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbmFtZSc6ICdsb2tpJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vY29tcG9uZW50JzogJ3MzLWNyZWRlbnRpYWxzJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbWFuYWdlZC1ieSc6ICdjZGs4cycsXG4gICAgICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL3BhcnQtb2YnOiAnbW9uaXRvcmluZycsXG4gICAgICAgIH0sXG4gICAgICAgIGFubm90YXRpb25zOiB7XG4gICAgICAgICAgJ2FyZ29jZC5hcmdvcHJvai5pby9zeW5jLXdhdmUnOiAnMScsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAgc3BlYzoge1xuICAgICAgICAvLyBSZWZyZXNoIGNyZWRlbnRpYWxzIGV2ZXJ5IGhvdXIgKGF1dG9tYXRpYyByb3RhdGlvbiBzdXBwb3J0KVxuICAgICAgICAvLyBVc2UgZnVsbCBmb3JtYXQgdG8gbWF0Y2ggS3ViZXJuZXRlcyBub3JtYWxpemF0aW9uIGFuZCBhdm9pZCBBcmdvQ0QgZHJpZnRcbiAgICAgICAgcmVmcmVzaEludGVydmFsOiAnMWgwbTBzJyxcblxuICAgICAgICAvLyBVc2UgZXhpc3RpbmcgQ2x1c3RlclNlY3JldFN0b3JlIGZvciBIZXR6bmVyIFMzIChpbmZyYXN0cnVjdHVyZS1sZXZlbCBzZWNyZXRzKVxuICAgICAgICBzZWNyZXRTdG9yZVJlZjoge1xuICAgICAgICAgIG5hbWU6ICdoZXR6bmVyLXMzLWNsdXN0ZXItc3RvcmUnLFxuICAgICAgICAgIGtpbmQ6IEV4dGVybmFsU2VjcmV0U3BlY1NlY3JldFN0b3JlUmVmS2luZC5DTFVTVEVSX1NFQ1JFVF9TVE9SRSxcbiAgICAgICAgfSxcblxuICAgICAgICAvLyBUYXJnZXQgc2VjcmV0IHRoYXQgTG9raSB3aWxsIGNvbnN1bWUgdmlhIGV4dHJhRW52RnJvbVxuICAgICAgICB0YXJnZXQ6IHtcbiAgICAgICAgICBuYW1lOiB0aGlzLnNlY3JldE5hbWUsXG4gICAgICAgICAgY3JlYXRpb25Qb2xpY3k6IEV4dGVybmFsU2VjcmV0U3BlY1RhcmdldENyZWF0aW9uUG9saWN5Lk9XTkVSLFxuICAgICAgICB9LFxuXG4gICAgICAgIC8vIEZldGNoIFMzIGNyZWRlbnRpYWxzIGZyb20gY3Jvc3NwbGFuZS1zeXN0ZW0vaGV0em5lci1zMy1jcmVkcy1zdGFuZGFyZFxuICAgICAgICBkYXRhRnJvbTogW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIGV4dHJhY3Q6IHtcbiAgICAgICAgICAgICAga2V5OiAnaGV0em5lci1zMy1jcmVkcy1zdGFuZGFyZCcsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgIF0sXG4gICAgICB9LFxuICAgIH0pO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,30 @@
1
+ import { Construct } from 'constructs';
2
+ import { MonitoringConfig } from '../types';
3
+ export interface LokiProps {
4
+ namespace: string;
5
+ config: MonitoringConfig;
6
+ s3CredentialsSecretName: string;
7
+ }
8
+ /**
9
+ * Creates Loki HelmChart for log aggregation.
10
+ *
11
+ * This construct deploys Loki in SimpleScalable mode with:
12
+ * - Backend (2 replicas) - Index and compaction
13
+ * - Read (2 replicas) - Query processing
14
+ * - Write (2 replicas) - Log ingestion
15
+ * - Gateway (1 replica) - HTTP API endpoint
16
+ * - S3 storage backend (Hetzner Object Storage)
17
+ *
18
+ * Prerequisites:
19
+ * - Namespace exists
20
+ * - PriorityClass 'high-priority' exists
21
+ * - Longhorn storage class available
22
+ * - S3 bucket exists (via Crossplane)
23
+ * - S3 credentials secret exists (via LokiS3CredentialsConstruct)
24
+ *
25
+ * Sync Wave: 2 (core services - after S3 credentials secret)
26
+ */
27
+ export declare class LokiConstruct extends Construct {
28
+ constructor(scope: Construct, id: string, props: LokiProps);
29
+ private generateHelmValues;
30
+ }