@r8s/harbor 0.2.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.
@@ -0,0 +1,143 @@
1
+ import { type DatabaseProps } from '@r8s/recipes';
2
+ export interface HarborProps {
3
+ /** Release / chart name (defaults to 'harbor') */
4
+ name?: string;
5
+ /** Kubernetes namespace for the release + data (inherited from <Platform> unless set) */
6
+ namespace?: string;
7
+ /** Public registry hostname (required), e.g. 'registry.berget.ai' */
8
+ host: string;
9
+ /**
10
+ * Harbor chart version (defaults to the pinned '1.18.3'). Flux applies
11
+ * chart upgrades automatically on the repo interval — bump deliberately.
12
+ */
13
+ chartVersion?: string;
14
+ /** HelmRepository URL (defaults to https://helm.goharbor.io) */
15
+ repoUrl?: string;
16
+ /** Namespace the HelmRepository lives in (defaults to 'flux-system') */
17
+ repoNamespace?: string;
18
+ /**
19
+ * S3 (RustFS-compatible) registry blob storage — required. The Docker
20
+ * registry protocol cannot follow S3 307 redirects: `disableredirect`
21
+ * and path-style access (`VIRTUAL_HOSTED_STYLE=false`) are encoded
22
+ * unconditionally, and the credentials bundle carries both the
23
+ * `REGISTRY_STORAGE_S3_*` and `AWS_*` key aliases the chart/registry
24
+ * each read. Credentials are provisioned through the Platform secrets
25
+ * backend unless `credentialsSecret` references a pre-created Secret.
26
+ */
27
+ s3: {
28
+ bucket: string;
29
+ region: string;
30
+ endpoint: string;
31
+ /** Backend path (defaults to `<provider.path>/<name>`) */
32
+ path?: string;
33
+ /** Reference a pre-created credentials Secret instead of provisioning */
34
+ credentialsSecret?: string;
35
+ };
36
+ /**
37
+ * CNPG cluster name (defaults to 'harbor-db'). The bootstrap database
38
+ * is 'registry' owned by 'harbor' — chart `existingSecret` gets the
39
+ * CNPG-managed `<db>-app` secret (credentialsMode 'cnpg').
40
+ */
41
+ dbName?: string;
42
+ /** Number of CNPG instances (defaults to 2) */
43
+ dbInstances?: number;
44
+ /** CNPG data volume size (defaults to '40Gi' — registry metadata grows with blob count) */
45
+ dbStorage?: string;
46
+ /** CNPG storage class (defaults to cluster default) */
47
+ dbStorageClass?: string;
48
+ /**
49
+ * CNPG backup configuration. Credential provisioning differs from the
50
+ * Database recipe default: the store holds keys `accesskey`/`secretkey`
51
+ * which the bundled remap turns into the CNPG-required
52
+ * `access-key-id`/`secret-access-key` — path defaults to
53
+ * `<provider.path>/berget-internal-cnpg` (facit). Set
54
+ * `credentialsSecret` to reference an existing, correctly-keyed Secret
55
+ * instead.
56
+ */
57
+ backup?: Omit<DatabaseProps['backup'], 'credentialsSecret'> & {
58
+ credentialsSecret?: string;
59
+ /** Backend path for the remapped backup creds (defaults to `<provider.path>/berget-internal-cnpg`) */
60
+ credentialsPath?: string;
61
+ };
62
+ /** Reference a pre-created admin password Secret (key: HARBOR_ADMIN_PASSWORD) instead of provisioning */
63
+ adminPasswordSecretRef?: string;
64
+ /**
65
+ * Reference a pre-created HTTP secret-key bundle (key: secretKey,
66
+ * EXACTLY 16 chars) instead of provisioning. The chart rejects
67
+ * other lengths — keep the constraint when generating.
68
+ */
69
+ secretKeySecretRef?: string;
70
+ /**
71
+ * OIDC client credentials for Keycloak login. NOTE: Harbor has no
72
+ * declarative OIDC — provisioning this Secret is preparatory; apply the
73
+ * actual OIDC configuration via the Harbor UI/API after deploy.
74
+ */
75
+ oidc?: {
76
+ path?: string;
77
+ };
78
+ /** Reference a pre-created OIDC Secret (keys: oidc-client-id, oidc-client-secret) */
79
+ oidcSecretRef?: string;
80
+ /** Scanner (trivy) settings — enabled by default with facit resources */
81
+ trivy?: {
82
+ enabled?: boolean;
83
+ resources?: {
84
+ requests?: {
85
+ cpu?: string;
86
+ memory?: string;
87
+ };
88
+ limits?: {
89
+ cpu?: string;
90
+ memory?: string;
91
+ };
92
+ };
93
+ };
94
+ /** Metrics: /metrics on port 8001 for core/registry/jobservice/exporter. `serviceMonitor` stays OFF — the cluster has no Prometheus CRDs. */
95
+ metrics?: {
96
+ enabled?: boolean;
97
+ serviceMonitor?: boolean;
98
+ };
99
+ /** TLS secret for the chart ingress (defaults to `harbor-tls` via letsencrypt-prod) */
100
+ tls?: {
101
+ secretName: string;
102
+ clusterIssuer: string;
103
+ };
104
+ }
105
+ /**
106
+ * Harbor — OCI artifact registry, facit-aligned via the official chart.
107
+ *
108
+ * @title Harbor
109
+ * @category Security & Identity
110
+ *
111
+ * The chart owns all app workloads (core/portal/registry/jobservice/redis/
112
+ * trivy/nginx ingress). This package owns the platform contract around it:
113
+ * - Flux `HelmRepository` + `HelmRelease` (pinned chart, facit values)
114
+ * - external CNPG cluster (`credentialsMode: 'cnpg'`; `<db>-app` secret is
115
+ * the chart's `existingSecret`) + optional scheduled backups
116
+ * - S3 blob-storage credentials bundle with REGISTRY_STORAGE_S3_* + AWS_*
117
+ * aliases, literal endpoint + path-style keys (RustFS trap: the registry
118
+ * protocol cannot follow 307 redirects)
119
+ * - admin/secret-key/(optional) OIDC bundles via the Platform secrets
120
+ * backend
121
+ *
122
+ * Encoded traps (learned from incidents — do not "fix" these):
123
+ * - `database.external.port` is the QUOTED STRING "5432" (chart bug)
124
+ * - ingress `proxy-body-size: "0"` — unlimited, large image layers
125
+ * - `updateStrategy.type: Recreate` — RWO jobLog PVC deadlocks rolling updates
126
+ * - `metrics.serviceMonitor.enabled: false` — no Prometheus CRDs on-cluster
127
+ * - OIDC is a manual post-deploy step; no declarative alternative exists
128
+ *
129
+ * @example
130
+ * import { Platform } from '@r8s/recipes'
131
+ * import { Harbor } from '@r8s/harbor'
132
+ *
133
+ * export default (
134
+ * <Platform secrets={{ backend: 'openbao', mount: 'secret', path: 'rustfs' }}>
135
+ * <Harbor
136
+ * host="registry.example.com"
137
+ * s3={{ bucket: 'harbor-registry', region: 'berget-cloud', endpoint: 'https://s3.example.com' }}
138
+ * />
139
+ * </Platform>
140
+ * )
141
+ */
142
+ export declare function Harbor(props: HarborProps): import("@r8s/core").r8sElement;
143
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,cAAc,CAAA;AAErB,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;;OAQG;IACH,EAAE,EAAE;QACF,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,EAAE,MAAM,CAAA;QAChB,0DAA0D;QAC1D,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,yEAAyE;QACzE,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAC3B,CAAA;IACD;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,2FAA2F;IAC3F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,GAAG;QAC5D,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,sGAAsG;QACtG,eAAe,CAAC,EAAE,MAAM,CAAA;KACzB,CAAA;IACD,yGAAyG;IACzG,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACxB,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yEAAyE;IACzE,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE;gBAAE,GAAG,CAAC,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;YAC5C,MAAM,CAAC,EAAE;gBAAE,GAAG,CAAC,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAC3C,CAAA;KACF,CAAA;IACD,6IAA6I;IAC7I,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IACzD,uFAAuF;IACvF,GAAG,CAAC,EAAE;QACJ,UAAU,EAAE,MAAM,CAAA;QAClB,aAAa,EAAE,MAAM,CAAA;KACtB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,kCA0TxC"}
package/dist/index.js ADDED
@@ -0,0 +1,295 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Harbor = Harbor;
4
+ const core_1 = require("@r8s/core");
5
+ const defaults_1 = require("@r8s/core/defaults");
6
+ const recipes_1 = require("@r8s/recipes");
7
+ /**
8
+ * Harbor — OCI artifact registry, facit-aligned via the official chart.
9
+ *
10
+ * @title Harbor
11
+ * @category Security & Identity
12
+ *
13
+ * The chart owns all app workloads (core/portal/registry/jobservice/redis/
14
+ * trivy/nginx ingress). This package owns the platform contract around it:
15
+ * - Flux `HelmRepository` + `HelmRelease` (pinned chart, facit values)
16
+ * - external CNPG cluster (`credentialsMode: 'cnpg'`; `<db>-app` secret is
17
+ * the chart's `existingSecret`) + optional scheduled backups
18
+ * - S3 blob-storage credentials bundle with REGISTRY_STORAGE_S3_* + AWS_*
19
+ * aliases, literal endpoint + path-style keys (RustFS trap: the registry
20
+ * protocol cannot follow 307 redirects)
21
+ * - admin/secret-key/(optional) OIDC bundles via the Platform secrets
22
+ * backend
23
+ *
24
+ * Encoded traps (learned from incidents — do not "fix" these):
25
+ * - `database.external.port` is the QUOTED STRING "5432" (chart bug)
26
+ * - ingress `proxy-body-size: "0"` — unlimited, large image layers
27
+ * - `updateStrategy.type: Recreate` — RWO jobLog PVC deadlocks rolling updates
28
+ * - `metrics.serviceMonitor.enabled: false` — no Prometheus CRDs on-cluster
29
+ * - OIDC is a manual post-deploy step; no declarative alternative exists
30
+ *
31
+ * @example
32
+ * import { Platform } from '@r8s/recipes'
33
+ * import { Harbor } from '@r8s/harbor'
34
+ *
35
+ * export default (
36
+ * <Platform secrets={{ backend: 'openbao', mount: 'secret', path: 'rustfs' }}>
37
+ * <Harbor
38
+ * host="registry.example.com"
39
+ * s3={{ bucket: 'harbor-registry', region: 'berget-cloud', endpoint: 'https://s3.example.com' }}
40
+ * />
41
+ * </Platform>
42
+ * )
43
+ */
44
+ function Harbor(props) {
45
+ const { name = 'harbor', namespace: namespaceProp, host, chartVersion = '1.18.3', repoUrl = 'https://helm.goharbor.io', repoNamespace = 'flux-system', s3, dbName = 'harbor-db', dbInstances = 2, dbStorage = '40Gi', dbStorageClass, backup, adminPasswordSecretRef, secretKeySecretRef, oidc, oidcSecretRef, trivy, metrics, tls = { secretName: 'harbor-tls', clusterIssuer: 'letsencrypt-prod' }, } = props;
46
+ const namespace = (0, defaults_1.useNamespace)(namespaceProp);
47
+ const secretProvider = (0, core_1.useContext)(defaults_1.SecretContext);
48
+ const resources_ = [];
49
+ // --- S3 credentials bundle (chart + registry read DIFFERENT key sets) ------
50
+ const s3CredentialsName = s3.credentialsSecret ?? `${name}-s3-credentials`;
51
+ if (!s3.credentialsSecret) {
52
+ if (!(0, recipes_1.canProvisionSecrets)(secretProvider)) {
53
+ throw (0, recipes_1.secretsRequiredError)('Harbor', name, 'S3 credentials for registry blob storage (keys accesskey/secretkey in the store)', {
54
+ propName: 's3.credentialsSecret',
55
+ exampleValue: `${name}-s3-credentials`,
56
+ keys: ['REGISTRY_STORAGE_S3_ACCESSKEY', 'REGISTRY_STORAGE_S3_SECRETKEY'],
57
+ });
58
+ }
59
+ resources_.push((0, core_1.jsx)(recipes_1.StaticSecret, {
60
+ name: `${name}-rustfs-s3-credentials`,
61
+ namespace,
62
+ path: s3.path ?? `${secretProvider.path ?? 'rustfs'}/${name}`,
63
+ secretName: s3CredentialsName,
64
+ // Chart needs REGISTRY_STORAGE_S3_*; registry tooling needs AWS_* —
65
+ // both map from the same store keys; endpoint + path-style are
66
+ // LITERAL templates (RustFS: no 307 redirects, no vhost buckets)
67
+ keys: {
68
+ REGISTRY_STORAGE_S3_ACCESSKEY: 'accesskey',
69
+ REGISTRY_STORAGE_S3_SECRETKEY: 'secretkey',
70
+ AWS_ACCESS_KEY_ID: 'accesskey',
71
+ AWS_SECRET_ACCESS_KEY: 'secretkey',
72
+ },
73
+ templates: {
74
+ AWS_ENDPOINTS: s3.endpoint,
75
+ VIRTUAL_HOSTED_STYLE: 'false',
76
+ },
77
+ refreshAfter: '3600s',
78
+ }));
79
+ }
80
+ // --- Admin + http secret key -------------------------------------------------
81
+ const adminSecretName = adminPasswordSecretRef ?? `${name}-admin-secret`;
82
+ if (!adminPasswordSecretRef) {
83
+ if (!(0, recipes_1.canProvisionSecrets)(secretProvider)) {
84
+ throw (0, recipes_1.secretsRequiredError)('Harbor', name, 'an admin password (key HARBOR_ADMIN_PASSWORD)', {
85
+ propName: 'adminPasswordSecretRef',
86
+ exampleValue: `${name}-admin-secret`,
87
+ keys: ['HARBOR_ADMIN_PASSWORD'],
88
+ });
89
+ }
90
+ resources_.push((0, core_1.jsx)(recipes_1.StaticSecret, {
91
+ name: `${name}-admin-secret`,
92
+ namespace,
93
+ path: `${secretProvider.path ?? name}/${name}/admin`,
94
+ secretName: adminSecretName,
95
+ keys: ['HARBOR_ADMIN_PASSWORD'],
96
+ }));
97
+ }
98
+ const secretKeySecretName = secretKeySecretRef ?? `${name}-secret`;
99
+ if (!secretKeySecretRef) {
100
+ if (!(0, recipes_1.canProvisionSecrets)(secretProvider)) {
101
+ throw (0, recipes_1.secretsRequiredError)('Harbor', name, 'an HTTP secret key (key secretKey — EXACTLY 16 chars, the chart rejects other lengths)', {
102
+ propName: 'secretKeySecretRef',
103
+ exampleValue: `${name}-secret`,
104
+ keys: ['secretKey'],
105
+ });
106
+ }
107
+ resources_.push((0, core_1.jsx)(recipes_1.StaticSecret, {
108
+ name: `${name}-secret`,
109
+ namespace,
110
+ path: `${secretProvider.path ?? name}/${name}/secret`,
111
+ secretName: secretKeySecretName,
112
+ keys: ['secretKey'],
113
+ }));
114
+ }
115
+ // --- OIDC bundle (manual post-deploy config on the Harbor side) -------------
116
+ if (oidc && !oidcSecretRef) {
117
+ if (!(0, recipes_1.canProvisionSecrets)(secretProvider)) {
118
+ throw (0, recipes_1.secretsRequiredError)('Harbor', name, 'OIDC client credentials because oidc is requested (keys oidc-client-id/oidc-client-secret)', {
119
+ propName: 'oidcSecretRef',
120
+ exampleValue: `${name}-keycloak-oidc`,
121
+ keys: ['oidc-client-id', 'oidc-client-secret'],
122
+ });
123
+ }
124
+ resources_.push((0, core_1.jsx)(recipes_1.StaticSecret, {
125
+ name: `${name}-keycloak-oidc`,
126
+ namespace,
127
+ path: oidc.path ?? `${secretProvider.path ?? name}/${name}/keycloak-oidc`,
128
+ secretName: `${name}-keycloak-oidc`,
129
+ keys: ['oidc-client-id', 'oidc-client-secret'],
130
+ }));
131
+ }
132
+ // --- CNPG cluster (CNPG-managed credentials: the chart's existingSecret) -----
133
+ resources_.push((0, core_1.jsx)(recipes_1.Database, {
134
+ name: dbName,
135
+ namespace,
136
+ database: 'registry',
137
+ owner: 'harbor',
138
+ instances: dbInstances,
139
+ storage: dbStorage,
140
+ ...(dbStorageClass ? { storageClass: dbStorageClass } : {}),
141
+ parameters: {
142
+ shared_buffers: '256MB',
143
+ max_connections: '200',
144
+ work_mem: '8MB',
145
+ maintenance_work_mem: '128MB',
146
+ effective_cache_size: '768MB',
147
+ },
148
+ credentialsMode: 'cnpg',
149
+ ...(backup
150
+ ? {
151
+ backup: {
152
+ ...backup,
153
+ // Explicit reference wins; otherwise the package provisions
154
+ // the remapped bundle below
155
+ credentialsSecret: backup.credentialsSecret ?? `${name}-cnpg-backup`,
156
+ },
157
+ }
158
+ : {}),
159
+ }));
160
+ if (backup && !backup.credentialsSecret) {
161
+ if (!(0, recipes_1.canProvisionSecrets)(secretProvider)) {
162
+ throw (0, recipes_1.secretsRequiredError)('Harbor', name, 'backup credentials (store keys accesskey/secretkey → CNPG access-key-id/secret-access-key)', {
163
+ propName: 'backup.credentialsSecret',
164
+ exampleValue: `${name}-cnpg-backup`,
165
+ keys: ['access-key-id', 'secret-access-key'],
166
+ });
167
+ }
168
+ resources_.push((0, core_1.jsx)(recipes_1.StaticSecret, {
169
+ name: `${name}-cnpg-backup`,
170
+ namespace,
171
+ path: backup.credentialsPath ?? `${secretProvider.path}/berget-internal-cnpg`,
172
+ secretName: `${name}-cnpg-backup`,
173
+ // The store uses camelCase keys; CNPG requires kebab-case
174
+ keys: { 'access-key-id': 'accesskey', 'secret-access-key': 'secretkey' },
175
+ refreshAfter: '3600s',
176
+ // No restart targets — barman reads credentials per backup run
177
+ }));
178
+ }
179
+ // --- Flux: HelmRepository + HelmRelease (facit values, traps encoded) --------
180
+ resources_.push((0, core_1.jsx)('HelmRepository', {
181
+ apiVersion: 'source.toolkit.fluxcd.io/v1',
182
+ kind: 'HelmRepository',
183
+ metadata: { name, namespace: repoNamespace },
184
+ spec: { interval: '24h', url: repoUrl },
185
+ }), (0, core_1.jsx)('HelmRelease', {
186
+ apiVersion: 'helm.toolkit.fluxcd.io/v2',
187
+ kind: 'HelmRelease',
188
+ metadata: { name, namespace },
189
+ spec: {
190
+ interval: '30m',
191
+ chart: {
192
+ spec: {
193
+ chart: 'harbor',
194
+ version: chartVersion,
195
+ sourceRef: { kind: 'HelmRepository', name, namespace: repoNamespace },
196
+ interval: '12h',
197
+ },
198
+ },
199
+ values: {
200
+ externalURL: `https://${host}`,
201
+ expose: {
202
+ type: 'ingress',
203
+ tls: { enabled: true, certSource: 'secret', secret: { secretName: tls.secretName } },
204
+ ingress: {
205
+ hosts: { core: host },
206
+ className: 'nginx',
207
+ annotations: {
208
+ 'cert-manager.io/cluster-issuer': tls.clusterIssuer,
209
+ 'external-dns.alpha.kubernetes.io/hostname': host,
210
+ 'nginx.ingress.kubernetes.io/ssl-redirect': 'true',
211
+ // TRAP: "0" = unlimited — large image layers; any size
212
+ // limit breaks `docker push` with 413
213
+ 'nginx.ingress.kubernetes.io/proxy-body-size': '0',
214
+ },
215
+ },
216
+ },
217
+ existingSecretAdminPassword: adminSecretName,
218
+ existingSecretAdminPasswordKey: 'HARBOR_ADMIN_PASSWORD',
219
+ existingSecretSecretKey: secretKeySecretName,
220
+ persistence: {
221
+ enabled: true,
222
+ resourcePolicy: 'keep',
223
+ imageChartStorage: {
224
+ type: 's3',
225
+ // TRAP: the registry protocol cannot follow S3 307 redirects —
226
+ // stream blobs server-side (RustFS requirement)
227
+ disableredirect: true,
228
+ s3: {
229
+ region: s3.region,
230
+ bucket: s3.bucket,
231
+ regionendpoint: s3.endpoint,
232
+ secure: true,
233
+ v4auth: true,
234
+ existingSecret: s3CredentialsName,
235
+ },
236
+ },
237
+ // PVCs only for jobLog/redis/trivy — registry blobs live in S3,
238
+ // database lives in CNPG (by design)
239
+ persistentVolumeClaim: {
240
+ jobservice: {
241
+ jobLog: {
242
+ size: '5Gi',
243
+ ...(dbStorageClass ? { storageClass: dbStorageClass } : {}),
244
+ },
245
+ },
246
+ redis: { size: '2Gi', ...(dbStorageClass ? { storageClass: dbStorageClass } : {}) },
247
+ trivy: { size: '10Gi', ...(dbStorageClass ? { storageClass: dbStorageClass } : {}) },
248
+ },
249
+ },
250
+ database: {
251
+ type: 'external',
252
+ external: {
253
+ host: `${dbName}-rw`,
254
+ // TRAP: must be a QUOTED string — numeric breaks the chart
255
+ // template (renders unquoted)
256
+ port: String(5432),
257
+ username: 'harbor',
258
+ password: '',
259
+ coreDatabase: 'registry',
260
+ existingSecret: `${dbName}-app`,
261
+ sslmode: 'disable',
262
+ },
263
+ },
264
+ redis: { type: 'internal' },
265
+ core: { replicas: 1 },
266
+ portal: { replicas: 1 },
267
+ registry: { replicas: 1 },
268
+ jobservice: { replicas: 1 },
269
+ // TRAP: RWO jobLog PVC deadlocks rolling updates on multi-node
270
+ updateStrategy: { type: 'Recreate' },
271
+ trivy: {
272
+ enabled: trivy?.enabled !== false,
273
+ replicas: 1,
274
+ resources: trivy?.resources ?? {
275
+ requests: { cpu: '200m', memory: '512Mi' },
276
+ limits: { cpu: '1', memory: '1Gi' },
277
+ },
278
+ },
279
+ metrics: {
280
+ enabled: metrics?.enabled !== false,
281
+ core: { path: '/metrics', port: 8001 },
282
+ registry: { path: '/metrics', port: 8001 },
283
+ jobservice: { path: '/metrics', port: 8001 },
284
+ exporter: { path: '/metrics', port: 8001 },
285
+ // TRAP: Prometheus CRDs (ServiceMonitor) are absent on-cluster
286
+ serviceMonitor: { enabled: metrics?.serviceMonitor === true },
287
+ },
288
+ ipFamily: 'ipv4',
289
+ logLevel: 'info',
290
+ },
291
+ },
292
+ }));
293
+ return (0, core_1.jsx)(core_1.Fragment, { children: resources_ });
294
+ }
295
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@r8s/harbor",
3
+ "version": "0.2.1",
4
+ "description": "Harbor container registry — chart release via Flux, CNPG persistence, S3 blob storage for r8s",
5
+ "license": "MIT",
6
+ "author": "Berget AI AB",
7
+ "homepage": "https://r8s.berget.ai",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/berget-ai/r8s.git",
11
+ "directory": "packages/harbor"
12
+ },
13
+ "bugs": "https://github.com/berget-ai/r8s/issues",
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "keywords": [
21
+ "harbor"
22
+ ],
23
+ "r8s": {
24
+ "category": "Security & Identity"
25
+ },
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "scripts": {
29
+ "build": "tsc --build",
30
+ "test": "vitest run",
31
+ "clean": "rm -rf dist tsconfig.tsbuildinfo"
32
+ },
33
+ "dependencies": {
34
+ "@r8s/core": "^0.2.0",
35
+ "@r8s/crds": "^0.2.0",
36
+ "@r8s/k8s-types": "^0.2.0",
37
+ "@r8s/recipes": "^0.2.0"
38
+ },
39
+ "devDependencies": {
40
+ "typescript": "^5.3.0",
41
+ "vitest": "^1.0.0"
42
+ }
43
+ }