From 5be4a5bd6148901b2d0d66cc2fdafb475d80746e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jan 2025 00:03:58 +0000 Subject: [PATCH] Added chart versions: nats/nats: - 1.2.10 speedscale/speedscale-operator: - 2.3.149 --- assets/nats/nats-1.2.10.tgz | Bin 0 -> 20134 bytes assets/nats/nats-1.2.9.tgz | Bin 20137 -> 20129 bytes .../speedscale-operator-2.3.149.tgz | Bin 0 -> 17472 bytes charts/nats/nats/1.2.10/.helmignore | 26 + charts/nats/nats/1.2.10/Chart.yaml | 23 + charts/nats/nats/1.2.10/README.md | 329 +++++++++ charts/nats/nats/1.2.10/UPGRADING.md | 155 ++++ charts/nats/nats/1.2.10/app-readme.md | 3 + charts/nats/nats/1.2.10/files/config-map.yaml | 10 + .../nats/1.2.10/files/config/cluster.yaml | 32 + .../nats/nats/1.2.10/files/config/config.yaml | 114 +++ .../nats/1.2.10/files/config/gateway.yaml | 11 + .../nats/1.2.10/files/config/jetstream.yaml | 23 + .../nats/1.2.10/files/config/leafnodes.yaml | 11 + .../nats/nats/1.2.10/files/config/mqtt.yaml | 10 + .../nats/1.2.10/files/config/protocol.yaml | 10 + .../nats/1.2.10/files/config/resolver.yaml | 3 + charts/nats/nats/1.2.10/files/config/tls.yaml | 16 + .../nats/1.2.10/files/config/websocket.yaml | 12 + .../nats/1.2.10/files/headless-service.yaml | 24 + charts/nats/nats/1.2.10/files/ingress.yaml | 34 + .../files/nats-box/contents-secret.yaml | 17 + .../nats-box/contexts-secret/context.yaml | 51 ++ .../contexts-secret/contexts-secret.yaml | 13 + .../files/nats-box/deployment/container.yaml | 46 ++ .../files/nats-box/deployment/deployment.yaml | 16 + .../nats-box/deployment/pod-template.yaml | 44 ++ .../files/nats-box/service-account.yaml | 7 + .../1.2.10/files/pod-disruption-budget.yaml | 12 + .../nats/nats/1.2.10/files/pod-monitor.yaml | 13 + .../nats/1.2.10/files/service-account.yaml | 7 + charts/nats/nats/1.2.10/files/service.yaml | 23 + .../files/stateful-set/jetstream-pvc.yaml | 13 + .../files/stateful-set/nats-container.yaml | 106 +++ .../files/stateful-set/pod-template.yaml | 71 ++ .../stateful-set/prom-exporter-container.yaml | 30 + .../stateful-set/reloader-container.yaml | 27 + .../files/stateful-set/resolver-pvc.yaml | 13 + .../files/stateful-set/stateful-set.yaml | 37 + charts/nats/nats/1.2.10/questions.yaml | 12 + .../nats/nats/1.2.10/templates/_helpers.tpl | 281 ++++++++ .../nats/nats/1.2.10/templates/_jsonpatch.tpl | 219 ++++++ .../1.2.10/templates/_toPrettyRawJson.tpl | 28 + .../nats/nats/1.2.10/templates/_tplYaml.tpl | 114 +++ .../nats/1.2.10/templates/config-map.yaml | 4 + .../1.2.10/templates/extra-resources.yaml | 5 + .../1.2.10/templates/headless-service.yaml | 4 + .../nats/nats/1.2.10/templates/ingress.yaml | 6 + .../templates/nats-box/contents-secret.yaml | 10 + .../templates/nats-box/contexts-secret.yaml | 8 + .../1.2.10/templates/nats-box/deployment.yaml | 8 + .../templates/nats-box/service-account.yaml | 8 + .../templates/pod-disruption-budget.yaml | 6 + .../nats/1.2.10/templates/pod-monitor.yaml | 8 + .../1.2.10/templates/service-account.yaml | 6 + .../nats/nats/1.2.10/templates/service.yaml | 6 + .../nats/1.2.10/templates/stateful-set.yaml | 4 + .../1.2.10/templates/tests/request-reply.yaml | 37 + charts/nats/nats/1.2.10/values.yaml | 669 ++++++++++++++++++ charts/nats/nats/1.2.9/Chart.yaml | 1 - .../speedscale-operator/2.3.149/.helmignore | 23 + .../speedscale-operator/2.3.149/Chart.yaml | 27 + .../speedscale-operator/2.3.149/LICENSE | 201 ++++++ .../speedscale-operator/2.3.149/README.md | 111 +++ .../speedscale-operator/2.3.149/app-readme.md | 111 +++ .../2.3.149/questions.yaml | 9 + .../2.3.149/templates/NOTES.txt | 12 + .../2.3.149/templates/admission.yaml | 209 ++++++ .../2.3.149/templates/configmap.yaml | 43 ++ .../2.3.149/templates/crds/agenttasks.yaml | 161 +++++ .../templates/crds/trafficreplays.yaml | 522 ++++++++++++++ .../2.3.149/templates/deployments.yaml | 132 ++++ .../2.3.149/templates/hooks.yaml | 79 +++ .../2.3.149/templates/rbac.yaml | 246 +++++++ .../2.3.149/templates/secrets.yaml | 18 + .../2.3.149/templates/services.yaml | 22 + .../2.3.149/templates/tls.yaml | 189 +++++ .../speedscale-operator/2.3.149/values.yaml | 138 ++++ index.yaml | 61 +- 79 files changed, 5147 insertions(+), 3 deletions(-) create mode 100644 assets/nats/nats-1.2.10.tgz create mode 100644 assets/speedscale/speedscale-operator-2.3.149.tgz create mode 100644 charts/nats/nats/1.2.10/.helmignore create mode 100644 charts/nats/nats/1.2.10/Chart.yaml create mode 100644 charts/nats/nats/1.2.10/README.md create mode 100644 charts/nats/nats/1.2.10/UPGRADING.md create mode 100644 charts/nats/nats/1.2.10/app-readme.md create mode 100644 charts/nats/nats/1.2.10/files/config-map.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/cluster.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/config.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/gateway.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/jetstream.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/leafnodes.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/mqtt.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/protocol.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/resolver.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/tls.yaml create mode 100644 charts/nats/nats/1.2.10/files/config/websocket.yaml create mode 100644 charts/nats/nats/1.2.10/files/headless-service.yaml create mode 100644 charts/nats/nats/1.2.10/files/ingress.yaml create mode 100644 charts/nats/nats/1.2.10/files/nats-box/contents-secret.yaml create mode 100644 charts/nats/nats/1.2.10/files/nats-box/contexts-secret/context.yaml create mode 100644 charts/nats/nats/1.2.10/files/nats-box/contexts-secret/contexts-secret.yaml create mode 100644 charts/nats/nats/1.2.10/files/nats-box/deployment/container.yaml create mode 100644 charts/nats/nats/1.2.10/files/nats-box/deployment/deployment.yaml create mode 100644 charts/nats/nats/1.2.10/files/nats-box/deployment/pod-template.yaml create mode 100644 charts/nats/nats/1.2.10/files/nats-box/service-account.yaml create mode 100644 charts/nats/nats/1.2.10/files/pod-disruption-budget.yaml create mode 100644 charts/nats/nats/1.2.10/files/pod-monitor.yaml create mode 100644 charts/nats/nats/1.2.10/files/service-account.yaml create mode 100644 charts/nats/nats/1.2.10/files/service.yaml create mode 100644 charts/nats/nats/1.2.10/files/stateful-set/jetstream-pvc.yaml create mode 100644 charts/nats/nats/1.2.10/files/stateful-set/nats-container.yaml create mode 100644 charts/nats/nats/1.2.10/files/stateful-set/pod-template.yaml create mode 100644 charts/nats/nats/1.2.10/files/stateful-set/prom-exporter-container.yaml create mode 100644 charts/nats/nats/1.2.10/files/stateful-set/reloader-container.yaml create mode 100644 charts/nats/nats/1.2.10/files/stateful-set/resolver-pvc.yaml create mode 100644 charts/nats/nats/1.2.10/files/stateful-set/stateful-set.yaml create mode 100644 charts/nats/nats/1.2.10/questions.yaml create mode 100644 charts/nats/nats/1.2.10/templates/_helpers.tpl create mode 100644 charts/nats/nats/1.2.10/templates/_jsonpatch.tpl create mode 100644 charts/nats/nats/1.2.10/templates/_toPrettyRawJson.tpl create mode 100644 charts/nats/nats/1.2.10/templates/_tplYaml.tpl create mode 100644 charts/nats/nats/1.2.10/templates/config-map.yaml create mode 100644 charts/nats/nats/1.2.10/templates/extra-resources.yaml create mode 100644 charts/nats/nats/1.2.10/templates/headless-service.yaml create mode 100644 charts/nats/nats/1.2.10/templates/ingress.yaml create mode 100644 charts/nats/nats/1.2.10/templates/nats-box/contents-secret.yaml create mode 100644 charts/nats/nats/1.2.10/templates/nats-box/contexts-secret.yaml create mode 100644 charts/nats/nats/1.2.10/templates/nats-box/deployment.yaml create mode 100644 charts/nats/nats/1.2.10/templates/nats-box/service-account.yaml create mode 100644 charts/nats/nats/1.2.10/templates/pod-disruption-budget.yaml create mode 100644 charts/nats/nats/1.2.10/templates/pod-monitor.yaml create mode 100644 charts/nats/nats/1.2.10/templates/service-account.yaml create mode 100644 charts/nats/nats/1.2.10/templates/service.yaml create mode 100644 charts/nats/nats/1.2.10/templates/stateful-set.yaml create mode 100644 charts/nats/nats/1.2.10/templates/tests/request-reply.yaml create mode 100644 charts/nats/nats/1.2.10/values.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/.helmignore create mode 100644 charts/speedscale/speedscale-operator/2.3.149/Chart.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/LICENSE create mode 100644 charts/speedscale/speedscale-operator/2.3.149/README.md create mode 100644 charts/speedscale/speedscale-operator/2.3.149/app-readme.md create mode 100644 charts/speedscale/speedscale-operator/2.3.149/questions.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/NOTES.txt create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/admission.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/configmap.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/crds/agenttasks.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/crds/trafficreplays.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/deployments.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/hooks.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/rbac.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/secrets.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/services.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/templates/tls.yaml create mode 100644 charts/speedscale/speedscale-operator/2.3.149/values.yaml diff --git a/assets/nats/nats-1.2.10.tgz b/assets/nats/nats-1.2.10.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e499ebfe1fe8d7299124586b0ebd7831e4a6700a GIT binary patch literal 20134 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 delta 16325 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 diff --git a/assets/speedscale/speedscale-operator-2.3.149.tgz b/assets/speedscale/speedscale-operator-2.3.149.tgz new file mode 100644 index 0000000000000000000000000000000000000000..b8ecc70f7db538bfb198896a6388a7db783bb9f2 GIT binary patch literal 17472 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 P00960s5_jT0CWKW9E?Wh literal 0 HcmV?d00001 diff --git a/charts/nats/nats/1.2.10/.helmignore b/charts/nats/nats/1.2.10/.helmignore new file mode 100644 index 000000000..240dfde2a --- /dev/null +++ b/charts/nats/nats/1.2.10/.helmignore @@ -0,0 +1,26 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + +# template tests +/test diff --git a/charts/nats/nats/1.2.10/Chart.yaml b/charts/nats/nats/1.2.10/Chart.yaml new file mode 100644 index 000000000..f807fb25e --- /dev/null +++ b/charts/nats/nats/1.2.10/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: NATS Server + catalog.cattle.io/featured: "5" + catalog.cattle.io/kube-version: '>=1.16-0' + catalog.cattle.io/release-name: nats +apiVersion: v2 +appVersion: 2.10.25 +description: A Helm chart for the NATS.io High Speed Cloud Native Distributed Communications + Technology. +home: http://github.com/nats-io/k8s +icon: file://assets/icons/nats.png +keywords: +- nats +- messaging +- cncf +kubeVersion: '>=1.16-0' +maintainers: +- email: info@nats.io + name: The NATS Authors + url: https://github.com/nats-io +name: nats +version: 1.2.10 diff --git a/charts/nats/nats/1.2.10/README.md b/charts/nats/nats/1.2.10/README.md new file mode 100644 index 000000000..0916999df --- /dev/null +++ b/charts/nats/nats/1.2.10/README.md @@ -0,0 +1,329 @@ +# NATS Server + +--- + +[NATS](https://nats.io) is a simple, secure and performant communications system for digital systems, services and devices. +NATS is part of the Cloud Native Computing Foundation ([CNCF](https://cncf.io)). +NATS has over [30 client language implementations](https://nats.io/download/), and its server can run on-premise, in the cloud, at the edge, and even on a Raspberry Pi. +NATS can secure and simplify design and operation of modern distributed systems. + +```shell +helm repo add nats https://nats-io.github.io/k8s/helm/charts/ +helm upgrade --install nats nats/nats +``` + +## Upgrade Nodes + +- **Upgrading from 0.x**: The `values.yaml` schema changed significantly from 0.x to 1.x. Read [UPGRADING.md](UPGRADING.md) for instructions on upgrading a 0.x release to 1.x. + +## Values + +There are a handful of explicitly defined options which are documented with comments in the [values.yaml](values.yaml) file. + +Everything in the NATS Config or Kubernetes Resources can be overridden by `merge` and `patch`, which is supported for the following values: + +| key | type | enabled by default | +|----------------------------------|-----------------------------------------------------------------------------------------------------------------------------|-----------------------------------------| +| `config` | [NATS Config](https://docs.nats.io/running-a-nats-service/configuration) | yes | +| `config.cluster` | [NATS Cluster](https://docs.nats.io/running-a-nats-service/configuration/clustering/cluster_config) | no | +| `config.cluster.tls` | [NATS TLS](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls) | no | +| `config.jetstream` | [NATS JetStream](https://docs.nats.io/running-a-nats-service/configuration#jetstream) | no | +| `config.jetstream.fileStore.pvc` | [k8s PVC](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#persistentvolumeclaim-v1-core) | yes, when `config.jetstream` is enabled | +| `config.nats.tls` | [NATS TLS](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls) | no | +| `config.leafnodes` | [NATS LeafNodes](https://docs.nats.io/running-a-nats-service/configuration/leafnodes/leafnode_conf) | no | +| `config.leafnodes.tls` | [NATS TLS](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls) | no | +| `config.websocket` | [NATS WebSocket](https://docs.nats.io/running-a-nats-service/configuration/websocket/websocket_conf) | no | +| `config.websocket.tls` | [NATS TLS](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls) | no | +| `config.websocket.ingress` | [k8s Ingress](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#ingress-v1-networking-k8s-io) | no | +| `config.mqtt` | [NATS MQTT](https://docs.nats.io/running-a-nats-service/configuration/mqtt/mqtt_config) | no | +| `config.mqtt.tls` | [NATS TLS](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls) | no | +| `config.gateway` | [NATS Gateway](https://docs.nats.io/running-a-nats-service/configuration/gateways/gateway#gateway-configuration-block) | no | +| `config.gateway.tls` | [NATS TLS](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls) | no | +| `config.resolver` | [NATS Resolver](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/jwt/resolver) | no | +| `config.resolver.pvc` | [k8s PVC](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#persistentvolumeclaim-v1-core) | yes, when `config.resolver` is enabled | +| `container` | nats [k8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#container-v1-core) | yes | +| `reloader` | config reloader [k8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#container-v1-core) | yes | +| `promExporter` | prometheus exporter [k8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#container-v1-core) | no | +| `promExporter.podMonitor` | [prometheus PodMonitor](https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.PodMonitor) | no | +| `service` | [k8s Service](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#service-v1-core) | yes | +| `statefulSet` | [k8s StatefulSet](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#statefulset-v1-apps) | yes | +| `podTemplate` | [k8s PodTemplate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#pod-v1-core) | yes | +| `headlessService` | [k8s Service](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#service-v1-core) | yes | +| `configMap` | [k8s ConfigMap](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#configmap-v1-core) | yes | +| `natsBox.contexts.default` | [NATS Context](https://docs.nats.io/using-nats/nats-tools/nats_cli#nats-contexts) | yes | +| `natsBox.contexts.[name]` | [NATS Context](https://docs.nats.io/using-nats/nats-tools/nats_cli#nats-contexts) | no | +| `natsBox.container` | nats-box [k8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#container-v1-core) | yes | +| `natsBox.deployment` | [k8s Deployment](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#deployment-v1-apps) | yes | +| `natsBox.podTemplate` | [k8s PodTemplate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#pod-v1-core) | yes | +| `natsBox.contextsSecret` | [k8s Secret](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#secret-v1-core) | yes | +| `natsBox.contentsSecret` | [k8s Secret](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#secret-v1-core) | yes | + +### Merge + +Merging is performed using the Helm `merge` function. Example - add NATS accounts and container resources: + +```yaml +config: + merge: + accounts: + A: + users: + - {user: a, password: a} + B: + users: + - {user: b, password: b} +natsBox: + contexts: + a: + merge: {user: a, password: a} + b: + merge: {user: b, password: b} + defaultContextName: a +``` + +## Patch + +Patching is performed using [JSON Patch](https://jsonpatch.com/). Example - add additional route to end of route list: + +```yaml +config: + cluster: + enabled: true + patch: + - op: add + path: /routes/- + value: nats://demo.nats.io:6222 +``` + +## Common Configurations + +### JetStream Cluster on 3 separate hosts + +```yaml +config: + cluster: + enabled: true + replicas: 3 + jetstream: + enabled: true + fileStore: + pvc: + size: 10Gi + +podTemplate: + topologySpreadConstraints: + kubernetes.io/hostname: + maxSkew: 1 + whenUnsatisfiable: DoNotSchedule +``` + +### NATS Container Resources + +```yaml +container: + env: + # different from k8s units, suffix must be B, KiB, MiB, GiB, or TiB + # should be ~90% of memory limit + GOMEMLIMIT: 7GiB + merge: + # recommended limit is at least 2 CPU cores and 8Gi Memory for production JetStream clusters + resources: + requests: + cpu: "2" + memory: 8Gi + limits: + cpu: "2" + memory: 8Gi +``` + +### Specify Image Version + +```yaml +container: + image: + tag: x.y.z-alpine +``` + +### Operator Mode with NATS Resolver + +Run `nsc generate config --nats-resolver` and replace the `OPERATOR_JWT`, `SYS_ACCOUNT_ID`, and `SYS_ACCOUNT_JWT` with your values. +Make sure that you do not include the trailing `,` in the `SYS_ACCOUNT_JWT`. + +``` +config: + resolver: + enabled: true + merge: + type: full + interval: 2m + timeout: 1.9s + merge: + operator: OPERATOR_JWT + system_account: SYS_ACCOUNT_ID + resolver_preload: + SYS_ACCOUNT_ID: SYS_ACCOUNT_JWT +``` + + +## Accessing NATS + +The chart contains 2 services by default, `service` and `headlessService`. + +### `service` + +The `service` is intended to be accessed by NATS Clients. It is a `ClusterIP` service by default, however it can easily be changed to a different service type. + +The `nats`, `websocket`, `leafnodes`, and `mqtt` ports will be exposed through this service by default if they are enabled. + +Example: change this service type to a `LoadBalancer`: + +```yaml +service: + merge: + spec: + type: LoadBalancer +``` + +### `headlessService` + +The `headlessService` is used for NATS Servers in the Stateful Set to discover one another. It is primarily intended to be used for Cluster Route connections. + +### TLS Considerations + +The TLS Certificate used for Client Connections should have a SAN covering DNS Name that clients access the `service` at. + +The TLS Certificate used for Cluster Route Connections should have a SAN covering the DNS Name that routes access each other on the `headlessService` at. This is `*.` by default. + +## Advanced Features + +### Templating Values + +Anything in `values.yaml` can be templated: + +- maps matching the following syntax will be templated and parsed as YAML: + ```yaml + $tplYaml: | + yaml template + ``` +- maps matching the follow syntax will be templated, parsed as YAML, and spread into the parent map/slice + ```yaml + $tplYamlSpread: | + yaml template + ``` + +Example - change service name: + +```yaml +service: + name: + $tplYaml: >- + {{ include "nats.fullname" . }}-svc +``` + +### NATS Config Units and Variables + +NATS configuration extends JSON, and can represent Units and Variables. They must be wrapped in `<< >>` in order to template correctly. Example: + +```yaml +config: + merge: + authorization: + # variable + token: << $TOKEN >> + # units + max_payload: << 2MB >> +``` + +templates to the `nats.conf`: + +``` +{ + "authorization": { + "token": $TOKEN + }, + "max_payload": 2MB, + "port": 4222, + ... +} +``` + +### NATS Config Includes + +Any NATS Config key ending in `$include` will be replaced with an include directive. Included files should be in paths relative to `/etc/nats-config`. Multiple `$include` keys are supported by using a prefix, and will be sorted alphabetically. Example: + +```yaml +config: + merge: + 00$include: auth.conf + 01$include: params.conf +configMap: + merge: + data: + auth.conf: | + accounts: { + A: { + users: [ + {user: a, password: a} + ] + }, + B: { + users: [ + {user: b, password: b} + ] + }, + } + params.conf: | + max_payload: 2MB +``` + +templates to the `nats.conf`: + +``` +include auth.conf; +"port": 4222, +... +include params.conf; +``` + +### Extra Resources + +Enables adding additional arbitrary resources. Example - expose WebSocket via VirtualService in Istio: + +```yaml +config: + websocket: + enabled: true +extraResources: +- apiVersion: networking.istio.io/v1beta1 + kind: VirtualService + metadata: + namespace: + $tplYamlSpread: > + {{ include "nats.metadataNamespace" $ }} + name: + $tplYaml: > + {{ include "nats.fullname" $ | quote }} + labels: + $tplYaml: | + {{ include "nats.labels" $ }} + spec: + hosts: + - demo.nats.io + gateways: + - my-gateway + http: + - name: default + match: + - name: root + uri: + exact: / + route: + - destination: + host: + $tplYaml: > + {{ .Values.service.name | quote }} + port: + number: + $tplYaml: > + {{ .Values.config.websocket.port }} +``` diff --git a/charts/nats/nats/1.2.10/UPGRADING.md b/charts/nats/nats/1.2.10/UPGRADING.md new file mode 100644 index 000000000..9cc177991 --- /dev/null +++ b/charts/nats/nats/1.2.10/UPGRADING.md @@ -0,0 +1,155 @@ +# Upgrading from 0.x to 1.x + +Instructions for upgrading an existing `nats` 0.x release to 1.x. + +## Rename Immutable Fields + +There are a number of immutable fields in the NATS Stateful Set and NATS Box deployment. All 1.x `values.yaml` files targeting an existing 0.x release will require some or all of these settings: + +```yaml +config: + # required if using JetStream file storage + jetstream: + # uncomment the next line if using JetStream file storage + # enabled: true + fileStore: + pvc: + name: + $tplYaml: >- + {{ include "nats.fullname" . }}-js-pvc + # set other PVC options here to make it match 0.x, refer to values.yaml for schema + + # required if using a full or cache resolver + resolver: + # uncomment the next line if using a full or cache resolver + # enabled: true + pvc: + name: nats-jwt-pvc + # set other PVC options here to make it match 0.x, refer to values.yaml for schema + +# required +statefulSet: + patch: + - op: remove + path: /spec/selector/matchLabels/app.kubernetes.io~1component + - $tplYamlSpread: |- + {{- if and + .Values.config.jetstream.enabled + .Values.config.jetstream.fileStore.enabled + .Values.config.jetstream.fileStore.pvc.enabled + .Values.config.resolver.enabled + .Values.config.resolver.pvc.enabled + }} + - op: move + from: /spec/volumeClaimTemplates/0 + path: /spec/volumeClaimTemplates/1 + {{- else}} + [] + {{- end }} + +# required +headlessService: + name: + $tplYaml: >- + {{ include "nats.fullname" . }} + +# required unless 0.x values explicitly set nats.serviceAccount.create=false +serviceAccount: + enabled: true + +# required to use new ClusterIP service for Clients accessing NATS +# if using TLS, this may require adding another SAN +service: + # uncomment the next line to disable the new ClusterIP service + # enabled: false + name: + $tplYaml: >- + {{ include "nats.fullname" . }}-svc + +# required if using NatsBox +natsBox: + deployment: + patch: + - op: replace + path: /spec/selector/matchLabels + value: + app: nats-box + - op: add + path: /spec/template/metadata/labels/app + value: nats-box +``` + +## Update NATS Config to new values.yaml schema + +Most values that control the NATS Config have changed and moved under the `config` key. Refer to the 1.x Chart's [values.yaml](values.yaml) for the complete schema. + +After migrating to the new values schema, ensure that changes you expect in the NATS Config files match by templating the old and new config files. + +Template your old 0.x Config Map, this example uses a file called `values-old.yaml`: + +```sh +helm template \ + --version "0.x" \ + -f values-old.yaml \ + -s templates/configmap.yaml \ + nats \ + nats/nats +``` + +Template your new 1.x Config Map, this example uses a file called `values.yaml`: + +```sh +helm template \ + --version "^1-beta" \ + -f values.yaml \ + -s templates/config-map.yaml \ + nats \ + nats/nats +``` + +## Update Kubernetes Resources to new values.yaml schema + +Most values that control Kubernetes Resources have been changed. Refer to the 1.x Chart's [values.yaml](values.yaml) for the complete schema. + +After migrating to the new values schema, ensure that changes you expect in resources match by templating the old and new resources. + +| Resource | 0.x Template File | 1.x Template File | +|-------------------------|---------------------------------|-------------------------------------------| +| Config Map | `templates/configmap.yaml` | `templates/config-map.yaml` | +| Stateful Set | `templates/statefulset.yaml` | `templates/stateful-set.yaml` | +| Headless Service | `templates/service.yaml` | `templates/headless-service.yaml` | +| ClusterIP Service | N/A | `templates/service.yaml` | +| Network Policy | `templates/networkpolicy.yaml` | N/A | +| Pod Disruption Budget | `templates/pdb.yaml` | `templates/pod-disruption-budget.yaml` | +| Service Account | `templates/rbac.yaml` | `templates/service-account.yaml` | +| Resource | `templates/` | `templates/` | +| Resource | `templates/` | `templates/` | +| Prometheus Monitor | `templates/serviceMonitor.yaml` | `templates/pod-monitor.yaml` | +| NatsBox Deployment | `templates/nats-box.yaml` | `templates/nats-box/deployment.yaml` | +| NatsBox Service Account | N/A | `templates/nats-box/service-account.yaml` | +| NatsBox Contents Secret | N/A | `templates/nats-box/contents-secret.yaml` | +| NatsBox Contexts Secret | N/A | `templates/nats-box/contexts-secret.yaml` | + +For example, to check that the Stateful Set matches: + +Template your old 0.x Stateful Set, this example uses a file called `values-old.yaml`: + +```sh +helm template \ + --version "0.x" \ + -f values-old.yaml \ + -s templates/statefulset.yaml \ + nats \ + nats/nats +``` + +Template your new 1.x Stateful Set, this example uses a file called `values.yaml`: + +```sh +helm template \ + --version "^1-beta" \ + -f values.yaml \ + -s templates/stateful-set.yaml \ + nats \ + nats/nats +``` diff --git a/charts/nats/nats/1.2.10/app-readme.md b/charts/nats/nats/1.2.10/app-readme.md new file mode 100644 index 000000000..b4511f4d5 --- /dev/null +++ b/charts/nats/nats/1.2.10/app-readme.md @@ -0,0 +1,3 @@ +# NATS Server + + [NATS](https://nats.io) is a simple, secure and performant communications system for digital systems, services and devices. NATS is part of the Cloud Native Computing Foundation ([CNCF](https://cncf.io)). NATS has over [30 client language implementations](https://nats.io/download/), and its server can run on-premise, in the cloud, at the edge, and even on a Raspberry Pi. NATS can secure and simplify design and operation of modern distributed systems. diff --git a/charts/nats/nats/1.2.10/files/config-map.yaml b/charts/nats/nats/1.2.10/files/config-map.yaml new file mode 100644 index 000000000..89ee3c281 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config-map.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.configMap.name }} + labels: + {{- include "nats.labels" $ | nindent 4 }} +data: + nats.conf: | + {{- include "nats.formatConfig" .config | nindent 4 }} diff --git a/charts/nats/nats/1.2.10/files/config/cluster.yaml b/charts/nats/nats/1.2.10/files/config/cluster.yaml new file mode 100644 index 000000000..719cb8ade --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/cluster.yaml @@ -0,0 +1,32 @@ +{{- with .Values.config.cluster }} +name: {{ $.Values.statefulSet.name }} +port: {{ .port }} +no_advertise: true +routes: +{{- $proto := ternary "tls" "nats" .tls.enabled }} +{{- $auth := "" }} +{{- if and .routeURLs.user .routeURLs.password }} + {{- $auth = printf "%s:%s@" (urlquery .routeURLs.user) (urlquery .routeURLs.password) -}} +{{- end }} +{{- $domain := $.Values.headlessService.name }} +{{- if .routeURLs.useFQDN }} + {{- $domain = printf "%s.%s.svc.%s" $domain (include "nats.namespace" $) .routeURLs.k8sClusterDomain }} +{{- end }} +{{- $port := (int .port) }} +{{- range $i, $_ := until (int .replicas) }} +- {{ printf "%s://%s%s-%d.%s:%d" $proto $auth $.Values.statefulSet.name $i $domain $port }} +{{- end }} + +{{- if and .routeURLs.user .routeURLs.password }} +authorization: + user: {{ .routeURLs.user | quote }} + password: {{ .routeURLs.password | quote }} +{{- end }} + +{{- with .tls }} +{{- if .enabled }} +tls: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/tls.yaml" "ctx" (merge (dict "tls" .) $)) .) | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/config.yaml b/charts/nats/nats/1.2.10/files/config/config.yaml new file mode 100644 index 000000000..92fd96f1a --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/config.yaml @@ -0,0 +1,114 @@ +{{- with .Values.config }} + +server_name: << $SERVER_NAME >> +lame_duck_grace_period: 10s +lame_duck_duration: 30s +pid_file: /var/run/nats/nats.pid + +######################################## +# NATS +######################################## +{{- with .nats }} +port: {{ .port }} + +{{- with .tls }} +{{- if .enabled }} +tls: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/tls.yaml" "ctx" (merge (dict "tls" .) $)) .) | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} + +######################################## +# leafnodes +######################################## +{{- with .leafnodes }} +{{- if .enabled }} +leafnodes: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/leafnodes.yaml" "ctx" $) .) | nindent 2 }} +{{- end }} +{{- end }} + +######################################## +# websocket +######################################## +{{- with .websocket }} +{{- if .enabled }} +websocket: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/websocket.yaml" "ctx" $) .) | nindent 2 }} +{{- end }} +{{- end }} + +######################################## +# MQTT +######################################## +{{- with .mqtt }} +{{- if .enabled }} +mqtt: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/mqtt.yaml" "ctx" $) .) | nindent 2 }} +{{- end }} +{{- end }} + +######################################## +# cluster +######################################## +{{- with .cluster }} +{{- if .enabled }} +cluster: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/cluster.yaml" "ctx" $) .) | nindent 2 }} +{{- end }} +{{- end }} + +######################################## +# gateway +######################################## +{{- with .gateway }} +{{- if .enabled }} +gateway: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/gateway.yaml" "ctx" $) .) | nindent 2 }} +{{- end }} +{{- end }} + +######################################## +# monitor +######################################## +{{- with .monitor }} +{{- if .enabled }} +{{- if .tls.enabled }} +https_port: {{ .port }} +{{- else }} +http_port: {{ .port }} +{{- end }} +{{- end }} +{{- end }} + +######################################## +# profiling +######################################## +{{- with .profiling }} +{{- if .enabled }} +prof_port: {{ .port }} +{{- end }} +{{- end }} + +######################################## +# jetstream +######################################## +{{- with $.Values.config.jetstream -}} +{{- if .enabled }} +jetstream: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/jetstream.yaml" "ctx" $) .) | nindent 2 }} +{{- end }} +{{- end }} + +######################################## +# resolver +######################################## +{{- with $.Values.config.resolver -}} +{{- if .enabled }} +resolver: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/resolver.yaml" "ctx" $) .) | nindent 2 }} +{{- end }} +{{- end }} + +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/gateway.yaml b/charts/nats/nats/1.2.10/files/config/gateway.yaml new file mode 100644 index 000000000..32d4ed9f7 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/gateway.yaml @@ -0,0 +1,11 @@ +{{- with .Values.config.gateway }} +name: {{ $.Values.statefulSet.name }} +port: {{ .port }} + +{{- with .tls }} +{{- if .enabled }} +tls: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/tls.yaml" "ctx" (merge (dict "tls" .) $)) .) | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/jetstream.yaml b/charts/nats/nats/1.2.10/files/config/jetstream.yaml new file mode 100644 index 000000000..17262f643 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/jetstream.yaml @@ -0,0 +1,23 @@ +{{- with .Values.config.jetstream }} +{{- with .memoryStore }} +{{- if .enabled }} +{{- with .maxSize }} +max_memory_store: << {{ . }} >> +{{- end }} +{{- else }} +max_memory_store: 0 +{{- end }} +{{- end }} +{{- with .fileStore }} +{{- if .enabled }} +store_dir: {{ .dir }} +{{- if .maxSize }} +max_file_store: << {{ .maxSize }} >> +{{- else if .pvc.enabled }} +max_file_store: << {{ .pvc.size }} >> +{{- end }} +{{- else }} +max_file_store: 0 +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/leafnodes.yaml b/charts/nats/nats/1.2.10/files/config/leafnodes.yaml new file mode 100644 index 000000000..3a1d9a14a --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/leafnodes.yaml @@ -0,0 +1,11 @@ +{{- with .Values.config.leafnodes }} +port: {{ .port }} +no_advertise: true + +{{- with .tls }} +{{- if .enabled }} +tls: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/tls.yaml" "ctx" (merge (dict "tls" .) $)) .) | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/mqtt.yaml b/charts/nats/nats/1.2.10/files/config/mqtt.yaml new file mode 100644 index 000000000..e25d8a3e0 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/mqtt.yaml @@ -0,0 +1,10 @@ +{{- with .Values.config.mqtt }} +port: {{ .port }} + +{{- with .tls }} +{{- if .enabled }} +tls: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/tls.yaml" "ctx" (merge (dict "tls" .) $)) .) | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/protocol.yaml b/charts/nats/nats/1.2.10/files/config/protocol.yaml new file mode 100644 index 000000000..288c80d75 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/protocol.yaml @@ -0,0 +1,10 @@ +{{- with .protocol }} +port: {{ .port }} + +{{- with .tls }} +{{- if .enabled }} +tls: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/tls.yaml" "ctx" (merge (dict "tls" .) $)) .) | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/resolver.yaml b/charts/nats/nats/1.2.10/files/config/resolver.yaml new file mode 100644 index 000000000..a6761c403 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/resolver.yaml @@ -0,0 +1,3 @@ +{{- with .Values.config.resolver }} +dir: {{ .dir }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/tls.yaml b/charts/nats/nats/1.2.10/files/config/tls.yaml new file mode 100644 index 000000000..26aee0155 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/tls.yaml @@ -0,0 +1,16 @@ +# tls +{{- with .tls }} +{{- if .secretName }} +{{- $dir := trimSuffix "/" .dir }} +cert_file: {{ printf "%s/%s" $dir (.cert | default "tls.crt") | quote }} +key_file: {{ printf "%s/%s" $dir (.key | default "tls.key") | quote }} +{{- end }} +{{- end }} + +# tlsCA +{{- with $.Values.tlsCA }} +{{- if and .enabled (or .configMapName .secretName) }} +{{- $dir := trimSuffix "/" .dir }} +ca_file: {{ printf "%s/%s" $dir (.key | default "ca.crt") | quote }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/config/websocket.yaml b/charts/nats/nats/1.2.10/files/config/websocket.yaml new file mode 100644 index 000000000..afcd178a7 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/config/websocket.yaml @@ -0,0 +1,12 @@ +{{- with .Values.config.websocket }} +port: {{ .port }} + +{{- if .tls.enabled }} +{{- with .tls }} +tls: + {{- include "nats.loadMergePatch" (merge (dict "file" "config/tls.yaml" "ctx" (merge (dict "tls" .) $)) .) | nindent 2 }} +{{- end }} +{{- else }} +no_tls: true +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/headless-service.yaml b/charts/nats/nats/1.2.10/files/headless-service.yaml new file mode 100644 index 000000000..da6552b37 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/headless-service.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.headlessService.name }} + labels: + {{- include "nats.labels" $ | nindent 4 }} +spec: + selector: + {{- include "nats.selectorLabels" $ | nindent 4 }} + clusterIP: None + publishNotReadyAddresses: true + ports: + {{- range $protocol := list "nats" "leafnodes" "websocket" "mqtt" "cluster" "gateway" "monitor" "profiling" }} + {{- $configProtocol := get $.Values.config $protocol }} + {{- if or (eq $protocol "nats") $configProtocol.enabled }} + {{- $tlsEnabled := false }} + {{- if hasKey $configProtocol "tls" }} + {{- $tlsEnabled = $configProtocol.tls.enabled }} + {{- end }} + {{- $appProtocol := or (eq $protocol "websocket") (eq $protocol "monitor") | ternary ($tlsEnabled | ternary "https" "http") ($tlsEnabled | ternary "tls" "tcp") }} + - {{ dict "name" $protocol "port" $configProtocol.port "targetPort" $protocol "appProtocol" $appProtocol | toYaml | nindent 4 }} + {{- end }} + {{- end }} diff --git a/charts/nats/nats/1.2.10/files/ingress.yaml b/charts/nats/nats/1.2.10/files/ingress.yaml new file mode 100644 index 000000000..b59f0fa5f --- /dev/null +++ b/charts/nats/nats/1.2.10/files/ingress.yaml @@ -0,0 +1,34 @@ +{{- with .Values.config.websocket.ingress }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .name }} + labels: + {{- include "nats.labels" $ | nindent 4 }} +spec: + {{- with .className }} + ingressClassName: {{ . | quote }} + {{- end }} + rules: + {{- $path := .path }} + {{- $pathType := .pathType }} + {{- range .hosts }} + - host: {{ . | quote }} + http: + paths: + - path: {{ $path | quote }} + pathType: {{ $pathType | quote }} + backend: + service: + name: {{ $.Values.service.name }} + port: + name: websocket + {{- end }} + {{- if .tlsSecretName }} + tls: + - secretName: {{ .tlsSecretName | quote }} + hosts: + {{- toYaml .hosts | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/nats-box/contents-secret.yaml b/charts/nats/nats/1.2.10/files/nats-box/contents-secret.yaml new file mode 100644 index 000000000..6e8fdb26f --- /dev/null +++ b/charts/nats/nats/1.2.10/files/nats-box/contents-secret.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Secret +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.natsBox.contentsSecret.name }} + labels: + {{- include "natsBox.labels" $ | nindent 4 }} +type: Opaque +stringData: + {{- range $ctxKey, $ctxVal := .Values.natsBox.contexts }} + {{- range $secretKey, $secretVal := dict "creds" "creds" "nkey" "nk" }} + {{- $secret := get $ctxVal $secretKey }} + {{- if and $secret $secret.contents }} + "{{ $ctxKey }}.{{ $secretVal }}": {{ $secret.contents | quote }} + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/nats/nats/1.2.10/files/nats-box/contexts-secret/context.yaml b/charts/nats/nats/1.2.10/files/nats-box/contexts-secret/context.yaml new file mode 100644 index 000000000..54480eac9 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/nats-box/contexts-secret/context.yaml @@ -0,0 +1,51 @@ +{{- $contextName := .contextName }} + +# url +{{- if .Values.service.enabled }} +url: nats://{{ .Values.service.name }} +{{- else }} +url: nats://{{ .Values.headlessService.name }} +{{- end }} + +{{- with .context }} + +# creds +{{- with .creds}} +{{- if .contents }} +creds: /etc/nats-contents/{{ $contextName }}.creds +{{- else if .secretName }} +{{- $dir := trimSuffix "/" .dir }} +creds: {{ printf "%s/%s" $dir (.key | default "nats.creds") | quote }} +{{- end }} +{{- end }} + +# nkey +{{- with .nkey}} +{{- if .contents }} +nkey: /etc/nats-contents/{{ $contextName }}.nk +{{- else if .secretName }} +{{- $dir := trimSuffix "/" .dir }} +nkey: {{ printf "%s/%s" $dir (.key | default "nats.nk") | quote }} +{{- end }} +{{- end }} + +# tls +{{- with .tls }} +{{- if .secretName }} +{{- $dir := trimSuffix "/" .dir }} +cert: {{ printf "%s/%s" $dir (.cert | default "tls.crt") | quote }} +key: {{ printf "%s/%s" $dir (.key | default "tls.key") | quote }} +{{- end }} +{{- end }} + +# tlsCA +{{- if $.Values.config.nats.tls.enabled }} +{{- with $.Values.tlsCA }} +{{- if and .enabled (or .configMapName .secretName) }} +{{- $dir := trimSuffix "/" .dir }} +ca: {{ printf "%s/%s" $dir (.key | default "ca.crt") | quote }} +{{- end }} +{{- end }} +{{- end }} + +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/nats-box/contexts-secret/contexts-secret.yaml b/charts/nats/nats/1.2.10/files/nats-box/contexts-secret/contexts-secret.yaml new file mode 100644 index 000000000..0ce8d1d87 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/nats-box/contexts-secret/contexts-secret.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.natsBox.contextsSecret.name }} + labels: + {{- include "natsBox.labels" $ | nindent 4 }} +type: Opaque +stringData: +{{- range $ctxKey, $ctxVal := .Values.natsBox.contexts }} + "{{ $ctxKey }}.json": | + {{- include "toPrettyRawJson" (include "nats.loadMergePatch" (dict "file" "nats-box/contexts-secret/context.yaml" "merge" (.merge | default dict) "patch" (.patch | default list) "ctx" (merge (dict "contextName" $ctxKey "context" $ctxVal) $)) | fromYaml) | nindent 4 }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/nats-box/deployment/container.yaml b/charts/nats/nats/1.2.10/files/nats-box/deployment/container.yaml new file mode 100644 index 000000000..aa1753b4b --- /dev/null +++ b/charts/nats/nats/1.2.10/files/nats-box/deployment/container.yaml @@ -0,0 +1,46 @@ +name: nats-box +{{ include "nats.image" (merge (pick $.Values "global") .Values.natsBox.container.image) }} + +{{- with .Values.natsBox.container.env }} +env: +{{- include "nats.env" . }} +{{- end }} + +command: +- sh +- -ec +- | + work_dir="$(pwd)" + mkdir -p "$XDG_CONFIG_HOME/nats" + cd "$XDG_CONFIG_HOME/nats" + if ! [ -s context ]; then + ln -s /etc/nats-contexts context + fi + {{- if .Values.natsBox.defaultContextName }} + if ! [ -f context.txt ]; then + echo -n {{ .Values.natsBox.defaultContextName | quote }} > context.txt + fi + {{- end }} + cd "$work_dir" + exec /entrypoint.sh "$@" +- -- +args: +- sh +- -ec +- trap true INT TERM; sleep infinity & wait +volumeMounts: +# contexts secret +- name: contexts + mountPath: /etc/nats-contexts +# contents secret +{{- if .hasContentsSecret }} +- name: contents + mountPath: /etc/nats-contents +{{- end }} +# tlsCA +{{- include "nats.tlsCAVolumeMount" $ }} +# secrets +{{- range (include "natsBox.secretNames" $ | fromJson).secretNames }} +- name: {{ .name | quote }} + mountPath: {{ .dir | quote }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/nats-box/deployment/deployment.yaml b/charts/nats/nats/1.2.10/files/nats-box/deployment/deployment.yaml new file mode 100644 index 000000000..bf39dd8d5 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/nats-box/deployment/deployment.yaml @@ -0,0 +1,16 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.natsBox.deployment.name }} + labels: + {{- include "natsBox.labels" $ | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "natsBox.selectorLabels" $ | nindent 6 }} + replicas: 1 + template: + {{- with .Values.natsBox.podTemplate }} + {{ include "nats.loadMergePatch" (merge (dict "file" "nats-box/deployment/pod-template.yaml" "ctx" $) .) | nindent 4 }} + {{- end }} diff --git a/charts/nats/nats/1.2.10/files/nats-box/deployment/pod-template.yaml b/charts/nats/nats/1.2.10/files/nats-box/deployment/pod-template.yaml new file mode 100644 index 000000000..71056bfb6 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/nats-box/deployment/pod-template.yaml @@ -0,0 +1,44 @@ +metadata: + labels: + {{- include "natsBox.labels" $ | nindent 4 }} +spec: + containers: + {{- with .Values.natsBox.container }} + - {{ include "nats.loadMergePatch" (merge (dict "file" "nats-box/deployment/container.yaml" "ctx" $) .) | nindent 4 }} + {{- end }} + + # service discovery uses DNS; don't need service env vars + enableServiceLinks: false + + {{- with .Values.global.image.pullSecretNames }} + imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} + {{- end }} + + {{- with .Values.natsBox.serviceAccount }} + {{- if .enabled }} + serviceAccountName: {{ .name | quote }} + {{- end }} + {{- end }} + + volumes: + # contexts secret + - name: contexts + secret: + secretName: {{ .Values.natsBox.contextsSecret.name }} + # contents secret + {{- if .hasContentsSecret }} + - name: contents + secret: + secretName: {{ .Values.natsBox.contentsSecret.name }} + {{- end }} + # tlsCA + {{- include "nats.tlsCAVolume" $ | nindent 2 }} + # secrets + {{- range (include "natsBox.secretNames" $ | fromJson).secretNames }} + - name: {{ .name | quote }} + secret: + secretName: {{ .secretName | quote }} + {{- end }} diff --git a/charts/nats/nats/1.2.10/files/nats-box/service-account.yaml b/charts/nats/nats/1.2.10/files/nats-box/service-account.yaml new file mode 100644 index 000000000..c31e52f18 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/nats-box/service-account.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.natsBox.serviceAccount.name }} + labels: + {{- include "natsBox.labels" $ | nindent 4 }} diff --git a/charts/nats/nats/1.2.10/files/pod-disruption-budget.yaml b/charts/nats/nats/1.2.10/files/pod-disruption-budget.yaml new file mode 100644 index 000000000..fd1fdead5 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/pod-disruption-budget.yaml @@ -0,0 +1,12 @@ +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.podDisruptionBudget.name }} + labels: + {{- include "nats.labels" $ | nindent 4 }} +spec: + maxUnavailable: 1 + selector: + matchLabels: + {{- include "nats.selectorLabels" $ | nindent 6 }} diff --git a/charts/nats/nats/1.2.10/files/pod-monitor.yaml b/charts/nats/nats/1.2.10/files/pod-monitor.yaml new file mode 100644 index 000000000..c6c8eae06 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/pod-monitor.yaml @@ -0,0 +1,13 @@ +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.promExporter.podMonitor.name }} + labels: + {{- include "nats.labels" $ | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "nats.selectorLabels" $ | nindent 6 }} + podMetricsEndpoints: + - port: prom-metrics diff --git a/charts/nats/nats/1.2.10/files/service-account.yaml b/charts/nats/nats/1.2.10/files/service-account.yaml new file mode 100644 index 000000000..22c18cc70 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/service-account.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.serviceAccount.name }} + labels: + {{- include "nats.labels" $ | nindent 4 }} diff --git a/charts/nats/nats/1.2.10/files/service.yaml b/charts/nats/nats/1.2.10/files/service.yaml new file mode 100644 index 000000000..db08fe5b5 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/service.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.service.name }} + labels: + {{- include "nats.labels" $ | nindent 4 }} +spec: + selector: + {{- include "nats.selectorLabels" $ | nindent 4 }} + ports: + {{- range $protocol := list "nats" "leafnodes" "websocket" "mqtt" "cluster" "gateway" "monitor" "profiling" }} + {{- $configProtocol := get $.Values.config $protocol }} + {{- $servicePort := get $.Values.service.ports $protocol }} + {{- if and (or (eq $protocol "nats") $configProtocol.enabled) $servicePort.enabled }} + {{- $tlsEnabled := false }} + {{- if hasKey $configProtocol "tls" }} + {{- $tlsEnabled = $configProtocol.tls.enabled }} + {{- end }} + {{- $appProtocol := or (eq $protocol "websocket") (eq $protocol "monitor") | ternary ($tlsEnabled | ternary "https" "http") ($tlsEnabled | ternary "tls" "tcp") }} + - {{ merge (dict "name" $protocol "targetPort" $protocol "appProtocol" $appProtocol) (omit $servicePort "enabled") (dict "port" $configProtocol.port) | toYaml | nindent 4 }} + {{- end }} + {{- end }} diff --git a/charts/nats/nats/1.2.10/files/stateful-set/jetstream-pvc.yaml b/charts/nats/nats/1.2.10/files/stateful-set/jetstream-pvc.yaml new file mode 100644 index 000000000..a43f20059 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/stateful-set/jetstream-pvc.yaml @@ -0,0 +1,13 @@ +{{- with .Values.config.jetstream.fileStore.pvc }} +metadata: + name: {{ .name }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .size | quote }} + {{- with .storageClassName }} + storageClassName: {{ . | quote }} + {{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/stateful-set/nats-container.yaml b/charts/nats/nats/1.2.10/files/stateful-set/nats-container.yaml new file mode 100644 index 000000000..c5402efea --- /dev/null +++ b/charts/nats/nats/1.2.10/files/stateful-set/nats-container.yaml @@ -0,0 +1,106 @@ +name: nats +{{ include "nats.image" (merge (pick $.Values "global") .Values.container.image) }} + +ports: +{{- range $protocol := list "nats" "leafnodes" "websocket" "mqtt" "cluster" "gateway" "monitor" "profiling" }} +{{- $configProtocol := get $.Values.config $protocol }} +{{- $containerPort := get $.Values.container.ports $protocol }} +{{- if or (eq $protocol "nats") $configProtocol.enabled }} +- {{ merge (dict "name" $protocol "containerPort" $configProtocol.port) $containerPort | toYaml | nindent 2 }} +{{- end }} +{{- end }} + +args: +- --config +- /etc/nats-config/nats.conf + +env: +- name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name +- name: SERVER_NAME + value: {{ printf "%s$(POD_NAME)" .Values.config.serverNamePrefix | quote }} +{{- with .Values.container.env }} +{{- include "nats.env" . }} +{{- end }} + +lifecycle: + preStop: + exec: + # send the lame duck shutdown signal to trigger a graceful shutdown + command: + - nats-server + - -sl=ldm=/var/run/nats/nats.pid + +{{- with .Values.config.monitor }} +{{- if .enabled }} +startupProbe: + httpGet: + path: /healthz + port: monitor + {{- if .tls.enabled }} + scheme: HTTPS + {{- end}} + initialDelaySeconds: 10 + timeoutSeconds: 5 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 90 +readinessProbe: + httpGet: + path: /healthz?js-server-only=true + port: monitor + {{- if .tls.enabled }} + scheme: HTTPS + {{- end}} + initialDelaySeconds: 10 + timeoutSeconds: 5 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 +livenessProbe: + httpGet: + path: /healthz?js-enabled-only=true + port: monitor + {{- if .tls.enabled }} + scheme: HTTPS + {{- end}} + initialDelaySeconds: 10 + timeoutSeconds: 5 + periodSeconds: 30 + successThreshold: 1 + failureThreshold: 3 +{{- end }} +{{- end }} + +volumeMounts: +# nats config +- name: config + mountPath: /etc/nats-config +# PID volume +- name: pid + mountPath: /var/run/nats +# JetStream PVC +{{- with .Values.config.jetstream }} +{{- if and .enabled .fileStore.enabled .fileStore.pvc.enabled }} +{{- with .fileStore }} +- name: {{ .pvc.name }} + mountPath: {{ .dir | quote }} +{{- end }} +{{- end }} +{{- end }} +# resolver PVC +{{- with .Values.config.resolver }} +{{- if and .enabled .pvc.enabled }} +- name: {{ .pvc.name }} + mountPath: {{ .dir | quote }} +{{- end }} +{{- end }} +# tlsCA +{{- include "nats.tlsCAVolumeMount" $ }} +# secrets +{{- range (include "nats.secretNames" $ | fromJson).secretNames }} +- name: {{ .name | quote }} + mountPath: {{ .dir | quote }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/stateful-set/pod-template.yaml b/charts/nats/nats/1.2.10/files/stateful-set/pod-template.yaml new file mode 100644 index 000000000..bb1d8d7be --- /dev/null +++ b/charts/nats/nats/1.2.10/files/stateful-set/pod-template.yaml @@ -0,0 +1,71 @@ +metadata: + labels: + {{- include "nats.labels" $ | nindent 4 }} + annotations: + {{- if .Values.podTemplate.configChecksumAnnotation }} + {{- $configMap := include "nats.loadMergePatch" (merge (dict "file" "config-map.yaml" "ctx" $) $.Values.configMap) }} + checksum/config: {{ sha256sum $configMap }} + {{- end }} +spec: + containers: + # nats + {{- $nats := dict }} + {{- with .Values.container }} + {{- $nats = include "nats.loadMergePatch" (merge (dict "file" "stateful-set/nats-container.yaml" "ctx" $) .) | fromYaml }} + - {{ toYaml $nats | nindent 4 }} + {{- end }} + # reloader + {{- with .Values.reloader }} + {{- if .enabled }} + - {{ include "nats.loadMergePatch" (merge (dict "file" "stateful-set/reloader-container.yaml" "ctx" (merge (dict "natsVolumeMounts" $nats.volumeMounts) $)) .) | nindent 4 }} + {{- end }} + {{- end }} + {{- with .Values.promExporter }} + {{- if .enabled }} + - {{ include "nats.loadMergePatch" (merge (dict "file" "stateful-set/prom-exporter-container.yaml" "ctx" $) .) | nindent 4 }} + {{- end }} + {{- end }} + + # service discovery uses DNS; don't need service env vars + enableServiceLinks: false + + {{- with .Values.global.image.pullSecretNames }} + imagePullSecrets: + {{- range . }} + - name: {{ . | quote }} + {{- end }} + {{- end }} + + {{- with .Values.serviceAccount }} + {{- if .enabled }} + serviceAccountName: {{ .name | quote }} + {{- end }} + {{- end }} + + {{- if .Values.reloader.enabled }} + shareProcessNamespace: true + {{- end }} + + volumes: + # nats config + - name: config + configMap: + name: {{ .Values.configMap.name }} + # PID volume + - name: pid + emptyDir: {} + # tlsCA + {{- include "nats.tlsCAVolume" $ | nindent 2 }} + # secrets + {{- range (include "nats.secretNames" $ | fromJson).secretNames }} + - name: {{ .name | quote }} + secret: + secretName: {{ .secretName | quote }} + {{- end }} + + {{- with .Values.podTemplate.topologySpreadConstraints }} + topologySpreadConstraints: + {{- range $k, $v := . }} + - {{ merge (dict "topologyKey" $k "labelSelector" (dict "matchLabels" (include "nats.selectorLabels" $ | fromYaml))) $v | toYaml | nindent 4 }} + {{- end }} + {{- end}} diff --git a/charts/nats/nats/1.2.10/files/stateful-set/prom-exporter-container.yaml b/charts/nats/nats/1.2.10/files/stateful-set/prom-exporter-container.yaml new file mode 100644 index 000000000..c3e1b6fbe --- /dev/null +++ b/charts/nats/nats/1.2.10/files/stateful-set/prom-exporter-container.yaml @@ -0,0 +1,30 @@ +name: prom-exporter +{{ include "nats.image" (merge (pick $.Values "global") .Values.promExporter.image) }} + +ports: +- name: prom-metrics + containerPort: {{ .Values.promExporter.port }} + +{{- with .Values.promExporter.env }} +env: +{{- include "nats.env" . }} +{{- end }} + +args: +- -port={{ .Values.promExporter.port }} +- -connz +- -routez +- -subz +- -varz +- -prefix=nats +- -use_internal_server_id +{{- if .Values.config.jetstream.enabled }} +- -jsz=all +{{- end }} +{{- if .Values.config.leafnodes.enabled }} +- -leafz +{{- end }} +{{- if .Values.config.gateway.enabled }} +- -gatewayz +{{- end }} +- http://localhost:{{ .Values.config.monitor.port }}/ diff --git a/charts/nats/nats/1.2.10/files/stateful-set/reloader-container.yaml b/charts/nats/nats/1.2.10/files/stateful-set/reloader-container.yaml new file mode 100644 index 000000000..96722045f --- /dev/null +++ b/charts/nats/nats/1.2.10/files/stateful-set/reloader-container.yaml @@ -0,0 +1,27 @@ +name: reloader +{{ include "nats.image" (merge (pick $.Values "global") .Values.reloader.image) }} + +{{- with .Values.reloader.env }} +env: +{{- include "nats.env" . }} +{{- end }} + +args: +- -pid +- /var/run/nats/nats.pid +- -config +- /etc/nats-config/nats.conf +{{ include "nats.reloaderConfig" (dict "config" .config "dir" "/etc/nats-config") }} + +volumeMounts: +- name: pid + mountPath: /var/run/nats +{{- range $mnt := .natsVolumeMounts }} +{{- $found := false }} +{{- range $.Values.reloader.natsVolumeMountPrefixes }} +{{- if and (not $found) (hasPrefix . $mnt.mountPath) }} +{{- $found = true }} +- {{ toYaml $mnt | nindent 2}} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/stateful-set/resolver-pvc.yaml b/charts/nats/nats/1.2.10/files/stateful-set/resolver-pvc.yaml new file mode 100644 index 000000000..3634cd826 --- /dev/null +++ b/charts/nats/nats/1.2.10/files/stateful-set/resolver-pvc.yaml @@ -0,0 +1,13 @@ +{{- with .Values.config.resolver.pvc }} +metadata: + name: {{ .name }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .size | quote }} + {{- with .storageClassName }} + storageClassName: {{ . | quote }} + {{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/files/stateful-set/stateful-set.yaml b/charts/nats/nats/1.2.10/files/stateful-set/stateful-set.yaml new file mode 100644 index 000000000..cd8082cbb --- /dev/null +++ b/charts/nats/nats/1.2.10/files/stateful-set/stateful-set.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + {{- include "nats.metadataNamespace" $ | nindent 2 }} + name: {{ .Values.statefulSet.name }} + labels: + {{- include "nats.labels" $ | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "nats.selectorLabels" $ | nindent 6 }} + {{- if .Values.config.cluster.enabled }} + replicas: {{ .Values.config.cluster.replicas }} + {{- else }} + replicas: 1 + {{- end }} + serviceName: {{ .Values.headlessService.name }} + podManagementPolicy: Parallel + template: + {{- with .Values.podTemplate }} + {{ include "nats.loadMergePatch" (merge (dict "file" "stateful-set/pod-template.yaml" "ctx" $) .) | nindent 4 }} + {{- end }} + volumeClaimTemplates: + {{- with .Values.config.jetstream }} + {{- if and .enabled .fileStore.enabled .fileStore.pvc.enabled }} + {{- with .fileStore.pvc }} + - {{ include "nats.loadMergePatch" (merge (dict "file" "stateful-set/jetstream-pvc.yaml" "ctx" $) .) | nindent 4 }} + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.config.resolver }} + {{- if and .enabled .pvc.enabled }} + {{- with .pvc }} + - {{ include "nats.loadMergePatch" (merge (dict "file" "stateful-set/resolver-pvc.yaml" "ctx" $) .) | nindent 4 }} + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/nats/nats/1.2.10/questions.yaml b/charts/nats/nats/1.2.10/questions.yaml new file mode 100644 index 000000000..a476e440d --- /dev/null +++ b/charts/nats/nats/1.2.10/questions.yaml @@ -0,0 +1,12 @@ +questions: +- variable: cluster.enabled + default: false + type: boolean + label: Enable Cluster + group: "Cluster Settings" + show_subquestion_if: "true" + subquestions: + - variable: cluster.replicas + default: 3 + type: int + label: Replicas diff --git a/charts/nats/nats/1.2.10/templates/_helpers.tpl b/charts/nats/nats/1.2.10/templates/_helpers.tpl new file mode 100644 index 000000000..ba831397d --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/_helpers.tpl @@ -0,0 +1,281 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "nats.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "nats.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "nats.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Print the namespace +*/}} +{{- define "nats.namespace" -}} +{{- default .Release.Namespace .Values.namespaceOverride }} +{{- end }} + +{{/* +Print the namespace for the metadata section +*/}} +{{- define "nats.metadataNamespace" -}} +{{- with .Values.namespaceOverride }} +namespace: {{ . | quote }} +{{- end }} +{{- end }} + +{{/* +Set default values. +*/}} +{{- define "nats.defaultValues" }} +{{- if not .defaultValuesSet }} + {{- $name := include "nats.fullname" . }} + {{- with .Values }} + {{- $_ := set .config.jetstream.fileStore.pvc "name" (.config.jetstream.fileStore.pvc.name | default (printf "%s-js" $name)) }} + {{- $_ := set .config.resolver.pvc "name" (.config.resolver.pvc.name | default (printf "%s-resolver" $name)) }} + {{- $_ := set .config.websocket.ingress "name" (.config.websocket.ingress.name | default (printf "%s-ws" $name)) }} + {{- $_ := set .configMap "name" (.configMap.name | default (printf "%s-config" $name)) }} + {{- $_ := set .headlessService "name" (.headlessService.name | default (printf "%s-headless" $name)) }} + {{- $_ := set .natsBox.contentsSecret "name" (.natsBox.contentsSecret.name | default (printf "%s-box-contents" $name)) }} + {{- $_ := set .natsBox.contextsSecret "name" (.natsBox.contextsSecret.name | default (printf "%s-box-contexts" $name)) }} + {{- $_ := set .natsBox.deployment "name" (.natsBox.deployment.name | default (printf "%s-box" $name)) }} + {{- $_ := set .natsBox.serviceAccount "name" (.natsBox.serviceAccount.name | default (printf "%s-box" $name)) }} + {{- $_ := set .podDisruptionBudget "name" (.podDisruptionBudget.name | default $name) }} + {{- $_ := set .service "name" (.service.name | default $name) }} + {{- $_ := set .serviceAccount "name" (.serviceAccount.name | default $name) }} + {{- $_ := set .statefulSet "name" (.statefulSet.name | default $name) }} + {{- $_ := set .promExporter.podMonitor "name" (.promExporter.podMonitor.name | default $name) }} + {{- end }} + + {{- $values := get (include "tplYaml" (dict "doc" .Values "ctx" $) | fromJson) "doc" }} + {{- $_ := set . "Values" $values }} + + {{- $hasContentsSecret := false }} + {{- range $ctxKey, $ctxVal := .Values.natsBox.contexts }} + {{- range $secretKey, $secretVal := dict "creds" "nats-creds" "nkey" "nats-nkeys" "tls" "nats-certs" }} + {{- $secret := get $ctxVal $secretKey }} + {{- if $secret }} + {{- $_ := set $secret "dir" ($secret.dir | default (printf "/etc/%s/%s" $secretVal $ctxKey)) }} + {{- if and (ne $secretKey "tls") $secret.contents }} + {{- $hasContentsSecret = true }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- $_ := set $ "hasContentsSecret" $hasContentsSecret }} + + {{- with .Values.config }} + {{- $config := include "nats.loadMergePatch" (merge (dict "file" "config/config.yaml" "ctx" $) .) | fromYaml }} + {{- $_ := set $ "config" $config }} + {{- end }} + + {{- $_ := set . "defaultValuesSet" true }} +{{- end }} +{{- end }} + +{{/* +NATS labels +*/}} +{{- define "nats.labels" -}} +{{- with .Values.global.labels -}} +{{ toYaml . }} +{{ end -}} +helm.sh/chart: {{ include "nats.chart" . }} +{{ include "nats.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +NATS selector labels +*/}} +{{- define "nats.selectorLabels" -}} +app.kubernetes.io/name: {{ include "nats.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: nats +{{- end }} + +{{/* +NATS Box labels +*/}} +{{- define "natsBox.labels" -}} +{{- with .Values.global.labels -}} +{{ toYaml . }} +{{ end -}} +helm.sh/chart: {{ include "nats.chart" . }} +{{ include "natsBox.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +NATS Box selector labels +*/}} +{{- define "natsBox.selectorLabels" -}} +app.kubernetes.io/name: {{ include "nats.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: nats-box +{{- end }} + +{{/* +Print the image +*/}} +{{- define "nats.image" }} +{{- $image := printf "%s:%s" .repository .tag }} +{{- if or .registry .global.image.registry }} +{{- $image = printf "%s/%s" (.registry | default .global.image.registry) $image }} +{{- end -}} +image: {{ $image }} +{{- if or .pullPolicy .global.image.pullPolicy }} +imagePullPolicy: {{ .pullPolicy | default .global.image.pullPolicy }} +{{- end }} +{{- end }} + +{{- define "nats.secretNames" -}} +{{- $secrets := list }} +{{- range $protocol := list "nats" "leafnodes" "websocket" "mqtt" "cluster" "gateway" }} + {{- $configProtocol := get $.Values.config $protocol }} + {{- if and (or (eq $protocol "nats") $configProtocol.enabled) $configProtocol.tls.enabled $configProtocol.tls.secretName }} + {{- $secrets = append $secrets (merge (dict "name" (printf "%s-tls" $protocol)) $configProtocol.tls) }} + {{- end }} +{{- end }} +{{- toJson (dict "secretNames" $secrets) }} +{{- end }} + +{{- define "natsBox.secretNames" -}} +{{- $secrets := list }} +{{- range $ctxKey, $ctxVal := .Values.natsBox.contexts }} +{{- range $secretKey, $secretVal := dict "creds" "nats-creds" "nkey" "nats-nkeys" "tls" "nats-certs" }} + {{- $secret := get $ctxVal $secretKey }} + {{- if and $secret $secret.secretName }} + {{- $secrets = append $secrets (merge (dict "name" (printf "ctx-%s-%s" $ctxKey $secretKey)) $secret) }} + {{- end }} + {{- end }} +{{- end }} +{{- toJson (dict "secretNames" $secrets) }} +{{- end }} + +{{- define "nats.tlsCAVolume" -}} +{{- with .Values.tlsCA }} +{{- if and .enabled (or .configMapName .secretName) }} +- name: tls-ca +{{- if .configMapName }} + configMap: + name: {{ .configMapName | quote }} +{{- else if .secretName }} + secret: + secretName: {{ .secretName | quote }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "nats.tlsCAVolumeMount" -}} +{{- with .Values.tlsCA }} +{{- if and .enabled (or .configMapName .secretName) }} +- name: tls-ca + mountPath: {{ .dir | quote }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +translates env var map to list +*/}} +{{- define "nats.env" -}} +{{- range $k, $v := . }} +{{- if kindIs "string" $v }} +- name: {{ $k | quote }} + value: {{ $v | quote }} +{{- else if kindIs "map" $v }} +- {{ merge (dict "name" $k) $v | toYaml | nindent 2 }} +{{- else }} +{{- fail (cat "env var" $k "must be string or map, got" (kindOf $v)) }} +{{- end }} +{{- end }} +{{- end }} + +{{- /* +nats.loadMergePatch +input: map with 4 keys: +- file: name of file to load +- ctx: context to pass to tpl +- merge: interface{} to merge +- patch: []interface{} valid JSON Patch document +output: JSON encoded map with 1 key: +- doc: interface{} patched json result +*/}} +{{- define "nats.loadMergePatch" -}} +{{- $doc := tpl (.ctx.Files.Get (printf "files/%s" .file)) .ctx | fromYaml | default dict -}} +{{- $doc = mergeOverwrite $doc (deepCopy (.merge | default dict)) -}} +{{- get (include "jsonpatch" (dict "doc" $doc "patch" (.patch | default list)) | fromJson ) "doc" | toYaml -}} +{{- end }} + + +{{- /* +nats.reloaderConfig +input: map with 2 keys: +- config: interface{} nats config +- dir: dir config file is in +output: YAML list of reloader config files +*/}} +{{- define "nats.reloaderConfig" -}} + {{- $dir := trimSuffix "/" .dir -}} + {{- with .config -}} + {{- if kindIs "map" . -}} + {{- range $k, $v := . -}} + {{- if or (eq $k "cert_file") (eq $k "key_file") (eq $k "ca_file") }} +- -config +- {{ $v }} + {{- else if hasSuffix "$include" $k }} +- -config +- {{ clean (printf "%s/%s" $dir $v) }} + {{- else }} + {{- include "nats.reloaderConfig" (dict "config" $v "dir" $dir) }} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + + +{{- /* +nats.formatConfig +input: map[string]interface{} +output: string with following format rules +1. keys ending in $natsRaw are unquoted +2. keys ending in $natsInclude are converted to include directives +*/}} +{{- define "nats.formatConfig" -}} + {{- + (regexReplaceAll "\"<<\\s+(.*)\\s+>>\"" + (regexReplaceAll "\".*\\$include\": \"(.*)\",?" (include "toPrettyRawJson" .) "include ${1};") + "${1}") + -}} +{{- end -}} diff --git a/charts/nats/nats/1.2.10/templates/_jsonpatch.tpl b/charts/nats/nats/1.2.10/templates/_jsonpatch.tpl new file mode 100644 index 000000000..cd42c3bbc --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/_jsonpatch.tpl @@ -0,0 +1,219 @@ +{{- /* +jsonpatch +input: map with 2 keys: +- doc: interface{} valid JSON document +- patch: []interface{} valid JSON Patch document +output: JSON encoded map with 1 key: +- doc: interface{} patched json result +*/}} +{{- define "jsonpatch" -}} + {{- $params := fromJson (toJson .) -}} + {{- $patches := $params.patch -}} + {{- $docContainer := pick $params "doc" -}} + + {{- range $patch := $patches -}} + {{- if not (hasKey $patch "op") -}} + {{- fail "patch is missing op key" -}} + {{- end -}} + {{- if and (ne $patch.op "add") (ne $patch.op "remove") (ne $patch.op "replace") (ne $patch.op "copy") (ne $patch.op "move") (ne $patch.op "test") -}} + {{- fail (cat "patch has invalid op" $patch.op) -}} + {{- end -}} + {{- if not (hasKey $patch "path") -}} + {{- fail "patch is missing path key" -}} + {{- end -}} + {{- if and (or (eq $patch.op "add") (eq $patch.op "replace") (eq $patch.op "test")) (not (hasKey $patch "value")) -}} + {{- fail (cat "patch with op" $patch.op "is missing value key") -}} + {{- end -}} + {{- if and (or (eq $patch.op "copy") (eq $patch.op "move")) (not (hasKey $patch "from")) -}} + {{- fail (cat "patch with op" $patch.op "is missing from key") -}} + {{- end -}} + + {{- $opPathKeys := list "path" -}} + {{- if or (eq $patch.op "copy") (eq $patch.op "move") -}} + {{- $opPathKeys = append $opPathKeys "from" -}} + {{- end -}} + {{- $reSlice := list -}} + + {{- range $opPathKey := $opPathKeys -}} + {{- $obj := $docContainer -}} + {{- if and (eq $patch.op "copy") (eq $opPathKey "from") -}} + {{- $obj = (fromJson (toJson $docContainer)) -}} + {{- end -}} + {{- $key := "doc" -}} + {{- $lastMap := dict "root" $obj -}} + {{- $lastKey := "root" -}} + {{- $paths := (splitList "/" (get $patch $opPathKey)) -}} + {{- $firstPath := index $paths 0 -}} + {{- if ne (index $paths 0) "" -}} + {{- fail (cat "invalid" $opPathKey (get $patch $opPathKey) "must be empty string or start with /") -}} + {{- end -}} + {{- $paths = slice $paths 1 -}} + + {{- range $path := $paths -}} + {{- $path = replace "~1" "/" $path -}} + {{- $path = replace "~0" "~" $path -}} + + {{- if kindIs "slice" $obj -}} + {{- $mapObj := dict -}} + {{- range $i, $v := $obj -}} + {{- $_ := set $mapObj (toString $i) $v -}} + {{- end -}} + {{- $obj = $mapObj -}} + {{- $_ := set $lastMap $lastKey $obj -}} + {{- $reSlice = prepend $reSlice (dict "lastMap" $lastMap "lastKey" $lastKey "mapObj" $obj) -}} + {{- end -}} + + {{- if kindIs "map" $obj -}} + {{- if not (hasKey $obj $key) -}} + {{- fail (cat "key" $key "does not exist") -}} + {{- end -}} + {{- $lastKey = $key -}} + {{- $lastMap = $obj -}} + {{- $obj = index $obj $key -}} + {{- $key = $path -}} + {{- else -}} + {{- fail (cat "cannot iterate into path" $key "on type" (kindOf $obj)) -}} + {{- end -}} + {{- end -}} + + {{- $_ := set $patch (printf "%sKey" $opPathKey) $key -}} + {{- $_ := set $patch (printf "%sLastKey" $opPathKey) $lastKey -}} + {{- $_ = set $patch (printf "%sLastMap" $opPathKey) $lastMap -}} + {{- end -}} + + {{- if eq $patch.op "move" }} + {{- if and (ne $patch.path $patch.from) (hasPrefix (printf "%s/" $patch.path) (printf "%s/" $patch.from)) -}} + {{- fail (cat "from" $patch.from "may not be a child of path" $patch.path) -}} + {{- end -}} + {{- end -}} + + {{- if or (eq $patch.op "move") (eq $patch.op "copy") (eq $patch.op "test") }} + {{- $key := $patch.fromKey -}} + {{- $lastMap := $patch.fromLastMap -}} + {{- $lastKey := $patch.fromLastKey -}} + {{- $setKey := "value" -}} + {{- if eq $patch.op "test" }} + {{- $key = $patch.pathKey -}} + {{- $lastMap = $patch.pathLastMap -}} + {{- $lastKey = $patch.pathLastKey -}} + {{- $setKey = "testValue" -}} + {{- end -}} + {{- $obj := index $lastMap $lastKey -}} + + {{- if kindIs "map" $obj -}} + {{- if not (hasKey $obj $key) -}} + {{- fail (cat $key "does not exist") -}} + {{- end -}} + {{- $_ := set $patch $setKey (index $obj $key) -}} + + {{- else if kindIs "slice" $obj -}} + {{- $i := atoi $key -}} + {{- if ne $key (toString $i) -}} + {{- fail (cat "cannot convert" $key "to int") -}} + {{- end -}} + {{- if lt $i 0 -}} + {{- fail "slice index <0" -}} + {{- else if lt $i (len $obj) -}} + {{- $_ := set $patch $setKey (index $obj $i) -}} + {{- else -}} + {{- fail "slice index >= slice length" -}} + {{- end -}} + + {{- else -}} + {{- fail (cat "cannot" $patch.op $key "on type" (kindOf $obj)) -}} + {{- end -}} + {{- end -}} + + {{- if or (eq $patch.op "remove") (eq $patch.op "replace") (eq $patch.op "move") }} + {{- $key := $patch.pathKey -}} + {{- $lastMap := $patch.pathLastMap -}} + {{- $lastKey := $patch.pathLastKey -}} + {{- if eq $patch.op "move" }} + {{- $key = $patch.fromKey -}} + {{- $lastMap = $patch.fromLastMap -}} + {{- $lastKey = $patch.fromLastKey -}} + {{- end -}} + {{- $obj := index $lastMap $lastKey -}} + + {{- if kindIs "map" $obj -}} + {{- if not (hasKey $obj $key) -}} + {{- fail (cat $key "does not exist") -}} + {{- end -}} + {{- $_ := unset $obj $key -}} + + {{- else if kindIs "slice" $obj -}} + {{- $i := atoi $key -}} + {{- if ne $key (toString $i) -}} + {{- fail (cat "cannot convert" $key "to int") -}} + {{- end -}} + {{- if lt $i 0 -}} + {{- fail "slice index <0" -}} + {{- else if eq $i 0 -}} + {{- $_ := set $lastMap $lastKey (slice $obj 1) -}} + {{- else if lt $i (sub (len $obj) 1) -}} + {{- $_ := set $lastMap $lastKey (concat (slice $obj 0 $i) (slice $obj (add $i 1) (len $obj))) -}} + {{- else if eq $i (sub (len $obj) 1) -}} + {{- $_ := set $lastMap $lastKey (slice $obj 0 (sub (len $obj) 1)) -}} + {{- else -}} + {{- fail "slice index >= slice length" -}} + {{- end -}} + + {{- else -}} + {{- fail (cat "cannot" $patch.op $key "on type" (kindOf $obj)) -}} + {{- end -}} + {{- end -}} + + {{- if or (eq $patch.op "add") (eq $patch.op "replace") (eq $patch.op "move") (eq $patch.op "copy") }} + {{- $key := $patch.pathKey -}} + {{- $lastMap := $patch.pathLastMap -}} + {{- $lastKey := $patch.pathLastKey -}} + {{- $value := $patch.value -}} + {{- $obj := index $lastMap $lastKey -}} + + {{- if kindIs "map" $obj -}} + {{- $_ := set $obj $key $value -}} + + {{- else if kindIs "slice" $obj -}} + {{- $i := 0 -}} + {{- if eq $key "-" -}} + {{- $i = len $obj -}} + {{- else -}} + {{- $i = atoi $key -}} + {{- if ne $key (toString $i) -}} + {{- fail (cat "cannot convert" $key "to int") -}} + {{- end -}} + {{- end -}} + {{- if lt $i 0 -}} + {{- fail "slice index <0" -}} + {{- else if eq $i 0 -}} + {{- $_ := set $lastMap $lastKey (prepend $obj $value) -}} + {{- else if lt $i (len $obj) -}} + {{- $_ := set $lastMap $lastKey (concat (append (slice $obj 0 $i) $value) (slice $obj $i)) -}} + {{- else if eq $i (len $obj) -}} + {{- $_ := set $lastMap $lastKey (append $obj $value) -}} + {{- else -}} + {{- fail "slice index > slice length" -}} + {{- end -}} + + {{- else -}} + {{- fail (cat "cannot" $patch.op $key "on type" (kindOf $obj)) -}} + {{- end -}} + {{- end -}} + + {{- if eq $patch.op "test" }} + {{- if not (deepEqual $patch.value $patch.testValue) }} + {{- fail (cat "test failed, expected" (toJson $patch.value) "but got" (toJson $patch.testValue)) -}} + {{- end -}} + {{- end -}} + + {{- range $reSliceOp := $reSlice -}} + {{- $sliceObj := list -}} + {{- range $i := until (len $reSliceOp.mapObj) -}} + {{- $sliceObj = append $sliceObj (index $reSliceOp.mapObj (toString $i)) -}} + {{- end -}} + {{- $_ := set $reSliceOp.lastMap $reSliceOp.lastKey $sliceObj -}} + {{- end -}} + + {{- end -}} + {{- toJson $docContainer -}} +{{- end -}} diff --git a/charts/nats/nats/1.2.10/templates/_toPrettyRawJson.tpl b/charts/nats/nats/1.2.10/templates/_toPrettyRawJson.tpl new file mode 100644 index 000000000..612a62f9c --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/_toPrettyRawJson.tpl @@ -0,0 +1,28 @@ +{{- /* +toPrettyRawJson +input: interface{} valid JSON document +output: pretty raw JSON string +*/}} +{{- define "toPrettyRawJson" -}} + {{- include "toPrettyRawJsonStr" (toPrettyJson .) -}} +{{- end -}} + +{{- /* +toPrettyRawJsonStr +input: pretty JSON string +output: pretty raw JSON string +*/}} +{{- define "toPrettyRawJsonStr" -}} + {{- $s := + (regexReplaceAll "([^\\\\](?:\\\\\\\\)*)\\\\u003e" + (regexReplaceAll "([^\\\\](?:\\\\\\\\)*)\\\\u003c" + (regexReplaceAll "([^\\\\](?:\\\\\\\\)*)\\\\u0026" . "${1}&") + "${1}<") + "${1}>") + -}} + {{- if regexMatch "([^\\\\](?:\\\\\\\\)*)\\\\u00(26|3c|3e)" $s -}} + {{- include "toPrettyRawJsonStr" $s -}} + {{- else -}} + {{- $s -}} + {{- end -}} +{{- end -}} diff --git a/charts/nats/nats/1.2.10/templates/_tplYaml.tpl b/charts/nats/nats/1.2.10/templates/_tplYaml.tpl new file mode 100644 index 000000000..f42b9c168 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/_tplYaml.tpl @@ -0,0 +1,114 @@ +{{- /* +tplYaml +input: map with 2 keys: +- doc: interface{} +- ctx: context to pass to tpl function +output: JSON encoded map with 1 key: +- doc: interface{} with any keys called tpl or tplSpread values templated and replaced + +maps matching the following syntax will be templated and parsed as YAML +{ + $tplYaml: string +} + +maps matching the follow syntax will be templated, parsed as YAML, and spread into the parent map/slice +{ + $tplYamlSpread: string +} +*/}} +{{- define "tplYaml" -}} + {{- $patch := get (include "tplYamlItr" (dict "ctx" .ctx "parentKind" "" "parentPath" "" "path" "/" "value" .doc) | fromJson) "patch" -}} + {{- include "jsonpatch" (dict "doc" .doc "patch" $patch) -}} +{{- end -}} + +{{- /* +tplYamlItr +input: map with 4 keys: +- path: string JSONPath to current element +- parentKind: string kind of parent element +- parentPath: string JSONPath to parent element +- value: interface{} +- ctx: context to pass to tpl function +output: JSON encoded map with 1 key: +- patch: list of patches to apply in order to template +*/}} +{{- define "tplYamlItr" -}} + {{- $params := . -}} + {{- $kind := kindOf $params.value -}} + {{- $patch := list -}} + {{- $joinPath := $params.path -}} + {{- if eq $params.path "/" -}} + {{- $joinPath = "" -}} + {{- end -}} + {{- $joinParentPath := $params.parentPath -}} + {{- if eq $params.parentPath "/" -}} + {{- $joinParentPath = "" -}} + {{- end -}} + + {{- if eq $kind "slice" -}} + {{- $iAdj := 0 -}} + {{- range $i, $v := $params.value -}} + {{- $iPath := printf "%s/%d" $joinPath (add $i $iAdj) -}} + {{- $itrPatch := get (include "tplYamlItr" (dict "ctx" $params.ctx "parentKind" $kind "parentPath" $params.path "path" $iPath "value" $v) | fromJson) "patch" -}} + {{- $itrLen := len $itrPatch -}} + {{- if gt $itrLen 0 -}} + {{- $patch = concat $patch $itrPatch -}} + {{- if eq (get (index $itrPatch 0) "op") "remove" -}} + {{- $iAdj = add $iAdj (sub $itrLen 2) -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- else if eq $kind "map" -}} + {{- if and (eq (len $params.value) 1) (or (hasKey $params.value "$tplYaml") (hasKey $params.value "$tplYamlSpread")) -}} + {{- $tpl := get $params.value "$tplYaml" -}} + {{- $spread := false -}} + {{- if hasKey $params.value "$tplYamlSpread" -}} + {{- if eq $params.path "/" -}} + {{- fail "cannot $tplYamlSpread on root object" -}} + {{- end -}} + {{- $tpl = get $params.value "$tplYamlSpread" -}} + {{- $spread = true -}} + {{- end -}} + + {{- $res := tpl $tpl $params.ctx -}} + {{- $res = get (fromYaml (tpl "tpl: {{ nindent 2 .res }}" (merge (dict "res" $res) $params.ctx))) "tpl" -}} + + {{- if eq $spread false -}} + {{- $patch = append $patch (dict "op" "replace" "path" $params.path "value" $res) -}} + {{- else -}} + {{- $resKind := kindOf $res -}} + {{- if and (ne $resKind "invalid") (ne $resKind $params.parentKind) -}} + {{- fail (cat "can only $tplYamlSpread slice onto a slice or map onto a map; attempted to spread" $resKind "on" $params.parentKind "at path" $params.path) -}} + {{- end -}} + {{- $patch = append $patch (dict "op" "remove" "path" $params.path) -}} + {{- if eq $resKind "invalid" -}} + {{- /* no-op */ -}} + {{- else if eq $resKind "slice" -}} + {{- range $v := reverse $res -}} + {{- $patch = append $patch (dict "op" "add" "path" $params.path "value" $v) -}} + {{- end -}} + {{- else -}} + {{- range $k, $v := $res -}} + {{- $kPath := replace "~" "~0" $k -}} + {{- $kPath = replace "/" "~1" $kPath -}} + {{- $kPath = printf "%s/%s" $joinParentPath $kPath -}} + {{- $patch = append $patch (dict "op" "add" "path" $kPath "value" $v) -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- else -}} + {{- range $k, $v := $params.value -}} + {{- $kPath := replace "~" "~0" $k -}} + {{- $kPath = replace "/" "~1" $kPath -}} + {{- $kPath = printf "%s/%s" $joinPath $kPath -}} + {{- $itrPatch := get (include "tplYamlItr" (dict "ctx" $params.ctx "parentKind" $kind "parentPath" $params.path "path" $kPath "value" $v) | fromJson) "patch" -}} + {{- if gt (len $itrPatch) 0 -}} + {{- $patch = concat $patch $itrPatch -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- toJson (dict "patch" $patch) -}} +{{- end -}} diff --git a/charts/nats/nats/1.2.10/templates/config-map.yaml b/charts/nats/nats/1.2.10/templates/config-map.yaml new file mode 100644 index 000000000..b95afda20 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/config-map.yaml @@ -0,0 +1,4 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.configMap }} +{{- include "nats.loadMergePatch" (merge (dict "file" "config-map.yaml" "ctx" $) .) }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/extra-resources.yaml b/charts/nats/nats/1.2.10/templates/extra-resources.yaml new file mode 100644 index 000000000..c11f0085e --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/extra-resources.yaml @@ -0,0 +1,5 @@ +{{- include "nats.defaultValues" . }} +{{- range .Values.extraResources }} +--- +{{ . | toYaml }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/headless-service.yaml b/charts/nats/nats/1.2.10/templates/headless-service.yaml new file mode 100644 index 000000000..f11a83d13 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/headless-service.yaml @@ -0,0 +1,4 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.headlessService }} +{{- include "nats.loadMergePatch" (merge (dict "file" "headless-service.yaml" "ctx" $) .) }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/ingress.yaml b/charts/nats/nats/1.2.10/templates/ingress.yaml new file mode 100644 index 000000000..eccd73ffd --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/ingress.yaml @@ -0,0 +1,6 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.config.websocket.ingress }} +{{- if and .enabled .hosts $.Values.config.websocket.enabled $.Values.service.enabled $.Values.service.ports.websocket.enabled }} +{{- include "nats.loadMergePatch" (merge (dict "file" "ingress.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/nats-box/contents-secret.yaml b/charts/nats/nats/1.2.10/templates/nats-box/contents-secret.yaml new file mode 100644 index 000000000..db629bf7b --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/nats-box/contents-secret.yaml @@ -0,0 +1,10 @@ +{{- include "nats.defaultValues" . }} +{{- if .hasContentsSecret }} +{{- with .Values.natsBox }} +{{- if .enabled }} +{{- with .contentsSecret}} +{{- include "nats.loadMergePatch" (merge (dict "file" "nats-box/contents-secret.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/nats-box/contexts-secret.yaml b/charts/nats/nats/1.2.10/templates/nats-box/contexts-secret.yaml new file mode 100644 index 000000000..5ae20f45a --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/nats-box/contexts-secret.yaml @@ -0,0 +1,8 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.natsBox }} +{{- if .enabled }} +{{- with .contextsSecret}} +{{- include "nats.loadMergePatch" (merge (dict "file" "nats-box/contexts-secret/contexts-secret.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/nats-box/deployment.yaml b/charts/nats/nats/1.2.10/templates/nats-box/deployment.yaml new file mode 100644 index 000000000..a063332a2 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/nats-box/deployment.yaml @@ -0,0 +1,8 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.natsBox }} +{{- if .enabled }} +{{- with .deployment }} +{{- include "nats.loadMergePatch" (merge (dict "file" "nats-box/deployment/deployment.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/nats-box/service-account.yaml b/charts/nats/nats/1.2.10/templates/nats-box/service-account.yaml new file mode 100644 index 000000000..e11bdd363 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/nats-box/service-account.yaml @@ -0,0 +1,8 @@ +{{- include "nats.defaultValues" . }} +{{- if .Values.natsBox.enabled }} +{{- with .Values.natsBox.serviceAccount }} +{{- if .enabled }} +{{- include "nats.loadMergePatch" (merge (dict "file" "nats-box/service-account.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/pod-disruption-budget.yaml b/charts/nats/nats/1.2.10/templates/pod-disruption-budget.yaml new file mode 100644 index 000000000..911722629 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/pod-disruption-budget.yaml @@ -0,0 +1,6 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.podDisruptionBudget }} +{{- if .enabled }} +{{- include "nats.loadMergePatch" (merge (dict "file" "pod-disruption-budget.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/pod-monitor.yaml b/charts/nats/nats/1.2.10/templates/pod-monitor.yaml new file mode 100644 index 000000000..0e42a43a5 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/pod-monitor.yaml @@ -0,0 +1,8 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.promExporter }} +{{- if and .enabled .podMonitor.enabled }} +{{- with .podMonitor }} +{{- include "nats.loadMergePatch" (merge (dict "file" "pod-monitor.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/service-account.yaml b/charts/nats/nats/1.2.10/templates/service-account.yaml new file mode 100644 index 000000000..6c763bd3e --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/service-account.yaml @@ -0,0 +1,6 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.serviceAccount }} +{{- if .enabled }} +{{- include "nats.loadMergePatch" (merge (dict "file" "service-account.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/service.yaml b/charts/nats/nats/1.2.10/templates/service.yaml new file mode 100644 index 000000000..04b0b37e7 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/service.yaml @@ -0,0 +1,6 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.service }} +{{- if .enabled }} +{{- include "nats.loadMergePatch" (merge (dict "file" "service.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/stateful-set.yaml b/charts/nats/nats/1.2.10/templates/stateful-set.yaml new file mode 100644 index 000000000..bb198323e --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/stateful-set.yaml @@ -0,0 +1,4 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.statefulSet }} +{{- include "nats.loadMergePatch" (merge (dict "file" "stateful-set/stateful-set.yaml" "ctx" $) .) }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/templates/tests/request-reply.yaml b/charts/nats/nats/1.2.10/templates/tests/request-reply.yaml new file mode 100644 index 000000000..3e06edc08 --- /dev/null +++ b/charts/nats/nats/1.2.10/templates/tests/request-reply.yaml @@ -0,0 +1,37 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.natsBox | deepCopy }} +{{- $natsBox := . }} +{{- if .enabled -}} +apiVersion: v1 +kind: Pod +{{- with .container }} +{{- $_ := set . "merge" (dict + "args" (list + "sh" + "-ec" + "nats reply --echo echo & pid=\"$!\"; sleep 1; nats request echo hi > /tmp/resp; kill \"$pid\"; wait; grep -qF hi /tmp/resp" + ) +) }} +{{- $_ := set . "patch" list }} +{{- end }} +{{- with .podTemplate }} +{{- $_ := set . "merge" (dict + "metadata" (dict + "name" (printf "%s-test-request-reply" $.Values.statefulSet.name) + "labels" (dict + "app.kubernetes.io/component" "test-request-reply" + ) + "annotations" (dict + "helm.sh/hook" "test" + "helm.sh/hook-delete-policy" "before-hook-creation,hook-succeeded" + ) + ) + "spec" (dict + "restartPolicy" "Never" + ) +) }} +{{- $_ := set . "patch" list }} +{{ include "nats.loadMergePatch" (merge (dict "file" "nats-box/deployment/pod-template.yaml" "ctx" (merge (dict "Values" (dict "natsBox" $natsBox)) $)) .) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/nats/nats/1.2.10/values.yaml b/charts/nats/nats/1.2.10/values.yaml new file mode 100644 index 000000000..57dfcc02b --- /dev/null +++ b/charts/nats/nats/1.2.10/values.yaml @@ -0,0 +1,669 @@ +################################################################################ +# Global options +################################################################################ +global: + image: + # global image pull policy to use for all container images in the chart + # can be overridden by individual image pullPolicy + pullPolicy: + # global list of secret names to use as image pull secrets for all pod specs in the chart + # secrets must exist in the same namespace + # https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + pullSecretNames: [] + # global registry to use for all container images in the chart + # can be overridden by individual image registry + registry: + + # global labels will be applied to all resources deployed by the chart + labels: {} + +################################################################################ +# Common options +################################################################################ +# override name of the chart +nameOverride: +# override full name of the chart+release +fullnameOverride: +# override the namespace that resources are installed into +namespaceOverride: + +# reference a common CA Certificate or Bundle in all nats config `tls` blocks and nats-box contexts +# note: `tls.verify` still must be set in the appropriate nats config `tls` blocks to require mTLS +tlsCA: + enabled: false + # set configMapName in order to mount an existing configMap to dir + configMapName: + # set secretName in order to mount an existing secretName to dir + secretName: + # directory to mount the configMap or secret to + dir: /etc/nats-ca-cert + # key in the configMap or secret that contains the CA Certificate or Bundle + key: ca.crt + +################################################################################ +# NATS Stateful Set and associated resources +################################################################################ + +############################################################ +# NATS config +############################################################ +config: + cluster: + enabled: false + port: 6222 + # must be 2 or higher when jetstream is enabled + replicas: 3 + + # apply to generated route URLs that connect to other pods in the StatefulSet + routeURLs: + # if both user and password are set, they will be added to route URLs + # and the cluster authorization block + user: + password: + # set to true to use FQDN in route URLs + useFQDN: false + k8sClusterDomain: cluster.local + + tls: + enabled: false + # set secretName in order to mount an existing secret to dir + secretName: + dir: /etc/nats-certs/cluster + cert: tls.crt + key: tls.key + # merge or patch the tls config + # https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls + merge: {} + patch: [] + + # merge or patch the cluster config + # https://docs.nats.io/running-a-nats-service/configuration/clustering/cluster_config + merge: {} + patch: [] + + jetstream: + enabled: false + + fileStore: + enabled: true + dir: /data + + ############################################################ + # stateful set -> volume claim templates -> jetstream pvc + ############################################################ + pvc: + enabled: true + size: 10Gi + storageClassName: + + # merge or patch the jetstream pvc + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#persistentvolumeclaim-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-js" + name: + + # defaults to the PVC size + maxSize: + + memoryStore: + enabled: false + # ensure that container has a sufficient memory limit greater than maxSize + maxSize: 1Gi + + # merge or patch the jetstream config + # https://docs.nats.io/running-a-nats-service/configuration#jetstream + merge: {} + patch: [] + + nats: + port: 4222 + tls: + enabled: false + # set secretName in order to mount an existing secret to dir + secretName: + dir: /etc/nats-certs/nats + cert: tls.crt + key: tls.key + # merge or patch the tls config + # https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls + merge: {} + patch: [] + + leafnodes: + enabled: false + port: 7422 + tls: + enabled: false + # set secretName in order to mount an existing secret to dir + secretName: + dir: /etc/nats-certs/leafnodes + cert: tls.crt + key: tls.key + # merge or patch the tls config + # https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls + merge: {} + patch: [] + + # merge or patch the leafnodes config + # https://docs.nats.io/running-a-nats-service/configuration/leafnodes/leafnode_conf + merge: {} + patch: [] + + websocket: + enabled: false + port: 8080 + tls: + enabled: false + # set secretName in order to mount an existing secret to dir + secretName: + dir: /etc/nats-certs/websocket + cert: tls.crt + key: tls.key + # merge or patch the tls config + # https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls + merge: {} + patch: [] + + ############################################################ + # ingress + ############################################################ + # service must be enabled also + ingress: + enabled: false + # must contain at least 1 host otherwise ingress will not be created + hosts: [] + path: / + pathType: Exact + # sets to the ingress class name + className: + # set to an existing secret name to enable TLS on the ingress; applies to all hosts + tlsSecretName: + + # merge or patch the ingress + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#ingress-v1-networking-k8s-io + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-ws" + name: + + # merge or patch the websocket config + # https://docs.nats.io/running-a-nats-service/configuration/websocket/websocket_conf + merge: {} + patch: [] + + mqtt: + enabled: false + port: 1883 + tls: + enabled: false + # set secretName in order to mount an existing secret to dir + secretName: + dir: /etc/nats-certs/mqtt + cert: tls.crt + key: tls.key + # merge or patch the tls config + # https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls + merge: {} + patch: [] + + # merge or patch the mqtt config + # https://docs.nats.io/running-a-nats-service/configuration/mqtt/mqtt_config + merge: {} + patch: [] + + gateway: + enabled: false + port: 7222 + tls: + enabled: false + # set secretName in order to mount an existing secret to dir + secretName: + dir: /etc/nats-certs/gateway + cert: tls.crt + key: tls.key + # merge or patch the tls config + # https://docs.nats.io/running-a-nats-service/configuration/securing_nats/tls + merge: {} + patch: [] + + # merge or patch the gateway config + # https://docs.nats.io/running-a-nats-service/configuration/gateways/gateway#gateway-configuration-block + merge: {} + patch: [] + + monitor: + enabled: true + port: 8222 + tls: + # config.nats.tls must be enabled also + # when enabled, monitoring port will use HTTPS with the options from config.nats.tls + enabled: false + + profiling: + enabled: false + port: 65432 + + resolver: + enabled: false + dir: /data/resolver + + ############################################################ + # stateful set -> volume claim templates -> resolver pvc + ############################################################ + pvc: + enabled: true + size: 1Gi + storageClassName: + + # merge or patch the pvc + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#persistentvolumeclaim-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-resolver" + name: + + # merge or patch the resolver + # https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/jwt/resolver + merge: {} + patch: [] + + # adds a prefix to the server name, which defaults to the pod name + # helpful for ensuring server name is unique in a super cluster + serverNamePrefix: "" + + # merge or patch the nats config + # https://docs.nats.io/running-a-nats-service/configuration + # following special rules apply + # 1. strings that start with << and end with >> will be unquoted + # use this for variables and numbers with units + # 2. keys ending in $include will be switched to include directives + # keys are sorted alphabetically, use prefix before $includes to control includes ordering + # paths should be relative to /etc/nats-config/nats.conf + # example: + # + # merge: + # $include: ./my-config.conf + # zzz$include: ./my-config-last.conf + # server_name: nats + # authorization: + # token: << $TOKEN >> + # jetstream: + # max_memory_store: << 1GB >> + # + # will yield the config: + # { + # include ./my-config.conf; + # "authorization": { + # "token": $TOKEN + # }, + # "jetstream": { + # "max_memory_store": 1GB + # }, + # "server_name": "nats", + # include ./my-config-last.conf; + # } + merge: {} + patch: [] + +############################################################ +# stateful set -> pod template -> nats container +############################################################ +container: + image: + repository: nats + tag: 2.10.25-alpine + pullPolicy: + registry: + + # container port options + # must be enabled in the config section also + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#containerport-v1-core + ports: + nats: {} + leafnodes: {} + websocket: {} + mqtt: {} + cluster: {} + gateway: {} + monitor: {} + profiling: {} + + # map with key as env var name, value can be string or map + # example: + # + # env: + # GOMEMLIMIT: 7GiB + # TOKEN: + # valueFrom: + # secretKeyRef: + # name: nats-auth + # key: token + env: {} + + # merge or patch the container + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#container-v1-core + merge: {} + patch: [] + +############################################################ +# stateful set -> pod template -> reloader container +############################################################ +reloader: + enabled: true + image: + repository: natsio/nats-server-config-reloader + tag: 0.16.1 + pullPolicy: + registry: + + # env var map, see nats.env for an example + env: {} + + # all nats container volume mounts with the following prefixes + # will be mounted into the reloader container + natsVolumeMountPrefixes: + - /etc/ + + # merge or patch the container + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#container-v1-core + merge: {} + patch: [] + +############################################################ +# stateful set -> pod template -> prom-exporter container +############################################################ +# config.monitor must be enabled +promExporter: + enabled: false + image: + repository: natsio/prometheus-nats-exporter + tag: 0.16.0 + pullPolicy: + registry: + + port: 7777 + # env var map, see nats.env for an example + env: {} + + # merge or patch the container + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#container-v1-core + merge: {} + patch: [] + + ############################################################ + # prometheus pod monitor + ############################################################ + podMonitor: + enabled: false + + # merge or patch the pod monitor + # https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.PodMonitor + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}" + name: + + +############################################################ +# service +############################################################ +service: + enabled: true + + # service port options + # additional boolean field enable to control whether port is exposed in the service + # must be enabled in the config section also + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#serviceport-v1-core + ports: + nats: + enabled: true + leafnodes: + enabled: true + websocket: + enabled: true + mqtt: + enabled: true + cluster: + enabled: false + gateway: + enabled: false + monitor: + enabled: false + profiling: + enabled: false + + # merge or patch the service + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#service-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}" + name: + +############################################################ +# other nats extension points +############################################################ + +# stateful set +statefulSet: + # merge or patch the stateful set + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#statefulset-v1-apps + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}" + name: + +# stateful set -> pod template +podTemplate: + # adds a hash of the ConfigMap as a pod annotation + # this will cause the StatefulSet to roll when the ConfigMap is updated + configChecksumAnnotation: true + + # map of topologyKey: topologySpreadConstraint + # labelSelector will be added to match StatefulSet pods + # + # topologySpreadConstraints: + # kubernetes.io/hostname: + # maxSkew: 1 + # + topologySpreadConstraints: {} + + # merge or patch the pod template + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#pod-v1-core + merge: {} + patch: [] + +# headless service +headlessService: + # merge or patch the headless service + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#service-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-headless" + name: + +# config map +configMap: + # merge or patch the config map + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#configmap-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-config" + name: + +# pod disruption budget +podDisruptionBudget: + enabled: true + # merge or patch the pod disruption budget + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#poddisruptionbudget-v1-policy + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}" + name: + +# service account +serviceAccount: + enabled: false + # merge or patch the service account + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#serviceaccount-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}" + name: + + +############################################################ +# natsBox +# +# NATS Box Deployment and associated resources +############################################################ +natsBox: + enabled: true + + ############################################################ + # NATS contexts + ############################################################ + contexts: + default: + creds: + # set contents in order to create a secret with the creds file contents + contents: + # set secretName in order to mount an existing secret to dir + secretName: + # defaults to /etc/nats-creds/ + dir: + key: nats.creds + nkey: + # set contents in order to create a secret with the nkey file contents + contents: + # set secretName in order to mount an existing secret to dir + secretName: + # defaults to /etc/nats-nkeys/ + dir: + key: nats.nk + # used to connect with client certificates + tls: + # set secretName in order to mount an existing secret to dir + secretName: + # defaults to /etc/nats-certs/ + dir: + cert: tls.crt + key: tls.key + + # merge or patch the context + # https://docs.nats.io/using-nats/nats-tools/nats_cli#nats-contexts + merge: {} + patch: [] + + # name of context to select by default + defaultContextName: default + + ############################################################ + # deployment -> pod template -> nats-box container + ############################################################ + container: + image: + repository: natsio/nats-box + tag: 0.16.0 + pullPolicy: + registry: + + # env var map, see nats.env for an example + env: {} + + # merge or patch the container + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#container-v1-core + merge: {} + patch: [] + + ############################################################ + # other nats-box extension points + ############################################################ + + # deployment + deployment: + # merge or patch the deployment + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#deployment-v1-apps + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-box" + name: + + # deployment -> pod template + podTemplate: + # merge or patch the pod template + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#pod-v1-core + merge: {} + patch: [] + + # contexts secret + contextsSecret: + # merge or patch the context secret + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#secret-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-box-contexts" + name: + + # contents secret + contentsSecret: + # merge or patch the contents secret + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#secret-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-box-contents" + name: + + # service account + serviceAccount: + enabled: false + # merge or patch the service account + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#serviceaccount-v1-core + merge: {} + patch: [] + # defaults to "{{ include "nats.fullname" $ }}-box" + name: + + +################################################################################ +# Extra user-defined resources +################################################################################ +# +# add arbitrary user-generated resources +# example: +# +# config: +# websocket: +# enabled: true +# extraResources: +# - apiVersion: networking.istio.io/v1beta1 +# kind: VirtualService +# metadata: +# name: +# $tplYaml: > +# {{ include "nats.fullname" $ | quote }} +# labels: +# $tplYaml: | +# {{ include "nats.labels" $ }} +# spec: +# hosts: +# - demo.nats.io +# gateways: +# - my-gateway +# http: +# - name: default +# match: +# - name: root +# uri: +# exact: / +# route: +# - destination: +# host: +# $tplYaml: > +# {{ .Values.service.name | quote }} +# port: +# number: +# $tplYaml: > +# {{ .Values.config.websocket.port }} +# +extraResources: [] diff --git a/charts/nats/nats/1.2.9/Chart.yaml b/charts/nats/nats/1.2.9/Chart.yaml index 5715f323c..aa180b137 100644 --- a/charts/nats/nats/1.2.9/Chart.yaml +++ b/charts/nats/nats/1.2.9/Chart.yaml @@ -1,7 +1,6 @@ annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: NATS Server - catalog.cattle.io/featured: "5" catalog.cattle.io/kube-version: '>=1.16-0' catalog.cattle.io/release-name: nats apiVersion: v2 diff --git a/charts/speedscale/speedscale-operator/2.3.149/.helmignore b/charts/speedscale/speedscale-operator/2.3.149/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/speedscale/speedscale-operator/2.3.149/Chart.yaml b/charts/speedscale/speedscale-operator/2.3.149/Chart.yaml new file mode 100644 index 000000000..54504934a --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/Chart.yaml @@ -0,0 +1,27 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Speedscale Operator + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: speedscale-operator +apiVersion: v1 +appVersion: 2.3.149 +description: Stress test your APIs with real world scenarios. Collect and replay + traffic without scripting. +home: https://speedscale.com +icon: file://assets/icons/speedscale-operator.png +keywords: +- speedscale +- test +- testing +- regression +- reliability +- load +- replay +- network +- traffic +kubeVersion: '>= 1.17.0-0' +maintainers: +- email: support@speedscale.com + name: Speedscale Support +name: speedscale-operator +version: 2.3.149 diff --git a/charts/speedscale/speedscale-operator/2.3.149/LICENSE b/charts/speedscale/speedscale-operator/2.3.149/LICENSE new file mode 100644 index 000000000..b78723d62 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Speedscale + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/speedscale/speedscale-operator/2.3.149/README.md b/charts/speedscale/speedscale-operator/2.3.149/README.md new file mode 100644 index 000000000..6ca25eed9 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/README.md @@ -0,0 +1,111 @@ +![GitHub Tag](https://img.shields.io/github/v/tag/speedscale/operator-helm) + + +# Speedscale Operator + +The [Speedscale](https://www.speedscale.com) Operator is a [Kubernetes operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) +that watches for deployments to be applied to the cluster and takes action based on annotations. The operator +can inject a proxy to capture traffic into or out of applications, or setup an isolation test environment around +a deployment for testing. The operator itself is a deployment that will be always present on the cluster once +the helm chart is installed. + +## Prerequisites + +- Kubernetes 1.20+ +- Helm 3+ +- Appropriate [network and firewall configuration](https://docs.speedscale.com/reference/networking) for Speedscale cloud and webhook traffic + +## Get Repo Info + +```bash +helm repo add speedscale https://speedscale.github.io/operator-helm/ +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +An API key is required. Sign up for a [free Speedscale trial](https://speedscale.com/free-trial/) if you do not have one. + +```bash +helm install speedscale-operator speedscale/speedscale-operator \ + -n speedscale \ + --create-namespace \ + --set apiKey= \ + --set clusterName= +``` + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +### Pre-install job failure + +We use pre-install job to check provided API key and provision some of the required resources. + +If the job failed during the installation, you'll see the following error during install: + +``` +Error: INSTALLATION FAILED: failed pre-install: job failed: BackoffLimitExceeded +``` + +You can inspect the logs using this command: + +```bash +kubectl -n speedscale logs job/speedscale-operator-pre-install +``` + +After fixing the error, uninstall the helm release, delete the failed job +and try installing again: + +```bash +helm -n speedscale uninstall speedscale-operator +kubectl -n speedscale delete job speedscale-operator-pre-install +``` + +## Uninstall Chart + +```bash +helm -n speedscale uninstall speedscale-operator +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +CRDs created by this chart are not removed by default and should be manually cleaned up: + +```bash +kubectl delete crd trafficreplays.speedscale.com +``` + +## Upgrading Chart + +```bash +helm repo update +helm -n speedscale upgrade speedscale-operator speedscale/speedscale-operator +``` + +Resources capturing traffic will need to be rolled to pick up the latest +Speedscale sidecar. Use the rollout restart command for each namespace and +resource type: + +```bash +kubectl -n rollout restart deployment +``` + +With Helm v3, CRDs created by this chart are not updated by default +and should be manually updated. +Consult also the [Helm Documentation on CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions). + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading an existing Release to a new version + +A major chart version change (like v1.2.3 -> v2.0.0) indicates that there is an +incompatible breaking change needing manual actions. + + +## Help + +Speedscale docs information available at [docs.speedscale.com](https://docs.speedscale.com) or join us +on the [Speedscale community Slack](https://join.slack.com/t/speedscalecommunity/shared_invite/zt-x5rcrzn4-XHG1QqcHNXIM~4yozRrz8A)! diff --git a/charts/speedscale/speedscale-operator/2.3.149/app-readme.md b/charts/speedscale/speedscale-operator/2.3.149/app-readme.md new file mode 100644 index 000000000..6ca25eed9 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/app-readme.md @@ -0,0 +1,111 @@ +![GitHub Tag](https://img.shields.io/github/v/tag/speedscale/operator-helm) + + +# Speedscale Operator + +The [Speedscale](https://www.speedscale.com) Operator is a [Kubernetes operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) +that watches for deployments to be applied to the cluster and takes action based on annotations. The operator +can inject a proxy to capture traffic into or out of applications, or setup an isolation test environment around +a deployment for testing. The operator itself is a deployment that will be always present on the cluster once +the helm chart is installed. + +## Prerequisites + +- Kubernetes 1.20+ +- Helm 3+ +- Appropriate [network and firewall configuration](https://docs.speedscale.com/reference/networking) for Speedscale cloud and webhook traffic + +## Get Repo Info + +```bash +helm repo add speedscale https://speedscale.github.io/operator-helm/ +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +An API key is required. Sign up for a [free Speedscale trial](https://speedscale.com/free-trial/) if you do not have one. + +```bash +helm install speedscale-operator speedscale/speedscale-operator \ + -n speedscale \ + --create-namespace \ + --set apiKey= \ + --set clusterName= +``` + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +### Pre-install job failure + +We use pre-install job to check provided API key and provision some of the required resources. + +If the job failed during the installation, you'll see the following error during install: + +``` +Error: INSTALLATION FAILED: failed pre-install: job failed: BackoffLimitExceeded +``` + +You can inspect the logs using this command: + +```bash +kubectl -n speedscale logs job/speedscale-operator-pre-install +``` + +After fixing the error, uninstall the helm release, delete the failed job +and try installing again: + +```bash +helm -n speedscale uninstall speedscale-operator +kubectl -n speedscale delete job speedscale-operator-pre-install +``` + +## Uninstall Chart + +```bash +helm -n speedscale uninstall speedscale-operator +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +CRDs created by this chart are not removed by default and should be manually cleaned up: + +```bash +kubectl delete crd trafficreplays.speedscale.com +``` + +## Upgrading Chart + +```bash +helm repo update +helm -n speedscale upgrade speedscale-operator speedscale/speedscale-operator +``` + +Resources capturing traffic will need to be rolled to pick up the latest +Speedscale sidecar. Use the rollout restart command for each namespace and +resource type: + +```bash +kubectl -n rollout restart deployment +``` + +With Helm v3, CRDs created by this chart are not updated by default +and should be manually updated. +Consult also the [Helm Documentation on CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions). + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading an existing Release to a new version + +A major chart version change (like v1.2.3 -> v2.0.0) indicates that there is an +incompatible breaking change needing manual actions. + + +## Help + +Speedscale docs information available at [docs.speedscale.com](https://docs.speedscale.com) or join us +on the [Speedscale community Slack](https://join.slack.com/t/speedscalecommunity/shared_invite/zt-x5rcrzn4-XHG1QqcHNXIM~4yozRrz8A)! diff --git a/charts/speedscale/speedscale-operator/2.3.149/questions.yaml b/charts/speedscale/speedscale-operator/2.3.149/questions.yaml new file mode 100644 index 000000000..29aee3895 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/questions.yaml @@ -0,0 +1,9 @@ +questions: +- variable: apiKey + default: "fffffffffffffffffffffffffffffffffffffffffffff" + description: "An API key is required to connect to the Speedscale cloud." + required: true + type: string + label: API Key + group: Authentication + diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/NOTES.txt b/charts/speedscale/speedscale-operator/2.3.149/templates/NOTES.txt new file mode 100644 index 000000000..cabb59b17 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/NOTES.txt @@ -0,0 +1,12 @@ +Thank you for installing the Speedscale Operator! + +Next you'll need to add the Speedscale Proxy Sidecar to your deployments. +See https://docs.speedscale.com/setup/sidecar/install/ + +If upgrading use the rollout restart command for each namespace and resource +type to ensure Speedscale sidecars are updated: + + kubectl -n rollout restart deployment + +Once your deployment is running the sidecar your service will show up on +https://app.speedscale.com/. diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/admission.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/admission.yaml new file mode 100644 index 000000000..301748a61 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/admission.yaml @@ -0,0 +1,209 @@ +{{- $cacrt := "" -}} +{{- $crt := "" -}} +{{- $key := "" -}} +{{- $s := (lookup "v1" "Secret" .Release.Namespace "speedscale-webhook-certs") -}} +{{- if $s -}} +{{- $cacrt = index $s.data "ca.crt" | default (index $s.data "tls.crt") | b64dec -}} +{{- $crt = index $s.data "tls.crt" | b64dec -}} +{{- $key = index $s.data "tls.key" | b64dec -}} +{{ else }} +{{- $altNames := list ( printf "speedscale-operator.%s" .Release.Namespace ) ( printf "speedscale-operator.%s.svc" .Release.Namespace ) -}} +{{- $ca := genCA "speedscale-operator" 3650 -}} +{{- $cert := genSignedCert "speedscale-operator" nil $altNames 3650 $ca -}} +{{- $cacrt = $ca.Cert -}} +{{- $crt = $cert.Cert -}} +{{- $key = $cert.Key -}} +{{- end -}} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /mutate + failurePolicy: Ignore + name: sidecar.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + reinvocationPolicy: IfNeeded + rules: + - apiGroups: + - apps + - batch + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - deployments + - statefulsets + - daemonsets + - jobs + - replicasets + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - pods + - apiGroups: + - argoproj.io + apiVersions: + - "*" + operations: + - CREATE + - UPDATE + - DELETE + resources: + - rollouts + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator-replay + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /mutate-speedscale-com-v1-trafficreplay + failurePolicy: Fail + name: replay.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + rules: + - apiGroups: + - speedscale.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - trafficreplays + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator-replay + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /validate-speedscale-com-v1-trafficreplay + failurePolicy: Fail + name: replay.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + rules: + - apiGroups: + - speedscale.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - trafficreplays + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-webhook-certs + namespace: {{ .Release.Namespace }} +type: kubernetes.io/tls +data: + ca.crt: {{ $cacrt | b64enc }} + tls.crt: {{ $crt | b64enc }} + tls.key: {{ $key | b64enc }} diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/configmap.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/configmap.yaml new file mode 100644 index 000000000..eb963d3c0 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/configmap.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +data: + CLUSTER_NAME: {{ .Values.clusterName }} + IMAGE_PULL_POLICY: {{ .Values.image.pullPolicy }} + IMAGE_PULL_SECRETS: "" + IMAGE_REGISTRY: {{ .Values.image.registry }} + IMAGE_TAG: {{ .Values.image.tag }} + INSTANCE_ID: '{{- $cm := (lookup "v1" "ConfigMap" .Release.Namespace "speedscale-operator") -}}{{ if $cm }}{{ $cm.data.INSTANCE_ID }}{{ else }}{{ ( printf "%s-%s" .Values.clusterName uuidv4 ) }}{{ end }}' + LOG_LEVEL: {{ .Values.logLevel }} + SPEEDSCALE_DLP_CONFIG: {{ .Values.dlp.config }} + SPEEDSCALE_FILTER_RULE: {{ .Values.filterRule }} + TELEMETRY_INTERVAL: 60s + WITH_DLP: {{ .Values.dlp.enabled | quote }} + WITH_INSPECTOR: {{ .Values.dashboardAccess | quote }} + API_KEY_SECRET_NAME: {{ .Values.apiKeySecret | quote }} + DEPLOY_DEMO: {{ .Values.deployDemo | quote }} + GLOBAL_ANNOTATIONS: {{ .Values.globalAnnotations | toJson | quote }} + GLOBAL_LABELS: {{ .Values.globalLabels | toJson | quote }} + {{- if .Values.http_proxy }} + HTTP_PROXY: {{ .Values.http_proxy }} + {{- end }} + {{- if .Values.https_proxy }} + HTTPS_PROXY: {{ .Values.https_proxy }} + {{- end }} + {{- if .Values.no_proxy }} + NO_PROXY: {{ .Values.no_proxy }} + {{- end }} + PRIVILEGED_SIDECARS: {{ .Values.privilegedSidecars | quote }} + DISABLE_SMARTDNS: {{ .Values.disableSidecarSmartReverseDNS | quote }} + SIDECAR_CONFIG: {{ .Values.sidecar | toJson | quote }} + FORWARDER_CONFIG: {{ .Values.forwarder | toJson | quote }} + TEST_PREP_TIMEOUT: {{ .Values.operator.test_prep_timeout }} + CONTROL_PLANE_TIMEOUT: {{ .Values.operator.control_plane_timeout }} diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/crds/agenttasks.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/crds/agenttasks.yaml new file mode 100644 index 000000000..4d56f8950 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/crds/agenttasks.yaml @@ -0,0 +1,161 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + creationTimestamp: null + name: agenttasks.speedscale.com +spec: + group: speedscale.com + names: + kind: AgentTask + listKind: AgentTaskList + plural: agenttasks + shortNames: + - sat + singular: agenttask + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.active + name: Active + type: boolean + - jsonPath: .spec.mode + name: Mode + type: string + - jsonPath: .status.conditions[-1:].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: AgentTask is the Schema for the agenttasks API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec is the desired state of the AgentTask. + type: object + status: + default: + observedGeneration: -1 + description: Status is the current state of the AgentTask. + properties: + active: + description: Active indicates whether this agent task is currently + underway or not. + type: boolean + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + reconcileFailures: + description: |- + ReconcileFailures is the number of times the agent task controller + experienced an error during the reconciliation process. The agent + task will be deleted if too many errors occur. + format: int64 + type: integer + reportID: + description: The ID of the agent report associated with this agent + task. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/crds/trafficreplays.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/crds/trafficreplays.yaml new file mode 100644 index 000000000..5a1158306 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/crds/trafficreplays.yaml @@ -0,0 +1,522 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + creationTimestamp: null + name: trafficreplays.speedscale.com +spec: + group: speedscale.com + names: + kind: TrafficReplay + listKind: TrafficReplayList + plural: trafficreplays + shortNames: + - replay + singular: trafficreplay + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.active + name: Active + type: boolean + - jsonPath: .spec.mode + name: Mode + type: string + - jsonPath: .status.conditions[-1:].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: TrafficReplay is the Schema for the trafficreplays API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of TrafficReplay. + properties: + buildTag: + description: |- + BuildTag links a unique tag, build hash, etc. to the generated + traffic replay report. That way you can connect the report results to the + version of the code that was tested. + type: string + cleanup: + description: |- + Cleanup is the name of cleanup mode used for this TrafficReplay. Set to + "none" to leave resources in the state they were during the replay. The + default mode "inventory" will revert the environment to the state it was + before the replay. + enum: + - inventory + - all + - none + type: string + collectLogs: + description: 'DEPRECATED: use TestReport.ActualConfig.Cluster.CollectLogs' + type: boolean + configChecksum: + description: |- + ConfigChecksum, managed my the operator, is the SHA1 checksum of the + configuration. + type: string + customURL: + description: |- + CustomURL specifies a custom URL to send *ALL* traffic to. Use + Workload.CustomURI to send traffic to a specific URL for only that + workload. + type: string + generatorLowData: + description: |- + GeneratorLowData forces the generator into a high + efficiency/low data output mode. This is ideal for high volume + performance tests. Defaults to false. + DEPRECATED + type: boolean + mode: + description: Mode is the name of replay mode used for this TrafficReplay. + enum: + - full-replay + - responder-only + - generator-only + type: string + needsReport: + description: 'DEPRECATED: replays always create reports' + type: boolean + proxyMode: + description: |- + ProxyMode defines proxy operational mode used with injected sidecar. + DEPRECATED + type: string + responderLowData: + description: |- + ResponderLowData forces the responder into a high + efficiency/low data output mode. This is ideal for high volume + performance tests. Defaults to false. + DEPRECATED + type: boolean + secretRefs: + description: |- + SecretRefs hold the references to the secrets which contain + various secrets like (e.g. short-lived JWTs to be used by the generator + for authorization with HTTP calls). + items: + description: |- + LocalObjectReference contains enough information to locate the referenced + Kubernetes resource object. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: array + sidecar: + description: |- + Sidecar defines sidecar specific configuration. + DEPRECATED: use Workloads + properties: + inject: + description: 'DEPRECATED: do not use' + type: boolean + patch: + description: Patch is .yaml file patch for the Workload + format: byte + type: string + tls: + properties: + in: + description: In provides configuration for sidecar inbound + TLS. + properties: + private: + description: Private is the filename of the TLS inbound + private key. + type: string + public: + description: Public is the filename of the TLS inbound + public key. + type: string + secret: + description: Secret is a secret with the TLS keys to use + for inbound traffic. + type: string + type: object + mutual: + description: Mutual provides configuration for sidecar mutual + TLS. + properties: + private: + description: Private is the filename of the mutual TLS + private key. + type: string + public: + description: Public is the filename of the mutual TLS + public key. + type: string + secret: + description: Secret is a secret with the mutual TLS keys. + type: string + type: object + out: + description: |- + Out enables or disables TLS out on the + sidecar during replay. + type: boolean + type: object + type: object + snapshotID: + description: |- + SnapshotID is the id of the traffic snapshot for this + TrafficReplay. + type: string + testConfigID: + description: |- + TestConfigID is the id of the replay configuration to be used + by the generator and responder for the TrafficReplay. + type: string + timeout: + description: |- + Timeout is the time to wait for replay test to finish. Defaults + to value of the `TIMEOUT` setting of the operator. + type: string + ttlAfterReady: + description: |- + TTLAfterReady provides a TTL (time to live) mechanism to limit + the lifetime of TrafficReplay object that have finished the execution and + reached its final state (either complete or failed). + type: string + workloadRef: + description: |- + WorkloadRef is the reference to the target workload (SUT) for + TrafficReplay. The operations will be performed in the namespace of the + target object. + DEPRECATED: use Workloads + properties: + apiVersion: + description: API version of the referenced object. + type: string + kind: + description: Kind of the referenced object. Defaults to "Deployment". + type: string + name: + description: |- + Name of the referenced object. Required when defining for a test unless a + custom URI is provided. Always required when defining mocks. + type: string + namespace: + description: Namespace of the referenced object. Defaults to the + TrafficReplay namespace. + type: string + required: + - name + type: object + workloads: + description: |- + Workloads define target workloads (SUT) for a TrafficReplay. Many + workloads may be provided, or none. Workloads may be modified and + restarted during replay to configure communication with a responder. + items: + description: |- + Workload represents a Kubernetes workload to be targeted during replay and + associated settings. + properties: + customURI: + description: |- + CustomURI will be target of the traffic instead of directly targeting + workload. This is required if a Ref is not specified. + type: string + inTrafficKey: + description: 'DEPRECATED: use Tests' + type: string + inTrafficKeys: + description: 'DEPRECATED: use Tests' + items: + type: string + type: array + mocks: + description: |- + Mocks are strings used to identify slices of outbound snapshot traffic to + mock for this workload and maps directly to a snapshot's `OutTraffic` + field. Snapshot egress traffic can be split across multiple slices where + each slice contains part of the traffic. A workload may specify multiple + keys and multiple workloads may specify the same key. + + + Only the traffic slices defined here will be mocked. A workload with no + keys defined will not mock any traffic. Pass '*' to mock all traffic. + + + Mock strings may only match part of the snapshot's `OutTraffic` key if the + string matches exactly one key. For example, the test string + `foo.example.com` would match the `OutTraffic` key of + my-service:foo.example.com:8080, as long as no other keys would match + `foo.example.com`. Multiple mocks must be specified for multiple keys + unless using '*'. + items: + type: string + type: array + outTrafficKeys: + description: 'DEPRECATED: use Mocks' + items: + type: string + type: array + ref: + description: |- + Ref is a reference to a cluster workload, like a service, deployment or + statefulset. This is required unless a CustomURI is specified. + properties: + apiVersion: + description: API version of the referenced object. + type: string + kind: + description: Kind of the referenced object. Defaults to + "Deployment". + type: string + name: + description: |- + Name of the referenced object. Required when defining for a test unless a + custom URI is provided. Always required when defining mocks. + type: string + namespace: + description: Namespace of the referenced object. Defaults + to the TrafficReplay namespace. + type: string + required: + - name + type: object + routing: + description: Routing configures how workloads route egress traffic + to responders + enum: + - hostalias + - nat + type: string + sidecar: + description: |- + TODO: this is not implemented, come back and replace deprecated Sidecar with workload specific settings + Sidecar defines sidecar specific configuration. + properties: + inject: + description: 'DEPRECATED: do not use' + type: boolean + patch: + description: Patch is .yaml file patch for the Workload + format: byte + type: string + tls: + properties: + in: + description: In provides configuration for sidecar inbound + TLS. + properties: + private: + description: Private is the filename of the TLS + inbound private key. + type: string + public: + description: Public is the filename of the TLS inbound + public key. + type: string + secret: + description: Secret is a secret with the TLS keys + to use for inbound traffic. + type: string + type: object + mutual: + description: Mutual provides configuration for sidecar + mutual TLS. + properties: + private: + description: Private is the filename of the mutual + TLS private key. + type: string + public: + description: Public is the filename of the mutual + TLS public key. + type: string + secret: + description: Secret is a secret with the mutual + TLS keys. + type: string + type: object + out: + description: |- + Out enables or disables TLS out on the + sidecar during replay. + type: boolean + type: object + type: object + tests: + description: |- + Tests are strings used to identify slices of inbound snapshot traffic this + workload is targeting and maps directly to a snapshot's `InTraffic` field. + Snapshot ingress traffic can be split across multiple slices where each + slice contains part of the traffic. A key must only be specified once + across all workloads, but a workload may specify multiple keys. Pass '*' + to match all keys. + + + Test strings may only match part of the snapshot's `InTraffic` key if the + string matches exactly one key. For example, the test string + `foo.example.com` would match the `InTraffic` key of + my-service:foo.example.com:8080, as long as no other keys would match + `foo.example.com` + + + This field is optional in the spec to provide support for single-workload + and legacy replays, but must be specified for multi-workload replays in + order to provide deterministic replay configuration. + items: + type: string + type: array + type: object + type: array + required: + - snapshotID + - testConfigID + type: object + status: + default: + observedGeneration: -1 + description: Status defines the observed state of TrafficReplay. + properties: + active: + description: Active indicates whether this traffic replay is currently + underway or not. + type: boolean + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + finishedTime: + description: Information when the traffic replay has finished. + format: date-time + type: string + initializedTime: + description: Information when the test environment was successfully + prepared. + format: date-time + type: string + lastHeartbeatTime: + description: 'DEPRECATED: will not be set' + format: date-time + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + reconcileFailures: + description: |- + ReconcileFailures is the number of times the traffic replay controller + experienced an error during the reconciliation process. The traffic + replay will be deleted if too many errors occur. + format: int64 + type: integer + reportID: + description: The ID of the traffic replay report created. + type: string + reportURL: + description: The URL to the traffic replay report. + type: string + startedTime: + description: Information when the traffic replay has started. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/deployments.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/deployments.yaml new file mode 100644 index 000000000..e5f329257 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/deployments.yaml @@ -0,0 +1,132 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + operator.speedscale.com/ignore: "true" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} + name: speedscale-operator + namespace: {{ .Release.Namespace }} +spec: + replicas: 1 + selector: + matchLabels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + strategy: + type: Recreate + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 8}} + {{- end }} + spec: + containers: + - command: + - /operator + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + envFrom: + - configMapRef: + name: speedscale-operator + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#container-v1-core + # When a key exists in multiple sources, the value associated with the last source will take precedence. + # Values defined by an Env with a duplicate key will take precedence. + - configMapRef: + name: speedscale-operator-override + optional: true + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/operator:{{ .Values.image.tag }}' + imagePullPolicy: {{ .Values.image.pullPolicy }} + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthz + port: health-check + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 5 + name: operator + ports: + - containerPort: 443 + name: webhook-server + - containerPort: 8081 + name: health-check + readinessProbe: + failureThreshold: 10 + httpGet: + path: /readyz + port: health-check + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + resources: {{- toYaml .Values.operator.resources | nindent 10 }} + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: false + # Run as root to bind 443 https://github.com/kubernetes/kubernetes/issues/56374 + runAsUser: 0 + volumeMounts: + - mountPath: /tmp + name: tmp + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-certs + readOnly: true + - mountPath: /etc/ssl/speedscale + name: speedscale-tls-out + readOnly: true + hostNetwork: {{ .Values.hostNetwork }} + securityContext: + runAsNonRoot: true + serviceAccountName: speedscale-operator + terminationGracePeriodSeconds: 10 + volumes: + - emptyDir: {} + name: tmp + - name: webhook-certs + secret: + secretName: speedscale-webhook-certs + - name: speedscale-tls-out + secret: + secretName: speedscale-certs + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/hooks.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/hooks.yaml new file mode 100644 index 000000000..b6b080f3e --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/hooks.yaml @@ -0,0 +1,79 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "4" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-pre-install + namespace: {{ .Release.Namespace }} + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 30 + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + creationTimestamp: null + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 8}} + {{- end }} + spec: + containers: + - args: + - |- + # ensure valid settings before the chart reports a successfull install + {{- if .Values.http_proxy }} + HTTP_PROXY={{ .Values.http_proxy | quote }} \ + {{- end }} + {{- if .Values.https_proxy }} + HTTPS_PROXY={{ .Values.https_proxy | quote }} \ + {{- end }} + {{- if .Values.no_proxy }} + NO_PROXY={{ .Values.no_proxy | quote }} \ + {{- end }} + speedctl init --overwrite --no-rcfile-update \ + --api-key $SPEEDSCALE_API_KEY \ + --app-url $SPEEDSCALE_APP_URL + + # in case we're in istio + curl -X POST http://127.0.0.1:15000/quitquitquit || true + command: + - sh + - -c + envFrom: + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/speedscale-cli:{{ .Values.image.tag }}' + imagePullPolicy: {{ .Values.image.pullPolicy }} + name: speedscale-cli + resources: + limits: + memory: "128M" + cpu: "1" + requests: + memory: "64M" + cpu: "100m" + restartPolicy: Never + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/rbac.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/rbac.yaml new file mode 100644 index 000000000..fc2a88683 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/rbac.yaml @@ -0,0 +1,246 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: speedscale-operator + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +rules: +- apiGroups: + - apps + resources: + - deployments + - statefulsets + - daemonsets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - get + - list +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - get + - list +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + - pods + - services + - serviceaccounts + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list +- apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - metrics.k8s.io + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.istio.io + resources: + - envoyfilters + - sidecars + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - security.istio.io + resources: + - peerauthentications + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - speedscale.com + resources: + - trafficreplays + - agenttasks + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - speedscale.com + resources: + - trafficreplays/status + - agenttasks/status + verbs: + - get + - update + - patch +- apiGroups: + - argoproj.io + resources: + - rollouts + verbs: + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: speedscale-operator + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: speedscale-operator +subjects: +- kind: ServiceAccount + name: speedscale-operator + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator + namespace: {{ .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/secrets.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/secrets.yaml new file mode 100644 index 000000000..1fb6999e4 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/secrets.yaml @@ -0,0 +1,18 @@ +--- +{{ if .Values.apiKey }} +apiVersion: v1 +kind: Secret +metadata: + name: speedscale-apikey + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: pre-install + helm.sh/hook-weight: "3" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +type: Opaque +data: + SPEEDSCALE_API_KEY: {{ .Values.apiKey | b64enc }} + SPEEDSCALE_APP_URL: {{ .Values.appUrl | b64enc }} +{{ end }} diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/services.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/services.yaml new file mode 100644 index 000000000..f9da2c25c --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/services.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator + namespace: {{ .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +spec: + ports: + - port: 443 + protocol: TCP + selector: + app: speedscale-operator + controlplane.speedscale.com/component: operator +status: + loadBalancer: {} diff --git a/charts/speedscale/speedscale-operator/2.3.149/templates/tls.yaml b/charts/speedscale/speedscale-operator/2.3.149/templates/tls.yaml new file mode 100644 index 000000000..4e293999e --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/templates/tls.yaml @@ -0,0 +1,189 @@ +{{- $crt := "" -}} +{{- $key := "" -}} +{{- $s := (lookup "v1" "Secret" .Release.Namespace "speedscale-certs") -}} +{{- if $s -}} +{{- $crt = index $s.data "tls.crt" | b64dec -}} +{{- $key = index $s.data "tls.key" | b64dec -}} +{{ else }} +{{- $cert := genCA "Speedscale" 3650 -}} +{{- $crt = $cert.Cert -}} +{{- $key = $cert.Key -}} +{{- end -}} +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "5" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-create-jks + namespace: {{ .Release.Namespace }} + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 30 + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + creationTimestamp: null + labels: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + spec: + containers: + - args: + - |- + keytool -keystore /usr/lib/jvm/jre/lib/security/cacerts -importcert -noprompt -trustcacerts -storepass changeit -alias speedscale -file /etc/ssl/speedscale/tls.crt + kubectl -n ${POD_NAMESPACE} delete secret speedscale-jks || true + kubectl -n ${POD_NAMESPACE} create secret generic speedscale-jks --from-file=cacerts.jks=/usr/lib/jvm/jre/lib/security/cacerts + + # in case we're in istio + curl -X POST http://127.0.0.1:15000/quitquitquit || true + command: + - sh + - -c + volumeMounts: + - mountPath: /etc/ssl/speedscale + name: speedscale-tls-out + readOnly: true + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + envFrom: + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/amazoncorretto' + imagePullPolicy: {{ .Values.image.pullPolicy }} + name: create-jks + resources: + limits: + memory: "256M" + cpu: "1" + requests: + memory: "128M" + cpu: "200m" + restartPolicy: Never + serviceAccountName: speedscale-operator-provisioning + volumes: + - name: speedscale-tls-out + secret: + secretName: speedscale-certs + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} +--- +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "1" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator-provisioning + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "2" + creationTimestamp: null + name: speedscale-operator-provisioning +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "3" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-provisioning +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: speedscale-operator-provisioning +subjects: +- kind: ServiceAccount + name: speedscale-operator-provisioning + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-certs + namespace: {{ .Release.Namespace }} +type: kubernetes.io/tls +data: + tls.crt: {{ $crt | b64enc }} + tls.key: {{ $key | b64enc }} diff --git a/charts/speedscale/speedscale-operator/2.3.149/values.yaml b/charts/speedscale/speedscale-operator/2.3.149/values.yaml new file mode 100644 index 000000000..cb3e9e4c5 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.3.149/values.yaml @@ -0,0 +1,138 @@ +# An API key is required to connect to the Speedscale cloud. +# If you need a key email support@speedscale.com. +apiKey: "" + +# A secret name can be referenced instead of the api key itself. +# The secret must be of the format: +# +# type: Opaque +# data: +# SPEEDSCALE_API_KEY: +# SPEEDSCALE_APP_URL: +apiKeySecret: "" + +# Speedscale domain to use. +appUrl: "app.speedscale.com" + +# The name of your cluster. +clusterName: "my-cluster" + +# Speedscale components image settings. +image: + registry: gcr.io/speedscale + tag: v2.3.149 + pullPolicy: Always + +# Log level for Speedscale components. +logLevel: "info" + +# Namespaces to be watched by Speedscale Operator as a list of names. +namespaceSelector: [] + +# Instructs operator to deploy resources necessary to interact with your cluster from the Speedscale dashboard. +dashboardAccess: true + +# Filter Rule to apply to the Speedscale Forwarder +filterRule: "standard" + +# Data Loss Prevention settings. +dlp: + # Instructs operator to enable data loss prevention features + enabled: false + + # Configuration for data loss prevention + config: "standard" + +# If the operator pod/webhooks need to be on the host network. +# This is only needed if the control plane cannot connect directly to a pod +# for eg. if Calico is used as EKS's default networking +# https://docs.tigera.io/calico/3.25/getting-started/kubernetes/managed-public-cloud/eks#install-eks-with-calico-networking +hostNetwork: false + +# A set of annotations to be applied to all Speedscale related deployments, +# services, jobs, pods, etc. +# +# Example: +# annotation.first: value +# annotation.second: value +globalAnnotations: {} + +# A set of labels to be applied to all Speedscale related deployments, +# services, jobs, pods, etc. +# +# Example: +# label1: value +# label2: value +globalLabels: {} + +# A full affinity object as detailed: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity +affinity: {} + +# The list of tolerations as detailed: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ +tolerations: [] + +# A nodeselector object as detailed: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/ +nodeSelector: {} + +# Deploy a demo app at startup. Set this to an empty string to not deploy. +# Valid values: ["java", ""] +deployDemo: "java" + +# Proxy connection settings if required by your network. These translate to standard proxy environment +# variables HTTP_PROXY, HTTPS_PROXY, and NO_PROXY +http_proxy: "" +https_proxy: "" +no_proxy: "" + +# control if sidecar init containers should run with privileged set +privilegedSidecars: false + +# control if the sidecar should enable/disable use of the smart dns lookup feature (requires NET_ADMIN) +disableSidecarSmartReverseDNS: false + +# Operator settings. These limits are recommended unless you have a cluster +# with a very large number of workloads (for eg. 10k+ deployments, replicasets, etc.). +operator: + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + # how long to wait for the SUT to become ready + test_prep_timeout: 10m + # timeout for deploying & upgrading control plane components + control_plane_timeout: 5m + + +# Default sidecar settings. Example: +# sidecar: +# resources: +# limits: +# cpu: 500m +# memory: 512Mi +# ephemeral-storage: 100Mi +# requests: +# cpu: 10m +# memory: 32Mi +# ephemeral-storage: 100Mi +# ignore_src_hosts: example.com, example.org +# ignore_src_ips: 8.8.8.8, 1.1.1.1 +# ignore_dst_hosts: example.com, example.org +# ignore_dst_ips: 8.8.8.8, 1.1.1.1 +# insert_init_first: false +# tls_out: false +# reinitialize_iptables: false +sidecar: {} + +# Forwarder settings +# forwarder: +# resources: +# limits: +# cpu: 500m +# memory: 500M +# requests: +# cpu: 300m +# memory: 250M +forwarder: {} diff --git a/index.yaml b/index.yaml index e782954d2..f0baa7b2e 100644 --- a/index.yaml +++ b/index.yaml @@ -28497,11 +28497,37 @@ entries: catalog.cattle.io/kube-version: '>=1.16-0' catalog.cattle.io/release-name: nats apiVersion: v2 + appVersion: 2.10.25 + created: "2025-01-25T00:02:05.266034289Z" + description: A Helm chart for the NATS.io High Speed Cloud Native Distributed + Communications Technology. + digest: eb4540a9c3074057c9d8253adcdb3b2dbf73387307c9777764ba4d3c44b7cc4e + home: http://github.com/nats-io/k8s + icon: file://assets/icons/nats.png + keywords: + - nats + - messaging + - cncf + kubeVersion: '>=1.16-0' + maintainers: + - email: info@nats.io + name: The NATS Authors + url: https://github.com/nats-io + name: nats + urls: + - assets/nats/nats-1.2.10.tgz + version: 1.2.10 + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: NATS Server + catalog.cattle.io/kube-version: '>=1.16-0' + catalog.cattle.io/release-name: nats + apiVersion: v2 appVersion: 2.10.24 created: "2025-01-15T00:01:38.507163219Z" description: A Helm chart for the NATS.io High Speed Cloud Native Distributed Communications Technology. - digest: d6a315ab17a2dd7bfa3b2f60e8f9031b3463fb7379d3f7750eec0596a1bf8e4d + digest: 6c9d6e74f3b4f02fc727902a5b624586b3e909f08bdc9b11d076d22ea10b9d0f home: http://github.com/nats-io/k8s icon: file://assets/icons/nats.png keywords: @@ -40746,6 +40772,37 @@ entries: - assets/redpanda/redpanda-4.0.33.tgz version: 4.0.33 speedscale-operator: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Speedscale Operator + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: speedscale-operator + apiVersion: v1 + appVersion: 2.3.149 + created: "2025-01-25T00:02:07.018624252Z" + description: Stress test your APIs with real world scenarios. Collect and replay + traffic without scripting. + digest: 8daa193ab55b2319f332c1bbc5880e818f5cf076e4ee7fc5876f834d417a013f + home: https://speedscale.com + icon: file://assets/icons/speedscale-operator.png + keywords: + - speedscale + - test + - testing + - regression + - reliability + - load + - replay + - network + - traffic + kubeVersion: '>= 1.17.0-0' + maintainers: + - email: support@speedscale.com + name: Speedscale Support + name: speedscale-operator + urls: + - assets/speedscale/speedscale-operator-2.3.149.tgz + version: 2.3.149 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Speedscale Operator @@ -48474,4 +48531,4 @@ entries: urls: - assets/netfoundry/ziti-host-1.5.1.tgz version: 1.5.1 -generated: "2025-01-24T00:01:40.139673015Z" +generated: "2025-01-25T00:02:01.352221227Z"