@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,477 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PrometheusStackConstruct = void 0;
4
+ const cdk8s_1 = require("cdk8s");
5
+ const constructs_1 = require("constructs");
6
+ /**
7
+ * Creates kube-prometheus-stack HelmChart with Prometheus, Grafana, and Alertmanager.
8
+ *
9
+ * This is the core monitoring stack that includes:
10
+ * - Prometheus (metrics collection, 2 replicas, 3d retention)
11
+ * - Grafana (visualization, ingress with Let's Encrypt)
12
+ * - Alertmanager (alert routing, SMTP notifications)
13
+ * - Prometheus Operator (manages Prometheus CRDs)
14
+ * - Node Exporter (system metrics DaemonSet)
15
+ * - kube-state-metrics (Kubernetes resource metrics)
16
+ *
17
+ * Prerequisites:
18
+ * - Namespace exists
19
+ * - PriorityClass 'high-priority' exists
20
+ * - Longhorn storage class available
21
+ * - cert-manager ClusterIssuer configured
22
+ * - Thanos S3 secret exists (thanos-objstore-config)
23
+ *
24
+ * Sync Wave: 2 (core services - after S3 buckets and secrets)
25
+ */
26
+ class PrometheusStackConstruct extends constructs_1.Construct {
27
+ constructor(scope, id, props) {
28
+ super(scope, id);
29
+ const { namespace, config } = props;
30
+ // Generate Helm values YAML
31
+ const helmValues = this.generateHelmValues(config);
32
+ // Create HelmChart resource (K3S Helm controller)
33
+ // Wave 2: Core Services - monitoring stack
34
+ new cdk8s_1.ApiObject(this, 'kube-prometheus-stack', {
35
+ apiVersion: 'helm.cattle.io/v1',
36
+ kind: 'HelmChart',
37
+ metadata: {
38
+ name: 'kube-prometheus-stack',
39
+ namespace: namespace,
40
+ labels: {
41
+ 'app.kubernetes.io/name': 'kube-prometheus-stack',
42
+ 'app.kubernetes.io/component': 'monitoring',
43
+ 'app.kubernetes.io/managed-by': 'cdk8s',
44
+ 'app.kubernetes.io/part-of': 'monitoring',
45
+ },
46
+ annotations: {
47
+ 'argocd.argoproj.io/sync-wave': '2',
48
+ },
49
+ },
50
+ spec: {
51
+ repo: 'https://prometheus-community.github.io/helm-charts',
52
+ chart: 'kube-prometheus-stack',
53
+ version: config.versions.prometheusStack === 'latest' ? undefined : config.versions.prometheusStack,
54
+ targetNamespace: namespace,
55
+ valuesContent: helmValues,
56
+ },
57
+ });
58
+ }
59
+ generateHelmValues(config) {
60
+ return `kubeEtcd:
61
+ enabled: false
62
+
63
+ # Disable kube-controller-manager and kube-scheduler monitoring. See https://github.com/cablespaghetti/k3s-monitoring/issues/2
64
+ kubeControllerManager:
65
+ enabled: false
66
+ kubeScheduler:
67
+ enabled: false
68
+
69
+ # Detect PodDisruptionBudgets that would block a node drain during k3s upgrades.
70
+ # Operator-managed PDBs sit at disruptionsAllowed=0 by design (Longhorn instance-managers,
71
+ # CNPG *-primary) and are excluded so the alert only fires on anomalous app PDBs.
72
+ additionalPrometheusRulesMap:
73
+ pdb-drain-safety:
74
+ groups:
75
+ - name: pdb.rules
76
+ interval: 1m
77
+ rules:
78
+ - alert: PodDisruptionBudgetUndrainable
79
+ expr: kube_poddisruptionbudget_status_pod_disruptions_allowed{namespace!="longhorn-system", poddisruptionbudget!~".*-primary"} == 0
80
+ for: 15m
81
+ labels:
82
+ severity: warning
83
+ component: cluster-upgrade
84
+ annotations:
85
+ summary: "PDB {{ $labels.namespace }}/{{ $labels.poddisruptionbudget }} allows 0 voluntary disruptions"
86
+ description: |
87
+ The PodDisruptionBudget {{ $labels.namespace }}/{{ $labels.poddisruptionbudget }}
88
+ has allowed 0 voluntary disruptions for over 15 minutes. This will block a node
89
+ drain during k3s upgrades. Operator-managed PDBs (Longhorn, CNPG primaries) are
90
+ excluded from this alert. Likely a degraded deployment (0 healthy pods) or a
91
+ single-replica deployment with minAvailable=1.
92
+ Check: kubectl get pdb -n {{ $labels.namespace }} {{ $labels.poddisruptionbudget }}
93
+ - alert: PodDisruptionBudgetUndrainableCritical
94
+ expr: kube_poddisruptionbudget_status_pod_disruptions_allowed{namespace!="longhorn-system", poddisruptionbudget!~".*-primary"} == 0
95
+ for: 1h
96
+ labels:
97
+ severity: critical
98
+ component: cluster-upgrade
99
+ annotations:
100
+ summary: "PDB {{ $labels.namespace }}/{{ $labels.poddisruptionbudget }} undrainable >1h"
101
+ description: |
102
+ PDB {{ $labels.namespace }}/{{ $labels.poddisruptionbudget }} has blocked
103
+ voluntary disruptions for over an hour. A k3s agent upgrade would stall on this node.
104
+ # Detect an EX dedicated worker stuck NotReady — likely a MicroOS reboot hang
105
+ # (pings but sshd/kubelet dead). A hung node also holds the cluster-wide kured
106
+ # lock, blocking all further graceful reboots, so this must be caught fast.
107
+ ex-worker-reboot:
108
+ groups:
109
+ - name: ex-worker.rules
110
+ interval: 1m
111
+ rules:
112
+ - alert: ExWorkerNotReady
113
+ expr: kube_node_status_condition{condition="Ready",status="true",node=~"kup6s-ex-.*"} == 0
114
+ for: 15m
115
+ labels:
116
+ severity: critical
117
+ component: node
118
+ annotations:
119
+ summary: "EX worker {{ $labels.node }} NotReady for >15m"
120
+ description: |
121
+ EX dedicated worker {{ $labels.node }} has been NotReady for over 15 minutes.
122
+ Possible MicroOS reboot hang (pings but sshd/kubelet/longhorn-manager dead) — the
123
+ node may still hold the cluster-wide kured lock, blocking all further reboots.
124
+ Recovery: Hetzner Robot hardware reset. Do NOT force-detach Longhorn volumes.
125
+ runbook_url: "https://docs.kup6s.com/how-to/troubleshooting/ex-worker-ping-but-dead.html"
126
+
127
+ # Drop high-cardinality API server and etcd histogram buckets to reduce memory usage
128
+ # These metrics alone account for ~200k series (47% of total cardinality)
129
+ # We keep _sum and _count suffixes which are sufficient for rate calculations
130
+ kubeApiServer:
131
+ serviceMonitor:
132
+ metricRelabelings:
133
+ - sourceLabels: [__name__]
134
+ regex: 'apiserver_request_duration_seconds_bucket'
135
+ action: drop
136
+ - sourceLabels: [__name__]
137
+ regex: 'apiserver_request_sli_duration_seconds_bucket'
138
+ action: drop
139
+ - sourceLabels: [__name__]
140
+ regex: 'apiserver_request_body_size_bytes_bucket'
141
+ action: drop
142
+ - sourceLabels: [__name__]
143
+ regex: 'apiserver_response_sizes_bucket'
144
+ action: drop
145
+ - sourceLabels: [__name__]
146
+ regex: 'etcd_request_duration_seconds_bucket'
147
+ action: drop
148
+
149
+ # Kubelet also exposes apiserver metrics - apply same filters
150
+ kubelet:
151
+ serviceMonitor:
152
+ metricRelabelings:
153
+ - sourceLabels: [__name__]
154
+ regex: 'apiserver_request_duration_seconds_bucket'
155
+ action: drop
156
+ - sourceLabels: [__name__]
157
+ regex: 'apiserver_request_sli_duration_seconds_bucket'
158
+ action: drop
159
+ - sourceLabels: [__name__]
160
+ regex: 'apiserver_request_body_size_bytes_bucket'
161
+ action: drop
162
+ - sourceLabels: [__name__]
163
+ regex: 'apiserver_response_sizes_bucket'
164
+ action: drop
165
+ - sourceLabels: [__name__]
166
+ regex: 'etcd_request_duration_seconds_bucket'
167
+ action: drop
168
+
169
+ alertmanager:
170
+ config:
171
+ global:
172
+ smtp_from: ${config.smtp.from}
173
+ smtp_smarthost: ${config.smtp.host}:${config.smtp.port}
174
+ smtp_require_tls: ${config.smtp.requireTls}
175
+ smtp_auth_username: ${config.smtp.username || config.smtp.from}
176
+ smtp_auth_password: ${config.smtp.password || ''}
177
+ route:
178
+ group_by: ['job']
179
+ group_wait: 30s
180
+ group_interval: 5m
181
+ repeat_interval: 1h
182
+ receiver: email
183
+ routes:
184
+ - match:
185
+ alertname: Watchdog
186
+ receiver: 'null'
187
+ - match:
188
+ alertname: CPUThrottlingHigh
189
+ receiver: 'null'
190
+ - match:
191
+ alertname: KubeMemoryOvercommit
192
+ receiver: 'null'
193
+ - match:
194
+ alertname: KubeCPUOvercommit
195
+ receiver: 'null'
196
+ - match:
197
+ alertname: KubeletTooManyPods
198
+ receiver: 'null'
199
+
200
+ receivers:
201
+ - name: 'null'
202
+ - name: email
203
+ email_configs:
204
+ - send_resolved: true
205
+ to: ${config.smtp.from}
206
+
207
+ # Inhibition rules allow to mute a set of alerts given that another alert is firing.
208
+ # We use this to mute any warning-level notifications if the same alert is already critical.
209
+ inhibit_rules:
210
+ - source_match:
211
+ severity: 'critical'
212
+ target_match:
213
+ severity: 'warning'
214
+ # Apply inhibition if the alertname is the same.
215
+ equal: ['alertname', 'namespace']
216
+
217
+ alertmanagerSpec:
218
+ replicas: ${config.replicas.alertmanager}
219
+ podAntiAffinity: "soft"
220
+ storage:
221
+ volumeClaimTemplate:
222
+ spec:
223
+ accessModes: ["ReadWriteOnce"]
224
+ resources:
225
+ requests:
226
+ storage: ${config.storage.alertmanager}
227
+ resources:
228
+ limits:
229
+ cpu: ${config.resources.alertmanager.limits.cpu}
230
+ memory: ${config.resources.alertmanager.limits.memory}
231
+ requests:
232
+ cpu: ${config.resources.alertmanager.requests.cpu}
233
+ memory: ${config.resources.alertmanager.requests.memory}
234
+ priorityClassName: high-priority
235
+
236
+ # Sidecar containers resource requests (added 2025-11-18)
237
+ containers:
238
+ - name: config-reloader
239
+ resources:
240
+ requests:
241
+ cpu: ${config.resources.configReloader.requests.cpu}
242
+ memory: ${config.resources.configReloader.requests.memory}
243
+ limits:
244
+ cpu: ${config.resources.configReloader.limits.cpu}
245
+ memory: ${config.resources.configReloader.limits.memory}
246
+
247
+
248
+ prometheus:
249
+ # Enable Thanos service for gRPC communication
250
+ # Required for Helm chart to properly render Thanos sidecar configuration
251
+ thanosService:
252
+ enabled: true
253
+
254
+ prometheusSpec:
255
+ retention: ${config.retention.prometheus} # Reduced from 7d - Thanos provides long-term storage in S3
256
+
257
+ # Discover ALL ServiceMonitors and PodMonitors in ALL namespaces
258
+ # Setting NilUsesHelmValues: false means nil selectors = "select all"
259
+ # Empty namespace selectors {} mean "search all namespaces"
260
+ # Note: This does NOT affect Thanos sidecar - controlled by thanos.objectStorageConfig
261
+ serviceMonitorSelectorNilUsesHelmValues: false
262
+ podMonitorSelectorNilUsesHelmValues: false
263
+ serviceMonitorNamespaceSelector: {}
264
+ podMonitorNamespaceSelector: {}
265
+
266
+ replicas: ${config.replicas.prometheus}
267
+ podAntiAffinity: "hard"
268
+ storageSpec:
269
+ volumeClaimTemplate:
270
+ spec:
271
+ storageClassName: longhorn
272
+ accessModes: ["ReadWriteOnce"]
273
+ resources:
274
+ requests:
275
+ storage: ${config.storage.prometheus} # Reduced from 6Gi (3d retention needs less space)
276
+
277
+ resources:
278
+ limits:
279
+ cpu: ${config.resources.prometheus.limits.cpu}
280
+ memory: ${config.resources.prometheus.limits.memory}
281
+ requests:
282
+ cpu: ${config.resources.prometheus.requests.cpu}
283
+ memory: ${config.resources.prometheus.requests.memory}
284
+ priorityClassName: high-priority
285
+
286
+ # External labels for multi-cluster identification and Thanos
287
+ externalLabels:
288
+ cluster: kup6s
289
+ replica: "$(POD_NAME)"
290
+
291
+ # Thanos sidecar for long-term metrics storage in S3
292
+ # NOTE: chart >=85.x requires the existingSecret shape. The legacy flat
293
+ # {name, key} form is silently dropped by the chart, which removes the
294
+ # sidecar and stops all S3 block uploads (regression seen 2026-03-31).
295
+ thanos:
296
+ objectStorageConfig:
297
+ existingSecret:
298
+ name: thanos-objstore-config # ESO-managed secret
299
+ key: objstore.yml
300
+
301
+ # Sidecar containers resource requests (added 2025-11-18)
302
+ # NOTE: thanos-sidecar is NOT listed here - it's automatically injected by Prometheus Operator
303
+ # when thanos.objectStorageConfig is set. Specifying it in containers[] would override
304
+ # the operator's definition and require us to manually specify the image, which breaks
305
+ # when the operator updates the thanos version.
306
+ containers:
307
+ - name: config-reloader
308
+ resources:
309
+ requests:
310
+ cpu: ${config.resources.configReloader.requests.cpu}
311
+ memory: ${config.resources.configReloader.requests.memory}
312
+ limits:
313
+ cpu: ${config.resources.configReloader.limits.cpu}
314
+ memory: ${config.resources.configReloader.limits.memory}
315
+
316
+ service:
317
+ sessionAffinity: "ClientIP"
318
+
319
+
320
+ grafana:
321
+ # Recreate strategy is required because the storage PVC is RWO:
322
+ # a RollingUpdate would surge a second pod that cannot mount the
323
+ # volume (FailedAttachVolume), blocking the rollout indefinitely.
324
+ deploymentStrategy:
325
+ type: Recreate
326
+
327
+ # Use existing secret for admin credentials (managed by ESO)
328
+ # This prevents password regeneration on Helm upgrades
329
+ admin:
330
+ existingSecret: "grafana-admin-credentials"
331
+ userKey: "admin-user"
332
+ passwordKey: "admin-password"
333
+
334
+ # Disable default Prometheus datasource (we provide our own pointing to Thanos Query)
335
+ defaultDatasourceEnabled: false
336
+
337
+ # Sidecar containers for dashboard/datasource provisioning
338
+ sidecar:
339
+ resources:
340
+ requests:
341
+ cpu: 10m
342
+ memory: 256Mi
343
+ limits:
344
+ cpu: 100m
345
+ memory: 512Mi
346
+ dashboards:
347
+ enabled: true
348
+ datasources:
349
+ enabled: false # We manually provision datasources (Prometheus→Thanos, Loki)
350
+
351
+ ingress:
352
+ enabled: true
353
+ hosts:
354
+ - ${config.domains.grafana}
355
+ annotations:
356
+ cert-manager.io/cluster-issuer: "letsencrypt-cluster-issuer"
357
+ traefik.ingress.kubernetes.io/router.entrypoints: websecure
358
+ tls:
359
+ - secretName: grafana-general-tls
360
+ hosts:
361
+ - ${config.domains.grafana}
362
+
363
+ resources:
364
+ limits:
365
+ cpu: ${config.resources.grafana.limits.cpu}
366
+ memory: ${config.resources.grafana.limits.memory}
367
+ requests:
368
+ cpu: ${config.resources.grafana.requests.cpu}
369
+ memory: ${config.resources.grafana.requests.memory}
370
+
371
+ readinessProbe:
372
+ httpGet:
373
+ path: /api/health
374
+ port: 3000
375
+ initialDelaySeconds: 30
376
+ periodSeconds: 10
377
+ timeoutSeconds: 5
378
+ failureThreshold: 3
379
+
380
+ livenessProbe:
381
+ httpGet:
382
+ path: /api/health
383
+ port: 3000
384
+ initialDelaySeconds: 60
385
+ periodSeconds: 10
386
+ timeoutSeconds: 30
387
+ failureThreshold: 10
388
+
389
+ persistence:
390
+ enabled: true
391
+ storageClassName: longhorn
392
+ accessModes:
393
+ - ReadWriteOnce
394
+ size: ${config.storage.grafana}
395
+
396
+ # Manual datasource provisioning (sidecar disabled)
397
+ # Thanos Query → long-term/historical metrics (S3 via Store + live sidecar), default
398
+ # Prometheus → local, recent/high-res only (kept for low-latency recent queries)
399
+ # Loki for log aggregation
400
+ datasources:
401
+ datasources.yaml:
402
+ apiVersion: 1
403
+ # Delete any pre-existing Loki datasource (e.g. one created with a
404
+ # randomly generated UID before we pinned uid: loki). Without this,
405
+ # Grafana fails provisioning with "data source not found" because
406
+ # the upsert collides with the existing entry.
407
+ deleteDatasources:
408
+ - name: Loki
409
+ orgId: 1
410
+ datasources:
411
+ - name: Thanos
412
+ type: prometheus
413
+ uid: thanos
414
+ access: proxy
415
+ # thanos-query Service: http port 9090 -> container http 10902
416
+ url: http://thanos-query.${config.namespace}.svc.cluster.local:9090
417
+ isDefault: true
418
+ jsonData:
419
+ timeInterval: 30s
420
+ - name: Prometheus
421
+ type: prometheus
422
+ uid: prometheus
423
+ access: proxy
424
+ url: http://kube-prometheus-stack-prometheus.${config.namespace}.svc.cluster.local:9090
425
+ isDefault: false
426
+ jsonData:
427
+ timeInterval: 30s
428
+ - name: Loki
429
+ type: loki
430
+ uid: loki
431
+ access: proxy
432
+ url: http://loki-gateway.${config.namespace}.svc.cluster.local
433
+ jsonData:
434
+ maxLines: 1000
435
+
436
+ prometheusOperator:
437
+ resources:
438
+ limits:
439
+ cpu: 1
440
+ memory: 512Mi
441
+ requests:
442
+ cpu: 50m
443
+ memory: 128Mi
444
+ priorityClassName: high-priority
445
+
446
+ prometheus-node-exporter:
447
+ # Bind :9100 to the node InternalIP only, not 0.0.0.0. With kubeRBACProxy
448
+ # disabled the sub-chart sets HOST_IP from fieldRef status.hostIP, which is
449
+ # the node's InternalIP (EX: vSwitch 10.6.0.x via --node-ip; cloud: hcloud
450
+ # private IP). node-exporter is hostNetwork, so Prometheus already scrapes
451
+ # that InternalIP endpoint -> scraping is unaffected, only the public-NIC
452
+ # exposure of the unauthenticated metrics on the EX public IPs goes away.
453
+ service:
454
+ listenOnAllInterfaces: false
455
+ resources:
456
+ limits:
457
+ cpu: 50m
458
+ memory: 50Mi
459
+ requests:
460
+ cpu: 5m
461
+ memory: 16Mi
462
+ priorityClassName: high-priority
463
+
464
+ kube-state-metrics:
465
+ resources:
466
+ limits:
467
+ cpu: 1
468
+ memory: 512Mi
469
+ requests:
470
+ cpu: 5m
471
+ memory: 128Mi
472
+ priorityClassName: high-priority
473
+ `;
474
+ }
475
+ }
476
+ exports.PrometheusStackConstruct = PrometheusStackConstruct;
477
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvbWV0aGV1cy1zdGFjay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25zdHJ1Y3RzL3Byb21ldGhldXMtc3RhY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaUNBQWtDO0FBQ2xDLDJDQUF1QztBQVF2Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNILE1BQWEsd0JBQXlCLFNBQVEsc0JBQVM7SUFDckQsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUEyQjtRQUNuRSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRWpCLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBSyxDQUFDO1FBRXBDLDRCQUE0QjtRQUM1QixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFbkQsa0RBQWtEO1FBQ2xELDJDQUEyQztRQUMzQyxJQUFJLGlCQUFTLENBQUMsSUFBSSxFQUFFLHVCQUF1QixFQUFFO1lBQzNDLFVBQVUsRUFBRSxtQkFBbUI7WUFDL0IsSUFBSSxFQUFFLFdBQVc7WUFDakIsUUFBUSxFQUFFO2dCQUNSLElBQUksRUFBRSx1QkFBdUI7Z0JBQzdCLFNBQVMsRUFBRSxTQUFTO2dCQUNwQixNQUFNLEVBQUU7b0JBQ04sd0JBQXdCLEVBQUUsdUJBQXVCO29CQUNqRCw2QkFBNkIsRUFBRSxZQUFZO29CQUMzQyw4QkFBOEIsRUFBRSxPQUFPO29CQUN2QywyQkFBMkIsRUFBRSxZQUFZO2lCQUMxQztnQkFDRCxXQUFXLEVBQUU7b0JBQ1gsOEJBQThCLEVBQUUsR0FBRztpQkFDcEM7YUFDRjtZQUNELElBQUksRUFBRTtnQkFDSixJQUFJLEVBQUUsb0RBQW9EO2dCQUMxRCxLQUFLLEVBQUUsdUJBQXVCO2dCQUM5QixPQUFPLEVBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxlQUFlLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxT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QU0sQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNOzthQUV6QyxNQUFNLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRztnQkFDbEMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU07Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7WUF5QjVDLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzttQ0FzQkMsTUFBTSxDQUFDLFNBQVM7Ozs7Ozs7O3VEQVFJLE1BQU0sQ0FBQyxTQUFTOzs7Ozs7OzttQ0FRcEMsTUFBTSxDQUFDLFNBQVM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBeUNsRCxDQUFDO0lBQ0EsQ0FBQztDQUNGO0FBcmNELDREQXFjQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEFwaU9iamVjdCB9IGZyb20gJ2NkazhzJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgTW9uaXRvcmluZ0NvbmZpZyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGludGVyZmFjZSBQcm9tZXRoZXVzU3RhY2tQcm9wcyB7XG4gIG5hbWVzcGFjZTogc3RyaW5nO1xuICBjb25maWc6IE1vbml0b3JpbmdDb25maWc7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBrdWJlLXByb21ldGhldXMtc3RhY2sgSGVsbUNoYXJ0IHdpdGggUHJvbWV0aGV1cywgR3JhZmFuYSwgYW5kIEFsZXJ0bWFuYWdlci5cbiAqXG4gKiBUaGlzIGlzIHRoZSBjb3JlIG1vbml0b3Jpbmcgc3RhY2sgdGhhdCBpbmNsdWRlczpcbiAqIC0gUHJvbWV0aGV1cyAobWV0cmljcyBjb2xsZWN0aW9uLCAyIHJlcGxpY2FzLCAzZCByZXRlbnRpb24pXG4gKiAtIEdyYWZhbmEgKHZpc3VhbGl6YXRpb24sIGluZ3Jlc3Mgd2l0aCBMZXQncyBFbmNyeXB0KVxuICogLSBBbGVydG1hbmFnZXIgKGFsZXJ0IHJvdXRpbmcsIFNNVFAgbm90aWZpY2F0aW9ucylcbiAqIC0gUHJvbWV0aGV1cyBPcGVyYXRvciAobWFuYWdlcyBQcm9tZXRoZXVzIENSRHMpXG4gKiAtIE5vZGUgRXhwb3J0ZXIgKHN5c3RlbSBtZXRyaWNzIERhZW1vblNldClcbiAqIC0ga3ViZS1zdGF0ZS1tZXRyaWNzIChLdWJlcm5ldGVzIHJlc291cmNlIG1ldHJpY3MpXG4gKlxuICogUHJlcmVxdWlzaXRlczpcbiAqIC0gTmFtZXNwYWNlIGV4aXN0c1xuICogLSBQcmlvcml0eUNsYXNzICdoaWdoLXByaW9yaXR5JyBleGlzdHNcbiAqIC0gTG9uZ2hvcm4gc3RvcmFnZSBjbGFzcyBhdmFpbGFibGVcbiAqIC0gY2VydC1tYW5hZ2VyIENsdXN0ZXJJc3N1ZXIgY29uZmlndXJlZFxuICogLSBUaGFub3MgUzMgc2VjcmV0IGV4aXN0cyAodGhhbm9zLW9ianN0b3JlLWNvbmZpZylcbiAqXG4gKiBTeW5jIFdhdmU6IDIgKGNvcmUgc2VydmljZXMgLSBhZnRlciBTMyBidWNrZXRzIGFuZCBzZWNyZXRzKVxuICovXG5leHBvcnQgY2xhc3MgUHJvbWV0aGV1c1N0YWNrQ29uc3RydWN0IGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IFByb21ldGhldXNTdGFja1Byb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcblxuICAgIGNvbnN0IHsgbmFtZXNwYWNlLCBjb25maWcgfSA9IHByb3BzO1xuXG4gICAgLy8gR2VuZXJhdGUgSGVsbSB2YWx1ZXMgWUFNTFxuICAgIGNvbnN0IGhlbG1WYWx1ZXMgPSB0aGlzLmdlbmVyYXRlSGVsbVZhbHVlcyhjb25maWcpO1xuXG4gICAgLy8gQ3JlYXRlIEhlbG1DaGFydCByZXNvdXJjZSAoSzNTIEhlbG0gY29udHJvbGxlcilcbiAgICAvLyBXYXZlIDI6IENvcmUgU2VydmljZXMgLSBtb25pdG9yaW5nIHN0YWNrXG4gICAgbmV3IEFwaU9iamVjdCh0aGlzLCAna3ViZS1wcm9tZXRoZXVzLXN0YWNrJywge1xuICAgICAgYXBpVmVyc2lvbjogJ2hlbG0uY2F0dGxlLmlvL3YxJyxcbiAgICAgIGtpbmQ6ICdIZWxtQ2hhcnQnLFxuICAgICAgbWV0YWRhdGE6IHtcbiAgICAgICAgbmFtZTogJ2t1YmUtcHJvbWV0aGV1cy1zdGFjaycsXG4gICAgICAgIG5hbWVzcGFjZTogbmFtZXNwYWNlLFxuICAgICAgICBsYWJlbHM6IHtcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbmFtZSc6ICdrdWJlLXByb21ldGhldXMtc3RhY2snLFxuICAgICAgICAgICdhcHAua3ViZXJuZXRlcy5pby9jb21wb25lbnQnOiAnbW9uaXRvcmluZycsXG4gICAgICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL21hbmFnZWQtYnknOiAnY2RrOHMnLFxuICAgICAgICAgICdhcHAua3ViZXJuZXRlcy5pby9wYXJ0LW9mJzogJ21vbml0b3JpbmcnLFxuICAgICAgICB9LFxuICAgICAgICBhbm5vdGF0aW9uczoge1xuICAgICAgICAgICdhcmdvY2QuYXJnb3Byb2ouaW8vc3luYy13YXZlJzogJzInLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIHNwZWM6IHtcbiAgICAgICAgcmVwbzogJ2h0dHBzOi8vcHJvbWV0aGV1cy1jb21tdW5pdHkuZ2l0aHViLmlvL2hlbG0tY2hhcnRzJyxcbiAgICAgICAgY2hhcnQ6ICdrdWJlLXByb21ldGhldXMtc3RhY2snLFxuICAgICAgICB2ZXJzaW9uOiBjb25maWcudmVyc2lvbnMucHJvbWV0aGV1c1N0YWNrID09PSAnbGF0ZXN0JyA/IHVuZGVmaW5lZCA6IGNvbmZpZy52ZXJzaW9ucy5wcm9tZXRoZXVzU3RhY2ssXG4gICAgICAgIHRhcmdldE5hbWVzcGFjZTogbmFtZXNwYWNlLFxuICAgICAgICB2YWx1ZXNDb250ZW50OiBoZWxtVmFsdWVzLFxuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgZ2VuZXJhdGVIZWxtVmFsdWVzKGNvbmZpZzogTW9uaXRvcmluZ0NvbmZpZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGBrdWJlRXRjZDpcbiAgZW5hYmxlZDogZmFsc2VcblxuIyBEaXNhYmxlIGt1YmUtY29udHJvbGxlci1tYW5hZ2VyIGFuZCBrdWJlLXNjaGVkdWxlciBtb25pdG9yaW5nLiBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2NhYmxlc3BhZ2hldHRpL2szcy1tb25pdG9yaW5nL2lzc3Vlcy8yXG5rdWJlQ29udHJvbGxlck1hbmFnZXI6XG4gIGVuYWJsZWQ6IGZhbHNlXG5rdWJlU2NoZWR1bGVyOlxuICBlbmFibGVkOiBmYWxzZVxuXG4jIERldGVjdCBQb2REaXNydXB0aW9uQnVkZ2V0cyB0aGF0IHdvdWxkIGJsb2NrIGEgbm9kZSBkcmFpbiBkdXJpbmcgazNzIHVwZ3JhZGVzLlxuIyBPcGVyYXRvci1tYW5hZ2VkIFBEQnMgc2l0IGF0IGRpc3J1cHRpb25zQWxsb3dlZD0wIGJ5IGRlc2lnbiAoTG9uZ2hvcm4gaW5zdGFuY2UtbWFuYWdlcnMsXG4jIENOUEcgKi1wcmltYXJ5KSBhbmQgYXJlIGV4Y2x1ZGVkIHNvIHRoZSBhbGVydCBvbmx5IGZpcmVzIG9uIGFub21hbG91cyBhcHAgUERCcy5cbmFkZGl0aW9uYWxQcm9tZXRoZXVzUnVsZXNNYXA6XG4gIHBkYi1kcmFpbi1zYWZldHk6XG4gICAgZ3JvdXBzOlxuICAgICAgLSBuYW1lOiBwZGIucnVsZXNcbiAgICAgICAgaW50ZXJ2YWw6IDFtXG4gICAgICAgIHJ1bGVzOlxuICAgICAgICAgIC0gYWxlcnQ6IFBvZERpc3J1cHRpb25CdWRnZXRVbmRyYWluYWJsZVxuICAgICAgICAgICAgZXhwcjoga3ViZV9wb2RkaXNydXB0aW9uYnVkZ2V0X3N0YXR1c19wb2RfZGlzcnVwdGlvbnNfYWxsb3dlZHtuYW1lc3BhY2UhPVwibG9uZ2hvcm4tc3lzdGVtXCIsIHBvZGRpc3J1cHRpb25idWRnZXQhflwiLiotcHJpbWFyeVwifSA9PSAwXG4gICAgICAgICAgICBmb3I6IDE1bVxuICAgICAgICAgICAgbGFiZWxzOlxuICAgICAgICAgICAgICBzZXZlcml0eTogd2FybmluZ1xuICAgICAgICAgICAgICBjb21wb25lbnQ6IGNsdXN0ZXItdXBncmFkZVxuICAgICAgICAgICAgYW5ub3RhdGlvbnM6XG4gICAgICAgICAgICAgIHN1bW1hcnk6IFwiUERCIHt7ICRsYWJlbHMubmFtZXNwYWNlIH19L3t7ICRsYWJlbHMucG9kZGlzcnVwdGlvbmJ1ZGdldCB9fSBhbGxvd3MgMCB2b2x1bnRhcnkgZGlzcnVwdGlvbnNcIlxuICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogfFxuICAgICAgICAgICAgICAgIFRoZSBQb2REaXNydXB0aW9uQnVkZ2V0IHt7ICRsYWJlbHMubmFtZXNwYWNlIH19L3t7ICRsYWJlbHMucG9kZGlzcnVwdGlvbmJ1ZGdldCB9fVxuICAgICAgICAgICAgICAgIGhhcyBhbGxvd2VkIDAgdm9sdW50YXJ5IGRpc3J1cHRpb25zIGZvciBvdmVyIDE1IG1pbnV0ZXMuIFRoaXMgd2lsbCBibG9jayBhIG5vZGVcbiAgICAgICAgICAgICAgICBkcmFpbiBkdXJpbmcgazNzIHVwZ3JhZGVzLiBPcGVyYXRvci1tYW5hZ2VkIFBEQnMgKExvbmdob3JuLCBDTlBHIHByaW1hcmllcykgYXJlXG4gICAgICAgICAgICAgICAgZXhjbHVkZWQgZnJvbSB0aGlzIGFsZXJ0LiBMaWtlbHkgYSBkZWdyYWRlZCBkZXBsb3ltZW50ICgwIGhlYWx0aHkgcG9kcykgb3IgYVxuICAgICAgICAgICAgICAgIHNpbmdsZS1yZXBsaWNhIGRlcGxveW1lbnQgd2l0aCBtaW5BdmFpbGFibGU9MS5cbiAgICAgICAgICAgICAgICBDaGVjazoga3ViZWN0bCBnZXQgcGRiIC1uIHt7ICRsYWJlbHMubmFtZXNwYWNlIH19IHt7ICRsYWJlbHMucG9kZGlzcnVwdGlvbmJ1ZGdldCB9fVxuICAgICAgICAgIC0gYWxlcnQ6IFBvZERpc3J1cHRpb25CdWRnZXRVbmRyYWluYWJsZUNyaXRpY2FsXG4gICAgICAgICAgICBleHByOiBrdWJlX3BvZGRpc3J1cHRpb25idWRnZXRfc3RhdHVzX3BvZF9kaXNydXB0aW9uc19hbGxvd2Vke25hbWVzcGFjZSE9XCJsb25naG9ybi1zeXN0ZW1cIiwgcG9kZGlzcnVwdGlvbmJ1ZGdldCF+XCIuKi1wcmltYXJ5XCJ9ID09IDBcbiAgICAgICAgICAgIGZvcjogMWhcbiAgICAgICAgICAgIGxhYmVsczpcbiAgICAgICAgICAgICAgc2V2ZXJpdHk6IGNyaXRpY2FsXG4gICAgICAgICAgICAgIGNvbXBvbmVudDogY2x1c3Rlci11cGdyYWRlXG4gICAgICAgICAgICBhbm5vdGF0aW9uczpcbiAgICAgICAgICAgICAgc3VtbWFyeTogXCJQREIge3sgJGxhYmVscy5uYW1lc3BhY2UgfX0ve3sgJGxhYmVscy5wb2RkaXNydXB0aW9uYnVkZ2V0IH19IHVuZHJhaW5hYmxlID4xaFwiXG4gICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiB8XG4gICAgICAgICAgICAgICAgUERCIHt7ICRsYWJlbHMubmFtZXNwYWNlIH19L3t7ICRsYWJlbHMucG9kZGlzcnVwdGlvbmJ1ZGdldCB9fSBoYXMgYmxvY2tlZFxuICAgICAgICAgICAgICAgIHZvbHVudGFyeSBkaXNydXB0aW9ucyBmb3Igb3ZlciBhbiBob3VyLiBBIGszcyBhZ2VudCB1cGdyYWRlIHdvdWxkIHN0YWxsIG9uIHRoaXMgbm9kZS5cbiAgIyBEZXRlY3QgYW4gRVggZGVkaWNhdGVkIHdvcmtlciBzdHVjayBOb3RSZWFkeSDigJQgbGlrZWx5IGEgTWljcm9PUyByZWJvb3QgaGFuZ1xuICAjIChwaW5ncyBidXQgc3NoZC9rdWJlbGV0IGRlYWQpLiBBIGh1bmcgbm9kZSBhbHNvIGhvbGRzIHRoZSBjbHVzdGVyLXdpZGUga3VyZWRcbiAgIyBsb2NrLCBibG9ja2luZyBhbGwgZnVydGhlciBncmFjZWZ1bCByZWJvb3RzLCBzbyB0aGlzIG11c3QgYmUgY2F1Z2h0IGZhc3QuXG4gIGV4LXdvcmtlci1yZWJvb3Q6XG4gICAgZ3JvdXBzOlxuICAgICAgLSBuYW1lOiBleC13b3JrZXIucnVsZXNcbiAgICAgICAgaW50ZXJ2YWw6IDFtXG4gICAgICAgIHJ1bGVzOlxuICAgICAgICAgIC0gYWxlcnQ6IEV4V29ya2VyTm90UmVhZHlcbiAgICAgICAgICAgIGV4cHI6IGt1YmVfbm9kZV9zdGF0dXNfY29uZGl0aW9ue2NvbmRpdGlvbj1cIlJlYWR5XCIsc3RhdHVzPVwidHJ1ZVwiLG5vZGU9flwia3VwNnMtZXgtLipcIn0gPT0gMFxuICAgICAgICAgICAgZm9yOiAxNW1cbiAgICAgICAgICAgIGxhYmVsczpcbiAgICAgICAgICAgICAgc2V2ZXJpdHk6IGNyaXRpY2FsXG4gICAgICAgICAgICAgIGNvbXBvbmVudDogbm9kZVxuICAgICAgICAgICAgYW5ub3RhdGlvbnM6XG4gICAgICAgICAgICAgIHN1bW1hcnk6IFwiRVggd29ya2VyIHt7ICRsYWJlbHMubm9kZSB9fSBOb3RSZWFkeSBmb3IgPjE1bVwiXG4gICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiB8XG4gICAgICAgICAgICAgICAgRVggZGVkaWNhdGVkIHdvcmtlciB7eyAkbGFiZWxzLm5vZGUgfX0gaGFzIGJlZW4gTm90UmVhZHkgZm9yIG92ZXIgMTUgbWludXRlcy5cbiAgICAgICAgICAgICAgICBQb3NzaWJsZSBNaWNyb09TIHJlYm9vdCBoYW5nIChwaW5ncyBidXQgc3NoZC9rdWJlbGV0L2xvbmdob3JuLW1hbmFnZXIgZGVhZCkg4oCUIHRoZVxuICAgICAgICAgICAgICAgIG5vZGUgbWF5IHN0aWxsIGhvbGQgdGhlIGNsdXN0ZXItd2lkZSBrdXJlZCBsb2NrLCBibG9ja2luZyBhbGwgZnVydGhlciByZWJvb3RzLlxuICAgICAgICAgICAgICAgIFJlY292ZXJ5OiBIZXR6bmVyIFJvYm90IGhhcmR3YXJlIHJlc2V0LiBEbyBOT1QgZm9yY2UtZGV0YWNoIExvbmdob3JuIHZvbHVtZXMuXG4gICAgICAgICAgICAgIHJ1bmJvb2tfdXJsOiBcImh0dHBzOi8vZG9jcy5rdXA2cy5jb20vaG93LXRvL3Ryb3VibGVzaG9vdGluZy9leC13b3JrZXItcGluZy1idXQtZGVhZC5odG1sXCJcblxuIyBEcm9wIGhpZ2gtY2FyZGluYWxpdHkgQVBJIHNlcnZlciBhbmQgZXRjZCBoaXN0b2dyYW0gYnVja2V0cyB0byByZWR1Y2UgbWVtb3J5IHVzYWdlXG4jIFRoZXNlIG1ldHJpY3MgYWxvbmUgYWNjb3VudCBmb3IgfjIwMGsgc2VyaWVzICg0NyUgb2YgdG90YWwgY2FyZGluYWxpdHkpXG4jIFdlIGtlZXAgX3N1bSBhbmQgX2NvdW50IHN1ZmZpeGVzIHdoaWNoIGFyZSBzdWZmaWNpZW50IGZvciByYXRlIGNhbGN1bGF0aW9uc1xua3ViZUFwaVNlcnZlcjpcbiAgc2VydmljZU1vbml0b3I6XG4gICAgbWV0cmljUmVsYWJlbGluZ3M6XG4gICAgICAtIHNvdXJjZUxhYmVsczogW19fbmFtZV9fXVxuICAgICAgICByZWdleDogJ2FwaXNlcnZlcl9yZXF1ZXN0X2R1cmF0aW9uX3NlY29uZHNfYnVja2V0J1xuICAgICAgICBhY3Rpb246IGRyb3BcbiAgICAgIC0gc291cmNlTGFiZWxzOiBbX19uYW1lX19dXG4gICAgICAgIHJlZ2V4OiAnYXBpc2VydmVyX3JlcXVlc3Rfc2xpX2R1cmF0aW9uX3NlY29uZHNfYnVja2V0J1xuICAgICAgICBhY3Rpb246IGRyb3BcbiAgICAgIC0gc291cmNlTGFiZWxzOiBbX19uYW1lX19dXG4gICAgICAgIHJlZ2V4OiAnYXBpc2VydmVyX3JlcXVlc3RfYm9keV9zaXplX2J5dGVzX2J1Y2tldCdcbiAgICAgICAgYWN0aW9uOiBkcm9wXG4gICAgICAtIHNvdXJjZUxhYmVsczogW19fbmFtZV9fXVxuICAgICAgICByZWdleDogJ2FwaXNlcnZlcl9yZXNwb25zZV9zaXplc19idWNrZXQnXG4gICAgICAgIGFjdGlvbjogZHJvcFxuICAgICAgLSBzb3VyY2VMYWJlbHM6IFtfX25hbWVfX11cbiAgICAgICAgcmVnZXg6ICdldGNkX3JlcXVlc3RfZHVyYXRpb25fc2Vjb25kc19idWNrZXQnXG4gICAgICAgIGFjdGlvbjogZHJvcFxuXG4jIEt1YmVsZXQgYWxzbyBleHBvc2VzIGFwaXNlcnZlciBtZXRyaWNzIC0gYXBwbHkgc2FtZSBmaWx0ZXJzXG5rdWJlbGV0OlxuICBzZXJ2aWNlTW9uaXRvcjpcbiAgICBtZXRyaWNSZWxhYmVsaW5nczpcbiAgICAgIC0gc291cmNlTGFiZWxzOiBbX19uYW1lX19dXG4gICAgICAgIHJlZ2V4OiAnYXBpc2VydmVyX3JlcXVlc3RfZHVyYXRpb25fc2Vjb25kc19idWNrZXQnXG4gICAgICAgIGFjdGlvbjogZHJvcFxuICAgICAgLSBzb3VyY2VMYWJlbHM6IFtfX25hbWVfX11cbiAgICAgICAgcmVnZXg6ICdhcGlzZXJ2ZXJfcmVxdWVzdF9zbGlfZHVyYXRpb25fc2Vjb25kc19idWNrZXQnXG4gICAgICAgIGFjdGlvbjogZHJvcFxuICAgICAgLSBzb3VyY2VMYWJlbHM6IFtfX25hbWVfX11cbiAgICAgICAgcmVnZXg6ICdhcGlzZXJ2ZXJfcmVxdWVzdF9ib2R5X3NpemVfYnl0ZXNfYnVja2V0J1xuICAgICAgICBhY3Rpb246IGRyb3BcbiAgICAgIC0gc291cmNlTGFiZWxzOiBbX19uYW1lX19dXG4gICAgICAgIHJlZ2V4OiAnYXBpc2VydmVyX3Jlc3BvbnNlX3NpemVzX2J1Y2tldCdcbiAgICAgICAgYWN0aW9uOiBkcm9wXG4gICAgICAtIHNvdXJjZUxhYmVsczogW19fbmFtZV9fXVxuICAgICAgICByZWdleDogJ2V0Y2RfcmVxdWVzdF9kdXJhdGlvbl9zZWNvbmRzX2J1Y2tldCdcbiAgICAgICAgYWN0aW9uOiBkcm9wXG5cbmFsZXJ0bWFuYWdlcjpcbiAgY29uZmlnOlxuICAgIGdsb2JhbDpcbiAgICAgIHNtdHBfZnJvbTogJHtjb25maWcuc210cC5mcm9tfVxuICAgICAgc210cF9zbWFydGhvc3Q6ICR7Y29uZmlnLnNtdHAuaG9zdH06JHtjb25maWcuc210cC5wb3J0fVxuICAgICAgc210cF9yZXF1aXJlX3RsczogJHtjb25maWcuc210cC5yZXF1aXJlVGxzfVxuICAgICAgc210cF9hdXRoX3VzZXJuYW1lOiAke2NvbmZpZy5zbXRwLnVzZXJuYW1lIHx8IGNvbmZpZy5zbXRwLmZyb219XG4gICAgICBzbXRwX2F1dGhfcGFzc3dvcmQ6ICR7Y29uZmlnLnNtdHAucGFzc3dvcmQgfHwgJyd9XG4gICAgcm91dGU6XG4gICAgICBncm91cF9ieTogWydqb2InXVxuICAgICAgZ3JvdXBfd2FpdDogMzBzXG4gICAgICBncm91cF9pbnRlcnZhbDogNW1cbiAgICAgIHJlcGVhdF9pbnRlcnZhbDogMWhcbiAgICAgIHJlY2VpdmVyOiBlbWFpbFxuICAgICAgcm91dGVzOlxuICAgICAgLSBtYXRjaDpcbiAgICAgICAgICBhbGVydG5hbWU6IFdhdGNoZG9nXG4gICAgICAgIHJlY2VpdmVyOiAnbnVsbCdcbiAgICAgIC0gbWF0Y2g6XG4gICAgICAgICAgYWxlcnRuYW1lOiBDUFVUaHJvdHRsaW5nSGlnaFxuICAgICAgICByZWNlaXZlcjogJ251bGwnXG4gICAgICAtIG1hdGNoOlxuICAgICAgICAgIGFsZXJ0bmFtZTogS3ViZU1lbW9yeU92ZXJjb21taXRcbiAgICAgICAgcmVjZWl2ZXI6ICdudWxsJ1xuICAgICAgLSBtYXRjaDpcbiAgICAgICAgICBhbGVydG5hbWU6IEt1YmVDUFVPdmVyY29tbWl0XG4gICAgICAgIHJlY2VpdmVyOiAnbnVsbCdcbiAgICAgIC0gbWF0Y2g6XG4gICAgICAgICAgYWxlcnRuYW1lOiBLdWJlbGV0VG9vTWFueVBvZHNcbiAgICAgICAgcmVjZWl2ZXI6ICdudWxsJ1xuXG4gICAgcmVjZWl2ZXJzOlxuICAgIC0gbmFtZTogJ251bGwnXG4gICAgLSBuYW1lOiBlbWFpbFxuICAgICAgZW1haWxfY29uZmlnczpcbiAgICAgIC0gc2VuZF9yZXNvbHZlZDogdHJ1ZVxuICAgICAgICB0bzogJHtjb25maWcuc210cC5mcm9tfVxuXG4gICAgIyBJbmhpYml0aW9uIHJ1bGVzIGFsbG93IHRvIG11dGUgYSBzZXQgb2YgYWxlcnRzIGdpdmVuIHRoYXQgYW5vdGhlciBhbGVydCBpcyBmaXJpbmcuXG4gICAgIyBXZSB1c2UgdGhpcyB0byBtdXRlIGFueSB3YXJuaW5nLWxldmVsIG5vdGlmaWNhdGlvbnMgaWYgdGhlIHNhbWUgYWxlcnQgaXMgYWxyZWFkeSBjcml0aWNhbC5cbiAgICBpbmhpYml0X3J1bGVzOlxuICAgIC0gc291cmNlX21hdGNoOlxuICAgICAgICBzZXZlcml0eTogJ2NyaXRpY2FsJ1xuICAgICAgdGFyZ2V0X21hdGNoOlxuICAgICAgICBzZXZlcml0eTogJ3dhcm5pbmcnXG4gICAgICAjIEFwcGx5IGluaGliaXRpb24gaWYgdGhlIGFsZXJ0bmFtZSBpcyB0aGUgc2FtZS5cbiAgICAgIGVxdWFsOiBbJ2FsZXJ0bmFtZScsICduYW1lc3BhY2UnXVxuXG4gIGFsZXJ0bWFuYWdlclNwZWM6XG4gICAgcmVwbGljYXM6ICR7Y29uZmlnLnJlcGxpY2FzLmFsZXJ0bWFuYWdlcn1cbiAgICBwb2RBbnRpQWZmaW5pdHk6IFwic29mdFwiXG4gICAgc3RvcmFnZTpcbiAgICAgIHZvbHVtZUNsYWltVGVtcGxhdGU6XG4gICAgICAgIHNwZWM6XG4gICAgICAgICAgYWNjZXNzTW9kZXM6IFtcIlJlYWRXcml0ZU9uY2VcIl1cbiAgICAgICAgICByZXNvdXJjZXM6XG4gICAgICAgICAgICByZXF1ZXN0czpcbiAgICAgICAgICAgICAgc3RvcmFnZTogJHtjb25maWcuc3RvcmFnZS5hbGVydG1hbmFnZXJ9XG4gICAgcmVzb3VyY2VzOlxuICAgICAgbGltaXRzOlxuICAgICAgICBjcHU6ICR7Y29uZmlnLnJlc291cmNlcy5hbGVydG1hbmFnZXIubGltaXRzLmNwdX1cbiAgICAgICAgbWVtb3J5OiAke2NvbmZpZy5yZXNvdXJjZXMuYWxlcnRtYW5hZ2VyLmxpbWl0cy5tZW1vcnl9XG4gICAgICByZXF1ZXN0czpcbiAgICAgICAgY3B1OiAke2NvbmZpZy5yZXNvdXJjZXMuYWxlcnRtYW5hZ2VyLnJlcXVlc3RzLmNwdX1cbiAgICAgICAgbWVtb3J5OiAke2NvbmZpZy5yZXNvdXJjZXMuYWxlcnRtYW5hZ2VyLnJlcXVlc3RzLm1lbW9yeX1cbiAgICBwcmlvcml0eUNsYXNzTmFtZTogaGlnaC1wcmlvcml0eVxuXG4gICAgIyBTaWRlY2FyIGNvbnRhaW5lcnMgcmVzb3VyY2UgcmVxdWVzdHMgKGFkZGVkIDIwMjUtMTEtMTgpXG4gICAgY29udGFpbmVyczpcbiAgICAtIG5hbWU6IGNvbmZpZy1yZWxvYWRlclxuICAgICAgcmVzb3VyY2VzOlxuICAgICAgICByZXF1ZXN0czpcbiAgICAgICAgICBjcHU6ICR7Y29uZmlnLnJlc291cmNlcy5jb25maWdSZWxvYWRlci5yZXF1ZXN0cy5jcHV9XG4gICAgICAgICAgbWVtb3J5OiAke2NvbmZpZy5yZXNvdXJjZXMuY29uZmlnUmVsb2FkZXIucmVxdWVzdHMubWVtb3J5fVxuICAgICAgICBsaW1pdHM6XG4gICAgICAgICAgY3B1OiAke2NvbmZpZy5yZXNvdXJjZXMuY29uZmlnUmVsb2FkZXIubGltaXRzLmNwdX1cbiAgICAgICAgICBtZW1vcnk6ICR7Y29uZmlnLnJlc291cmNlcy5jb25maWdSZWxvYWRlci5saW1pdHMubWVtb3J5fVxuXG5cbnByb21ldGhldXM6XG4gICMgRW5hYmxlIFRoYW5vcyBzZXJ2aWNlIGZvciBnUlBDIGNvbW11bmljYXRpb25cbiAgIyBSZXF1aXJlZCBmb3IgSGVsbSBjaGFydCB0byBwcm9wZXJseSByZW5kZXIgVGhhbm9zIHNpZGVjYXIgY29uZmlndXJhdGlvblxuICB0aGFub3NTZXJ2aWNlOlxuICAgIGVuYWJsZWQ6IHRydWVcblxuICBwcm9tZXRoZXVzU3BlYzpcbiAgICByZXRlbnRpb246ICR7Y29uZmlnLnJldGVudGlvbi5wcm9tZXRoZXVzfSAgIyBSZWR1Y2VkIGZyb20gN2QgLSBUaGFub3MgcHJvdmlkZXMgbG9uZy10ZXJtIHN0b3JhZ2UgaW4gUzNcblxuICAgICMgRGlzY292ZXIgQUxMIFNlcnZpY2VNb25pdG9ycyBhbmQgUG9kTW9uaXRvcnMgaW4gQUxMIG5hbWVzcGFjZXNcbiAgICAjIFNldHRpbmcgTmlsVXNlc0hlbG1WYWx1ZXM6IGZhbHNlIG1lYW5zIG5pbCBzZWxlY3RvcnMgPSBcInNlbGVjdCBhbGxcIlxuICAgICMgRW1wdHkgbmFtZXNwYWNlIHNlbGVjdG9ycyB7fSBtZWFuIFwic2VhcmNoIGFsbCBuYW1lc3BhY2VzXCJcbiAgICAjIE5vdGU6IFRoaXMgZG9lcyBOT1QgYWZmZWN0IFRoYW5vcyBzaWRlY2FyIC0gY29udHJvbGxlZCBieSB0aGFub3Mub2JqZWN0U3RvcmFnZUNvbmZpZ1xuICAgIHNlcnZpY2VNb25pdG9yU2VsZWN0b3JOaWxVc2VzSGVsbVZhbHVlczogZmFsc2VcbiAgICBwb2RNb25pdG9yU2VsZWN0b3JOaWxVc2VzSGVsbVZhbHVlczogZmFsc2VcbiAgICBzZXJ2aWNlTW9uaXRvck5hbWVzcGFjZVNlbGVjdG9yOiB7fVxuICAgIHBvZE1vbml0b3JOYW1lc3BhY2VTZWxlY3Rvcjoge31cblxuICAgIHJlcGxpY2FzOiAke2NvbmZpZy5yZXBsaWNhcy5wcm9tZXRoZXVzfVxuICAgIHBvZEFudGlBZmZpbml0eTogXCJoYXJkXCJcbiAgICBzdG9yYWdlU3BlYzpcbiAgICAgIHZvbHVtZUNsYWltVGVtcGxhdGU6XG4gICAgICAgIHNwZWM6XG4gICAgICAgICAgc3RvcmFnZUNsYXNzTmFtZTogbG9uZ2hvcm5cbiAgICAgICAgICBhY2Nlc3NNb2RlczogW1wiUmVhZFdyaXRlT25jZVwiXVxuICAgICAgICAgIHJlc291cmNlczpcbiAgICAgICAgICAgIHJlcXVlc3RzOlxuICAgICAgICAgICAgICBzdG9yYWdlOiAke2NvbmZpZy5zdG9yYWdlLnByb21ldGhldXN9ICAjIFJlZHVjZWQgZnJvbSA2R2kgKDNkIHJldGVudGlvbiBuZWVkcyBsZXNzIHNwYWNlKVxuXG4gICAgcmVzb3VyY2VzOlxuICAgICAgbGltaXRzOlxuICAgICAgICBjcHU6ICR7Y29uZmlnLnJlc291cmNlcy5wcm9tZXRoZXVzLmxpbWl0cy5jcHV9XG4gICAgICAgIG1lbW9yeTogJHtjb25maWcucmVzb3VyY2VzLnByb21ldGhldXMubGltaXRzLm1lbW9yeX1cbiAgICAgIHJlcXVlc3RzOlxuICAgICAgICBjcHU6ICR7Y29uZmlnLnJlc291cmNlcy5wcm9tZXRoZXVzLnJlcXVlc3RzLmNwdX1cbiAgICAgICAgbWVtb3J5OiAke2NvbmZpZy5yZXNvdXJjZXMucHJvbWV0aGV1cy5yZXF1ZXN0cy5tZW1vcnl9XG4gICAgcHJpb3JpdHlDbGFzc05hbWU6IGhpZ2gtcHJpb3JpdHlcblxuICAgICMgRXh0ZXJuYWwgbGFiZWxzIGZvciBtdWx0aS1jbHVzdGVyIGlkZW50aWZpY2F0aW9uIGFuZCBUaGFub3NcbiAgICBleHRlcm5hbExhYmVsczpcbiAgICAgIGNsdXN0ZXI6IGt1cDZzXG4gICAgICByZXBsaWNhOiBcIiQoUE9EX05BTUUpXCJcblxuICAgICMgVGhhbm9zIHNpZGVjYXIgZm9yIGxvbmctdGVybSBtZXRyaWNzIHN0b3JhZ2UgaW4gUzNcbiAgICAjIE5PVEU6IGNoYXJ0ID49ODUueCByZXF1aXJlcyB0aGUgZXhpc3RpbmdTZWNyZXQgc2hhcGUuIFRoZSBsZWdhY3kgZmxhdFxuICAgICMge25hbWUsIGtleX0gZm9ybSBpcyBzaWxlbnRseSBkcm9wcGVkIGJ5IHRoZSBjaGFydCwgd2hpY2ggcmVtb3ZlcyB0aGVcbiAgICAjIHNpZGVjYXIgYW5kIHN0b3BzIGFsbCBTMyBibG9jayB1cGxvYWRzIChyZWdyZXNzaW9uIHNlZW4gMjAyNi0wMy0zMSkuXG4gICAgdGhhbm9zOlxuICAgICAgb2JqZWN0U3RvcmFnZUNvbmZpZzpcbiAgICAgICAgZXhpc3RpbmdTZWNyZXQ6XG4gICAgICAgICAgbmFtZTogdGhhbm9zLW9ianN0b3JlLWNvbmZpZyAgIyBFU08tbWFuYWdlZCBzZWNyZXRcbiAgICAgICAgICBrZXk6IG9ianN0b3JlLnltbFxuXG4gICAgIyBTaWRlY2FyIGNvbnRhaW5lcnMgcmVzb3VyY2UgcmVxdWVzdHMgKGFkZGVkIDIwMjUtMTEtMTgpXG4gICAgIyBOT1RFOiB0aGFub3Mtc2lkZWNhciBpcyBOT1QgbGlzdGVkIGhlcmUgLSBpdCdzIGF1dG9tYXRpY2FsbHkgaW5qZWN0ZWQgYnkgUHJvbWV0aGV1cyBPcGVyYXRvclxuICAgICMgd2hlbiB0aGFub3Mub2JqZWN0U3RvcmFnZUNvbmZpZyBpcyBzZXQuIFNwZWNpZnlpbmcgaXQgaW4gY29udGFpbmVyc1tdIHdvdWxkIG92ZXJyaWRlXG4gICAgIyB0aGUgb3BlcmF0b3IncyBkZWZpbml0aW9uIGFuZCByZXF1aXJlIHVzIHRvIG1hbnVhbGx5IHNwZWNpZnkgdGhlIGltYWdlLCB3aGljaCBicmVha3NcbiAgICAjIHdoZW4gdGhlIG9wZXJhdG9yIHVwZGF0ZXMgdGhlIHRoYW5vcyB2ZXJzaW9uLlxuICAgIGNvbnRhaW5lcnM6XG4gICAgLSBuYW1lOiBjb25maWctcmVsb2FkZXJcbiAgICAgIHJlc291cmNlczpcbiAgICAgICAgcmVxdWVzdHM6XG4gICAgICAgICAgY3B1OiAke2NvbmZpZy5yZXNvdXJjZXMuY29uZmlnUmVsb2FkZXIucmVxdWVzdHMuY3B1fVxuICAgICAgICAgIG1lbW9yeTogJHtjb25maWcucmVzb3VyY2VzLmNvbmZpZ1JlbG9hZGVyLnJlcXVlc3RzLm1lbW9yeX1cbiAgICAgICAgbGltaXRzOlxuICAgICAgICAgIGNwdTogJHtjb25maWcucmVzb3VyY2VzLmNvbmZpZ1JlbG9hZGVyLmxpbWl0cy5jcHV9XG4gICAgICAgICAgbWVtb3J5OiAke2NvbmZpZy5yZXNvdXJjZXMuY29uZmlnUmVsb2FkZXIubGltaXRzLm1lbW9yeX1cblxuICAgIHNlcnZpY2U6XG4gICAgICBzZXNzaW9uQWZmaW5pdHk6IFwiQ2xpZW50SVBcIlxuXG5cbmdyYWZhbmE6XG4gICMgUmVjcmVhdGUgc3RyYXRlZ3kgaXMgcmVxdWlyZWQgYmVjYXVzZSB0aGUgc3RvcmFnZSBQVkMgaXMgUldPOlxuICAjIGEgUm9sbGluZ1VwZGF0ZSB3b3VsZCBzdXJnZSBhIHNlY29uZCBwb2QgdGhhdCBjYW5ub3QgbW91bnQgdGhlXG4gICMgdm9sdW1lIChGYWlsZWRBdHRhY2hWb2x1bWUpLCBibG9ja2luZyB0aGUgcm9sbG91dCBpbmRlZmluaXRlbHkuXG4gIGRlcGxveW1lbnRTdHJhdGVneTpcbiAgICB0eXBlOiBSZWNyZWF0ZVxuXG4gICMgVXNlIGV4aXN0aW5nIHNlY3JldCBmb3IgYWRtaW4gY3JlZGVudGlhbHMgKG1hbmFnZWQgYnkgRVNPKVxuICAjIFRoaXMgcHJldmVudHMgcGFzc3dvcmQgcmVnZW5lcmF0aW9uIG9uIEhlbG0gdXBncmFkZXNcbiAgYWRtaW46XG4gICAgZXhpc3RpbmdTZWNyZXQ6IFwiZ3JhZmFuYS1hZG1pbi1jcmVkZW50aWFsc1wiXG4gICAgdXNlcktleTogXCJhZG1pbi11c2VyXCJcbiAgICBwYXNzd29yZEtleTogXCJhZG1pbi1wYXNzd29yZFwiXG5cbiAgIyBEaXNhYmxlIGRlZmF1bHQgUHJvbWV0aGV1cyBkYXRhc291cmNlICh3ZSBwcm92aWRlIG91ciBvd24gcG9pbnRpbmcgdG8gVGhhbm9zIFF1ZXJ5KVxuICBkZWZhdWx0RGF0YXNvdXJjZUVuYWJsZWQ6IGZhbHNlXG5cbiAgIyBTaWRlY2FyIGNvbnRhaW5lcnMgZm9yIGRhc2hib2FyZC9kYXRhc291cmNlIHByb3Zpc2lvbmluZ1xuICBzaWRlY2FyOlxuICAgIHJlc291cmNlczpcbiAgICAgIHJlcXVlc3RzOlxuICAgICAgICBjcHU6IDEwbVxuICAgICAgICBtZW1vcnk6IDI1Nk1pXG4gICAgICBsaW1pdHM6XG4gICAgICAgIGNwdTogMTAwbVxuICAgICAgICBtZW1vcnk6IDUxMk1pXG4gICAgZGFzaGJvYXJkczpcbiAgICAgIGVuYWJsZWQ6IHRydWVcbiAgICBkYXRhc291cmNlczpcbiAgICAgIGVuYWJsZWQ6IGZhbHNlICAjIFdlIG1hbnVhbGx5IHByb3Zpc2lvbiBkYXRhc291cmNlcyAoUHJvbWV0aGV1c+KGklRoYW5vcywgTG9raSlcblxuICBpbmdyZXNzOlxuICAgIGVuYWJsZWQ6IHRydWVcbiAgICBob3N0czpcbiAgICAgIC0gJHtjb25maWcuZG9tYWlucy5ncmFmYW5hfVxuICAgIGFubm90YXRpb25zOlxuICAgICAgY2VydC1tYW5hZ2VyLmlvL2NsdXN0ZXItaXNzdWVyOiBcImxldHNlbmNyeXB0LWNsdXN0ZXItaXNzdWVyXCJcbiAgICAgIHRyYWVmaWsuaW5ncmVzcy5rdWJlcm5ldGVzLmlvL3JvdXRlci5lbnRyeXBvaW50czogd2Vic2VjdXJlXG4gICAgdGxzOlxuICAgICAgLSBzZWNyZXROYW1lOiBncmFmYW5hLWdlbmVyYWwtdGxzXG4gICAgICAgIGhvc3RzOlxuICAgICAgICAtICR7Y29uZmlnLmRvbWFpbnMuZ3JhZmFuYX1cblxuICByZXNvdXJjZXM6XG4gICAgbGltaXRzOlxuICAgICAgY3B1OiAke2NvbmZpZy5yZXNvdXJjZXMuZ3JhZmFuYS5saW1pdHMuY3B1fVxuICAgICAgbWVtb3J5OiAke2NvbmZpZy5yZXNvdXJjZXMuZ3JhZmFuYS5saW1pdHMubWVtb3J5fVxuICAgIHJlcXVlc3RzOlxuICAgICAgY3B1OiAke2NvbmZpZy5yZXNvdXJjZXMuZ3JhZmFuYS5yZXF1ZXN0cy5jcHV9XG4gICAgICBtZW1vcnk6ICR7Y29uZmlnLnJlc291cmNlcy5ncmFmYW5hLnJlcXVlc3RzLm1lbW9yeX1cblxuICByZWFkaW5lc3NQcm9iZTpcbiAgICBodHRwR2V0OlxuICAgICAgcGF0aDogL2FwaS9oZWFsdGhcbiAgICAgIHBvcnQ6IDMwMDBcbiAgICBpbml0aWFsRGVsYXlTZWNvbmRzOiAzMFxuICAgIHBlcmlvZFNlY29uZHM6IDEwXG4gICAgdGltZW91dFNlY29uZHM6IDVcbiAgICBmYWlsdXJlVGhyZXNob2xkOiAzXG5cbiAgbGl2ZW5lc3NQcm9iZTpcbiAgICBodHRwR2V0OlxuICAgICAgcGF0aDogL2FwaS9oZWFsdGhcbiAgICAgIHBvcnQ6IDMwMDBcbiAgICBpbml0aWFsRGVsYXlTZWNvbmRzOiA2MFxuICAgIHBlcmlvZFNlY29uZHM6IDEwXG4gICAgdGltZW91dFNlY29uZHM6IDMwXG4gICAgZmFpbHVyZVRocmVzaG9sZDogMTBcblxuICBwZXJzaXN0ZW5jZTpcbiAgICBlbmFibGVkOiB0cnVlXG4gICAgc3RvcmFnZUNsYXNzTmFtZTogbG9uZ2hvcm5cbiAgICBhY2Nlc3NNb2RlczpcbiAgICAgIC0gUmVhZFdyaXRlT25jZVxuICAgIHNpemU6ICR7Y29uZmlnLnN0b3JhZ2UuZ3JhZmFuYX1cblxuICAjIE1hbnVhbCBkYXRhc291cmNlIHByb3Zpc2lvbmluZyAoc2lkZWNhciBkaXNhYmxlZClcbiAgIyBUaGFub3MgUXVlcnkg4oaSIGxvbmctdGVybS9oaXN0b3JpY2FsIG1ldHJpY3MgKFMzIHZpYSBTdG9yZSArIGxpdmUgc2lkZWNhciksIGRlZmF1bHRcbiAgIyBQcm9tZXRoZXVzIOKGkiBsb2NhbCwgcmVjZW50L2hpZ2gtcmVzIG9ubHkgKGtlcHQgZm9yIGxvdy1sYXRlbmN5IHJlY2VudCBxdWVyaWVzKVxuICAjIExva2kgZm9yIGxvZyBhZ2dyZWdhdGlvblxuICBkYXRhc291cmNlczpcbiAgICBkYXRhc291cmNlcy55YW1sOlxuICAgICAgYXBpVmVyc2lvbjogMVxuICAgICAgIyBEZWxldGUgYW55IHByZS1leGlzdGluZyBMb2tpIGRhdGFzb3VyY2UgKGUuZy4gb25lIGNyZWF0ZWQgd2l0aCBhXG4gICAgICAjIHJhbmRvbWx5IGdlbmVyYXRlZCBVSUQgYmVmb3JlIHdlIHBpbm5lZCB1aWQ6IGxva2kpLiBXaXRob3V0IHRoaXMsXG4gICAgICAjIEdyYWZhbmEgZmFpbHMgcHJvdmlzaW9uaW5nIHdpdGggXCJkYXRhIHNvdXJjZSBub3QgZm91bmRcIiBiZWNhdXNlXG4gICAgICAjIHRoZSB1cHNlcnQgY29sbGlkZXMgd2l0aCB0aGUgZXhpc3RpbmcgZW50cnkuXG4gICAgICBkZWxldGVEYXRhc291cmNlczpcbiAgICAgIC0gbmFtZTogTG9raVxuICAgICAgICBvcmdJZDogMVxuICAgICAgZGF0YXNvdXJjZXM6XG4gICAgICAtIG5hbWU6IFRoYW5vc1xuICAgICAgICB0eXBlOiBwcm9tZXRoZXVzXG4gICAgICAgIHVpZDogdGhhbm9zXG4gICAgICAgIGFjY2VzczogcHJveHlcbiAgICAgICAgIyB0aGFub3MtcXVlcnkgU2VydmljZTogaHR0cCBwb3J0IDkwOTAgLT4gY29udGFpbmVyIGh0dHAgMTA5MDJcbiAgICAgICAgdXJsOiBodHRwOi8vdGhhbm9zLXF1ZXJ5LiR7Y29uZmlnLm5hbWVzcGFjZX0uc3ZjLmNsdXN0ZXIubG9jYWw6OTA5MFxuICAgICAgICBpc0RlZmF1bHQ6IHRydWVcbiAgICAgICAganNvbkRhdGE6XG4gICAgICAgICAgdGltZUludGVydmFsOiAzMHNcbiAgICAgIC0gbmFtZTogUHJvbWV0aGV1c1xuICAgICAgICB0eXBlOiBwcm9tZXRoZXVzXG4gICAgICAgIHVpZDogcHJvbWV0aGV1c1xuICAgICAgICBhY2Nlc3M6IHByb3h5XG4gICAgICAgIHVybDogaHR0cDovL2t1YmUtcHJvbWV0aGV1cy1zdGFjay1wcm9tZXRoZXVzLiR7Y29uZmlnLm5hbWVzcGFjZX0uc3ZjLmNsdXN0ZXIubG9jYWw6OTA5MFxuICAgICAgICBpc0RlZmF1bHQ6IGZhbHNlXG4gICAgICAgIGpzb25EYXRhOlxuICAgICAgICAgIHRpbWVJbnRlcnZhbDogMzBzXG4gICAgICAtIG5hbWU6IExva2lcbiAgICAgICAgdHlwZTogbG9raVxuICAgICAgICB1aWQ6IGxva2lcbiAgICAgICAgYWNjZXNzOiBwcm94eVxuICAgICAgICB1cmw6IGh0dHA6Ly9sb2tpLWdhdGV3YXkuJHtjb25maWcubmFtZXNwYWNlfS5zdmMuY2x1c3Rlci5sb2NhbFxuICAgICAgICBqc29uRGF0YTpcbiAgICAgICAgICBtYXhMaW5lczogMTAwMFxuXG5wcm9tZXRoZXVzT3BlcmF0b3I6XG4gIHJlc291cmNlczpcbiAgICBsaW1pdHM6XG4gICAgICBjcHU6IDFcbiAgICAgIG1lbW9yeTogNTEyTWlcbiAgICByZXF1ZXN0czpcbiAgICAgIGNwdTogNTBtXG4gICAgICBtZW1vcnk6IDEyOE1pXG4gIHByaW9yaXR5Q2xhc3NOYW1lOiBoaWdoLXByaW9yaXR5XG5cbnByb21ldGhldXMtbm9kZS1leHBvcnRlcjpcbiAgIyBCaW5kIDo5MTAwIHRvIHRoZSBub2RlIEludGVybmFsSVAgb25seSwgbm90IDAuMC4wLjAuIFdpdGgga3ViZVJCQUNQcm94eVxuICAjIGRpc2FibGVkIHRoZSBzdWItY2hhcnQgc2V0cyBIT1NUX0lQIGZyb20gZmllbGRSZWYgc3RhdHVzLmhvc3RJUCwgd2hpY2ggaXNcbiAgIyB0aGUgbm9kZSdzIEludGVybmFsSVAgKEVYOiB2U3dpdGNoIDEwLjYuMC54IHZpYSAtLW5vZGUtaXA7IGNsb3VkOiBoY2xvdWRcbiAgIyBwcml2YXRlIElQKS4gbm9kZS1leHBvcnRlciBpcyBob3N0TmV0d29yaywgc28gUHJvbWV0aGV1cyBhbHJlYWR5IHNjcmFwZXNcbiAgIyB0aGF0IEludGVybmFsSVAgZW5kcG9pbnQgLT4gc2NyYXBpbmcgaXMgdW5hZmZlY3RlZCwgb25seSB0aGUgcHVibGljLU5JQ1xuICAjIGV4cG9zdXJlIG9mIHRoZSB1bmF1dGhlbnRpY2F0ZWQgbWV0cmljcyBvbiB0aGUgRVggcHVibGljIElQcyBnb2VzIGF3YXkuXG4gIHNlcnZpY2U6XG4gICAgbGlzdGVuT25BbGxJbnRlcmZhY2VzOiBmYWxzZVxuICByZXNvdXJjZXM6XG4gICAgbGltaXRzOlxuICAgICAgY3B1OiA1MG1cbiAgICAgIG1lbW9yeTogNTBNaVxuICAgIHJlcXVlc3RzOlxuICAgICAgY3B1OiA1bVxuICAgICAgbWVtb3J5OiAxNk1pXG4gIHByaW9yaXR5Q2xhc3NOYW1lOiBoaWdoLXByaW9yaXR5XG5cbmt1YmUtc3RhdGUtbWV0cmljczpcbiAgcmVzb3VyY2VzOlxuICAgIGxpbWl0czpcbiAgICAgIGNwdTogMVxuICAgICAgbWVtb3J5OiA1MTJNaVxuICAgIHJlcXVlc3RzOlxuICAgICAgY3B1OiA1bVxuICAgICAgbWVtb3J5OiAxMjhNaVxuICBwcmlvcml0eUNsYXNzTmFtZTogaGlnaC1wcmlvcml0eVxuYDtcbiAgfVxufVxuIl19
@@ -0,0 +1,35 @@
1
+ import { Construct } from 'constructs';
2
+ import { MonitoringConfig } from '../types';
3
+ export interface ThanosCompactorProps {
4
+ namespace: string;
5
+ config: MonitoringConfig;
6
+ }
7
+ /**
8
+ * Creates Thanos Compactor StatefulSet and Service.
9
+ *
10
+ * Thanos Compactor performs:
11
+ * - Downsampling: Creates 5-minute and 1-hour resolution data from raw blocks
12
+ * - Compaction: Merges small blocks into larger ones for better query performance
13
+ * - Retention enforcement: Deletes blocks older than configured retention
14
+ *
15
+ * Retention Strategy:
16
+ * - Raw (15s resolution): 30 days
17
+ * - 5-minute resolution: 180 days (6 months)
18
+ * - 1-hour resolution: 730 days (2 years)
19
+ *
20
+ * Components:
21
+ * - StatefulSet (1 replica - singleton, 20Gi PVC for working directory)
22
+ * - HTTP Service (for metrics and debugging)
23
+ *
24
+ * Prerequisites:
25
+ * - Namespace exists
26
+ * - PriorityClass 'high-priority' exists
27
+ * - Longhorn storage class available
28
+ * - Thanos S3 secret exists (thanos-objstore-config)
29
+ * - S3 bucket contains Prometheus data blocks
30
+ *
31
+ * Sync Wave: 3 (compaction - can run alongside Store Gateway)
32
+ */
33
+ export declare class ThanosCompactorConstruct extends Construct {
34
+ constructor(scope: Construct, id: string, props: ThanosCompactorProps);
35
+ }