diff --git a/.github/workflows/nats-helm.yaml b/.github/workflows/nats-helm.yaml new file mode 100644 index 00000000..d2854330 --- /dev/null +++ b/.github/workflows/nats-helm.yaml @@ -0,0 +1,27 @@ +name: nats-helm + +on: + pull_request: + paths: + - 'helm/charts/nats/**' + - .github/workflows/nats-helm.yml + +jobs: + test: + runs-on: ubuntu-latest + steps: + + - name: Setup repo + uses: actions/checkout@v3 + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: '1.20' + check-latest: true + cache: true + cache-dependency-path: helm/charts/nats/test/go.sum + + - name: Test + working-directory: helm/charts/nats/test + run: go test diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 85fcf87e..ae0c18bc 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -5,6 +5,7 @@ on: push: branches: - main + - helm-nats-1.x jobs: release: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e451a7a7..d38945b4 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -5,7 +5,6 @@ on: pull_request: paths: - 'helm/charts/**' - - '!helm/charts/index.yaml' - '.github/workflows/test.yaml' jobs: @@ -15,9 +14,9 @@ jobs: fail-fast: false matrix: k8s: - - "1.23" - - "1.22" - - "1.21" + - "1.26" + - "1.25" + - "1.24" steps: - name: Checkout uses: actions/checkout@v3 @@ -57,6 +56,8 @@ jobs: helm/charts/stan \ -type f \ -not -name "*.md" \ + -not -name "*.go" \ + -not -name "_tplYaml.tpl" \ | xargs grep -E "^[ ]*\{[^\}]+nindent $i" \ | grep -Ev "^[^:]+:[ ]{$i}\{"; then echo "invalid nindent $i found; nindent should match template indentation" @@ -68,8 +69,7 @@ jobs: - name: Create k8s cluster run: |- sudo snap install microk8s --channel="${{ matrix.k8s }}/stable" --classic - sudo microk8s.enable dns - sudo microk8s.enable storage + sudo microk8s.enable dns hostpath-storage sudo microk8s.kubectl wait --for=condition=ready --timeout=60s node --all sudo microk8s.kubectl wait --for=condition=ready --timeout=120s --namespace=kube-system pod --all mkdir -p ~/.kube diff --git a/helm/charts/nats/.helmignore b/helm/charts/nats/.helmignore index 50af0317..240dfde2 100644 --- a/helm/charts/nats/.helmignore +++ b/helm/charts/nats/.helmignore @@ -14,9 +14,13 @@ *.swp *.bak *.tmp +*.orig *~ # Various IDEs .project .idea/ *.tmproj .vscode/ + +# template tests +/test diff --git a/helm/charts/nats/Chart.yaml b/helm/charts/nats/Chart.yaml index 2601707a..4627b6c8 100644 --- a/helm/charts/nats/Chart.yaml +++ b/helm/charts/nats/Chart.yaml @@ -1,13 +1,12 @@ apiVersion: v2 appVersion: 2.9.20 -description: A Helm chart for the NATS.io High Speed Cloud Native Distributed Communications - Technology. +description: A Helm chart for the NATS.io High Speed Cloud Native Distributed Communications Technology. name: nats keywords: - nats - messaging - cncf -version: 0.19.17 +version: 1.0.0 home: http://github.com/nats-io/k8s maintainers: - email: info@nats.io diff --git a/helm/charts/nats/README.md b/helm/charts/nats/README.md index 14e0ec2e..b596a67c 100644 --- a/helm/charts/nats/README.md +++ b/helm/charts/nats/README.md @@ -1,918 +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. - -## TL;DR; - -```console -helm repo add nats https://nats-io.github.io/k8s/helm/charts/ -helm install my-nats nats/nats -``` - -## Breaking Change Log - -- **0.15.0**: For users with JetStream enabled (`nats.jetstream.enabled = true`): `nats.jetstream.fileStorage.enabled` now defaults to `true` and `nats.jetstream.fileStorage.size` now defaults to `10Gi`. This updates the StatefulSet `spec.volumeClaimTemplates` field, which is immutable and cannot be changed on an existing StatefulSet; to upgrade from an older chart version, add the value: - ```yaml - nats: - jetstream: - fileStorage: - # add if enabled was previously the default setting - # not recommended; it would be better to migrate to a StatefulSet with storage enabled - enabled: false - # add if size was previously the default setting - size: 1Gi - ``` -- **0.12.0**: The `podManagementPolicy` value was introduced and set to `Parallel` by default, which controls the StatefulSet `spec.podManagementPolicy` field. This field is immutable and cannot be changed on an existing StatefulSet; to upgrade from an older chart version, add the value: - ```yaml - podManagementPolicy: OrderedReady - ``` - -## Configuration - -### Server Image - -```yaml -# use a specific versions -nats: - image: - tag: X.Y.Z-alpine - -# fully custom location -nats: - image: - registry: my.custom.registry - repository: my-nats - tag: latest - pullPolicy: Always -``` - -### Limits - -```yaml -nats: - # The number of connect attempts against discovered routes. - connectRetries: 30 - - # How many seconds should pass before sending a PING - # to a client that has no activity. - pingInterval: - - # Server settings. - limits: - maxConnections: - maxSubscriptions: - maxControlLine: - maxPayload: - - writeDeadline: - maxPending: - maxPings: - lameDuckDuration: - - # Number of seconds to wait for client connections to end after the pod termination is requested - terminationGracePeriodSeconds: 60 -``` - -#### Setting Go Memory Limit (Recommended) - -Since NATS Server v2.9 release, it is possible to use the `GOMEMLIMIT` environment variable to signal memory limits to the Go runtime (which is by default unaware of cgroups memory limits). You should set this to about 90% of the intended available memory resources for the NATS Server container. - -```yaml -nats: - gomemlimit: "4GiB" -``` - -### Logging - -*Note*: It is not recommended to enable trace or debug in production since enabling it will significantly degrade performance. - -```yaml -nats: - logging: - debug: - trace: - logtime: - connectErrorReports: - reconnectErrorReports: -``` - -### TLS setup for client connections - -You can find more on how to setup and trouble shoot TLS connnections at: -https://docs.nats.io/nats-server/configuration/securing_nats/tls - -```yaml -nats: - tls: - secret: - name: nats-client-tls - ca: "ca.crt" - cert: "tls.crt" - key: "tls.key" -``` - -## Clustering - -If clustering is enabled, then a 3-node cluster will be setup. More info at: -https://docs.nats.io/nats-server/configuration/clustering#nats-server-clustering - -```yaml -cluster: - enabled: true - replicas: 3 - - tls: - secret: - name: nats-server-tls - ca: "ca.crt" - cert: "tls.crt" - key: "tls.key" -``` - -Example: - -```sh -$ helm install nats nats/nats --set cluster.enabled=true -``` - -## Leafnodes - -Leafnode connections to extend a cluster. More info at: -https://docs.nats.io/nats-server/configuration/leafnodes - -```yaml -leafnodes: - enabled: true - remotes: - - url: "tls://connect.ngs.global:7422" - # credentials: - # secret: - # name: leafnode-creds - # key: TA.creds - # tls: - # secret: - # name: nats-leafnode-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - tls: - secret: - name: nats-client-tls - ca: "ca.crt" - cert: "tls.crt" - key: "tls.key" -``` - -## Setting up External Access - -### Using HostPorts - -In case of both external access and advertisements being enabled, an -initializer container will be used to gather the public ips. This -container will required to have enough RBAC policy to be able to make a -look up of the public ip of the node where it is running. - -For example, to setup external access for a cluster and advertise the public ip to clients: - -```yaml -nats: - # Toggle whether to enable external access. - # This binds a host port for clients, gateways and leafnodes. - externalAccess: true - - # Toggle to disable client advertisements (connect_urls), - # in case of running behind a load balancer - # it might be required to disable advertisements. - advertise: true - - # In case both external access and advertise are enabled - # then a service account would be required to be able to - # gather the public ip from a node. - serviceAccount: "nats-server" -``` - -Where the service account named `nats-server` has the following RBAC policy for example: - -```yaml ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: nats-server - namespace: default ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: nats-server -rules: -- apiGroups: [""] - resources: - - nodes - verbs: ["get"] --- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: nats-server-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: nats-server -subjects: -- kind: ServiceAccount - name: nats-server - namespace: default -``` - -The container image of the initializer can be customized via: - -```yaml -bootconfig: - image: - tag: X.Y.Z -``` - -### Using LoadBalancers - -In case of using a load balancer for external access, it is recommended to disable advertise -so that internal ips from the NATS Servers are not advertised to the clients connecting through -the load balancer. - -```yaml -cluster: - enabled: true - noAdvertise: true - -leafnodes: - enabled: true - noAdvertise: true - -natsbox: - enabled: true -``` - -Then could use an L4 enabled load balancer to connect to NATS, for example: - -```yaml -apiVersion: v1 -kind: Service -metadata: - name: nats-lb -spec: - type: LoadBalancer - selector: - app.kubernetes.io/name: nats - ports: - - protocol: TCP - port: 4222 - targetPort: 4222 - name: nats - - protocol: TCP - port: 7422 - targetPort: 7422 - name: leafnodes - - protocol: TCP - port: 7522 - targetPort: 7522 - name: gateways -``` - -### Using NATS Chart as a Dependency - -In order to fully manage your deployment through Helm, you can use `nats` as a [helm dependency](https://helm.sh/docs/helm/helm_dependency/#helm-dependency). This is our recommend approach for exposing your NATS deployment with Services or WebSocket Ingresses. - -1. Example uses a helm chart named `mynats` (example: `helm create mynats`) -2. In `Chart.yaml` add the following dependencies block - ```yaml - dependencies: - - name: nats - version: 0.18.0 - repository: https://nats-io.github.io/k8s/helm/charts/ - ``` -3. Run `helm dep update` now (and any time you update the `nats` dependency version) -4. Add `nats` settings to the `values.yaml` file: - ```yaml - # notice the extra nats key here, must match the dependency name in Chart.yaml - nats: - nats: - jetstream: - enabled: true - cluster: - enabled: true - # disable cluster advertisements when running behind a load balancer - noAdvertise: true - - # add whatever other nats settings you need here - ``` -5. Add a template for your service to `templates/service-lb.yaml`: - ```yaml - apiVersion: v1 - kind: Service - metadata: - name: {{ include "mynats.fullname" . }}-lb - labels: - {{- include "mynats.labels" . | nindent 4 }} - spec: - type: LoadBalancer - selector: - {{- include "nats.selectorLabels" .Subcharts.nats | nindent 4 }} - ports: - - name: nats - port: 4222 - protocol: TCP - targetPort: 4222 - ``` - -## Gateways -A super cluster can be formed by pointing to remote gateways. -You can find more about gateways in the NATS documentation: -https://docs.nats.io/nats-server/configuration/gateways +[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. -> ⚠️ Note: When using Gateways and JetStream make sure that the deployment name is different so that the generated server names do not collide. - -```yaml -gateway: - enabled: false - name: 'default' - - ############################# - # # - # List of remote gateways # - # # - ############################# - # gateways: - # - name: other - # url: nats://my-gateway-url:7522 - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - # tls: - # secret: - # name: nats-client-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" -``` - -## Auth setup - -### Auth with a Memory Resolver - -```yaml -auth: - enabled: true - - # Reference to the Operator JWT. - operatorjwt: - configMap: - name: operator-jwt - key: KO.jwt - - # Public key of the System Account - systemAccount: - - resolver: - ############################ - # # - # Memory resolver settings # - # # - ############################## - type: memory - - # - # Use a configmap reference which will be mounted - # into the container. - # - configMap: - name: nats-accounts - key: resolver.conf -``` - -### Auth using an Account Server Resolver - -```yaml -auth: - enabled: true - - # Reference to the Operator JWT. - operatorjwt: - configMap: - name: operator-jwt - key: KO.jwt - - # Public key of the System Account - systemAccount: - - resolver: - ########################## - # # - # URL resolver settings # - # # - ########################## - type: URL - url: "http://nats-account-server:9090/jwt/v1/accounts/" +```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/leafnodes_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 ConfiegMap](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#configmap-v1-core) | yes | +| `natsBox.context.default` | [NATS Context](https://docs.nats.io/using-nats/nats-tools/nats_cli#nats-contexts) | yes | +| `natsBox.context.[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 ``` -## JetStream - -### Setting up Memory and File Storage +## Patch -File Storage is **always** recommended, since JetStream's RAFT Meta Group will be persisted to file storage. The Storage Class used should be block storage. NFS is not recommended. +Patching is performed using [JSON Patch](https://jsonpatch.com/). Example - add additional route to end of route list: ```yaml -nats: - jetstream: +config: + cluster: enabled: true - - memStorage: - enabled: true - size: 2Gi - - fileStorage: - enabled: true - size: 10Gi - # storageClassName: gp2 # NOTE: AWS setup but customize as needed for your infra. + patch: + - op: add + path: /routes/- + value: nats://demo.nats.io:6222 ``` -### Using with an existing PersistentVolumeClaim +## Common Configurations -For example, given the following `PersistentVolumeClaim`: +### JetStream Cluster on 3 separate hosts ```yaml ---- -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: nats-js-disk - annotations: - volume.beta.kubernetes.io/storage-class: "default" -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 3Gi -``` - -You can start JetStream so that one pod is bounded to it: - -```yaml -nats: +config: + cluster: + enabled: true + replicas: 3 jetstream: enabled: true + fileStore: + pvc: + size: 10Gi - fileStorage: - enabled: true - storageDirectory: /data/ - existingClaim: nats-js-disk - claimStorageSize: 3Gi +podTemplate: + topologySpreadConstraints: + kubernetes.io/hostname: + maxSkew: 1 + whenUnsatisfiable: DoNotSchedule ``` -### Clustering example +### NATS Container Resources ```yaml - -nats: - jetstream: - enabled: true - - memStorage: - enabled: true - size: "2Gi" - - fileStorage: - enabled: true - size: "10Gi" - -cluster: - enabled: true - # Cluster name is required, by default will be release name. - # name: "nats" - replicas: 3 +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 ``` -### Basic Authentication and JetStream +### Specify Image Version ```yaml -nats: - jetstream: - enabled: true - - memStorage: - enabled: true - size: "2Gi" - - fileStorage: - enabled: true - size: "10Gi" - # storageClassName: gp2 # NOTE: AWS setup but customize as needed for your infra. - -cluster: - enabled: true - # Can set a custom cluster name - # name: "nats" - replicas: 3 - -auth: - enabled: true - - systemAccount: "$SYS" - - basic: - accounts: - $SYS: - users: - - user: sys - pass: sys - js: - jetstream: true - users: - - user: foo -``` - -### NATS Resolver setup example - -As of NATS v2.2, the server now has a built-in NATS resolver of accounts. -The following is an example guide of how to get it configured. - -```sh -# Create a working directory to keep the creds. -mkdir nats-creds -cd nats-creds - -# This just creates some accounts for you to get started. -curl -fSl https://nats-io.github.io/k8s/setup/nsc-setup.sh | sh -source .nsc.env - -# You should have some accounts now, at least the following. -nsc list accounts -+-------------------------------------------------------------------+ -| Accounts | -+--------+----------------------------------------------------------+ -| Name | Public Key | -+--------+----------------------------------------------------------+ -| A | ABJ4OIKBBFCNXZDP25C7EWXCXOVCYYAGBEHFAG7F5XYCOYPHZLNSJYDF | -| B | ACVRK7GFBRQUCB3NEABGQ7XPNED2BSPT27GOX5QBDYW2NOFMQKK755DJ | -| SYS | ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N | -+--------+----------------------------------------------------------+ - -# Now create an account with JetStream support -export account=JS1 -nsc add account --name $account -nsc edit account --name $account --js-disk-storage -1 --js-consumer -1 --js-streams -1 -nsc add user -a $account js-user +container: + image: + tag: x.y.z-alpine ``` -Next, generate the NATS resolver config. This will be used to fill in the values of the YAML in the Helm template. -For example the result of generating this: - -```sh -nsc generate config --sys-account SYS --nats-resolver - -# Operator named KO -operator: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDRlozRlE0WURNTUc1Q1UzU0FUWVlHWUdQUDJaQU1QUzVNRUdNWFdWTUJFWUdIVzc2WEdBIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJLTyIsInN1YiI6Ik9DSVYyUUZKV0lOWlVBVDVUMllKQklSQzNCNkpLTVNaS1FORjVLR1A0TjNLWjRGRkRWQVdZWENMIiwibmF0cyI6eyJ0eXBlIjoib3BlcmF0b3IiLCJ2ZXJzaW9uIjoyfX0.e3gvJ-C1IBznmbUljeT_wbLRl1akv5IGBS3rbxs6mzzTvf3zlqQI4wDKVE8Gvb8qfTX6TIwocClfOqNaN3k3CQ +### Operator Mode with NATS Resolver -# System Account named SYS -system_account: ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N - -resolver_preload: { - ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDR0tWVzJGQUszUE5XQTRBWkhHT083UTdZWUVPQkJYNDZaTU1VSFc1TU5QSUFVSFE0RVRRIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJTWVMiLCJzdWIiOiJBREdGSDROWVY1Vjc1U1ZNNURZU1c1QVdPRDdIMk5SVVdBTU82WExaS0lER1VXWUVYQ1pHNUQ2TiIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwid2lsZGNhcmRzIjp0cnVlLCJjb25uIjotMSwibGVhZiI6LTF9LCJkZWZhdWx0X3Blcm1pc3Npb25zIjp7InB1YiI6e30sInN1YiI6e319LCJ0eXBlIjoiYWNjb3VudCIsInZlcnNpb24iOjJ9fQ.J7g73TEn-ZT13owq4cVWl4l0hZnGK4DJtH2WWOZmGbefcCQ1xsx4cIagKc1cZTCwUpELVAYnSkmPp4LsQOspBg, -} -``` - -In the YAML would be configured as follows: +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`. ``` -auth: - enabled: true - - timeout: "5s" - +config: resolver: - type: full - - operator: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDRlozRlE0WURNTUc1Q1UzU0FUWVlHWUdQUDJaQU1QUzVNRUdNWFdWTUJFWUdIVzc2WEdBIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJLTyIsInN1YiI6Ik9DSVYyUUZKV0lOWlVBVDVUMllKQklSQzNCNkpLTVNaS1FORjVLR1A0TjNLWjRGRkRWQVdZWENMIiwibmF0cyI6eyJ0eXBlIjoib3BlcmF0b3IiLCJ2ZXJzaW9uIjoyfX0.e3gvJ-C1IBznmbUljeT_wbLRl1akv5IGBS3rbxs6mzzTvf3zlqQI4wDKVE8Gvb8qfTX6TIwocClfOqNaN3k3CQ - - systemAccount: ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N - - store: - dir: "/etc/nats-config/accounts/jwt" - size: "1Gi" - - resolverPreload: - ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDR0tWVzJGQUszUE5XQTRBWkhHT083UTdZWUVPQkJYNDZaTU1VSFc1TU5QSUFVSFE0RVRRIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJTWVMiLCJzdWIiOiJBREdGSDROWVY1Vjc1U1ZNNURZU1c1QVdPRDdIMk5SVVdBTU82WExaS0lER1VXWUVYQ1pHNUQ2TiIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwid2lsZGNhcmRzIjp0cnVlLCJjb25uIjotMSwibGVhZiI6LTF9LCJkZWZhdWx0X3Blcm1pc3Npb25zIjp7InB1YiI6e30sInN1YiI6e319LCJ0eXBlIjoiYWNjb3VudCIsInZlcnNpb24iOjJ9fQ.J7g73TEn-ZT13owq4cVWl4l0hZnGK4DJtH2WWOZmGbefcCQ1xsx4cIagKc1cZTCwUpELVAYnSkmPp4LsQOspBg -``` - -Now we start the server with the NATS Account Resolver (`auth.resolver.type=full`): - -```yaml -nats: - logging: - debug: false - trace: false - - jetstream: enabled: true - - memStorage: - enabled: true - size: "2Gi" - - fileStorage: - enabled: true - size: "10Gi" - # storageClassName: gp2 # NOTE: AWS setup but customize as needed for your infra. - -cluster: - enabled: true - # Can set a custom cluster name - name: "nats" - replicas: 3 - -auth: - enabled: true - - timeout: "5s" - - resolver: - type: full - - operator: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDRlozRlE0WURNTUc1Q1UzU0FUWVlHWUdQUDJaQU1QUzVNRUdNWFdWTUJFWUdIVzc2WEdBIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJLTyIsInN1YiI6Ik9DSVYyUUZKV0lOWlVBVDVUMllKQklSQzNCNkpLTVNaS1FORjVLR1A0TjNLWjRGRkRWQVdZWENMIiwibmF0cyI6eyJ0eXBlIjoib3BlcmF0b3IiLCJ2ZXJzaW9uIjoyfX0.e3gvJ-C1IBznmbUljeT_wbLRl1akv5IGBS3rbxs6mzzTvf3zlqQI4wDKVE8Gvb8qfTX6TIwocClfOqNaN3k3CQ - - systemAccount: ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N - - store: - dir: "/etc/nats-config/accounts/jwt" - size: "1Gi" - - resolverPreload: - ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDR0tWVzJGQUszUE5XQTRBWkhHT083UTdZWUVPQkJYNDZaTU1VSFc1TU5QSUFVSFE0RVRRIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJTWVMiLCJzdWIiOiJBREdGSDROWVY1Vjc1U1ZNNURZU1c1QVdPRDdIMk5SVVdBTU82WExaS0lER1VXWUVYQ1pHNUQ2TiIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwid2lsZGNhcmRzIjp0cnVlLCJjb25uIjotMSwibGVhZiI6LTF9LCJkZWZhdWx0X3Blcm1pc3Npb25zIjp7InB1YiI6e30sInN1YiI6e319LCJ0eXBlIjoiYWNjb3VudCIsInZlcnNpb24iOjJ9fQ.J7g73TEn-ZT13owq4cVWl4l0hZnGK4DJtH2WWOZmGbefcCQ1xsx4cIagKc1cZTCwUpELVAYnSkmPp4LsQOspBg + 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 ``` -Finally, using a local port-forward make it possible to establish a connection to one of the servers and upload the accounts. -```sh -nsc push --system-account SYS -u nats://localhost:4222 -A -[ OK ] push to nats-server "nats://localhost:4222" using system account "SYS": - [ OK ] push JS1 to nats-server with nats account resolver: - [ OK ] pushed "JS1" to nats-server nats-0: jwt updated - [ OK ] pushed "JS1" to nats-server nats-1: jwt updated - [ OK ] pushed "JS1" to nats-server nats-2: jwt updated - [ OK ] pushed to a total of 3 nats-server -``` - -Now you should be able to use JetStream and the NATS based account resolver: - -```sh -nats stream ls -s localhost --creds ./nsc/nkeys/creds/KO/JS1/js-user.creds -No Streams defined -``` - -## Misc - -### NATS Box - -A lightweight container with NATS and NATS Streaming utilities that is deployed along the cluster to confirm the setup. -You can find the image at: https://github.com/nats-io/nats-box - -```yaml -natsbox: - enabled: true - image: - tag: X.Y.Z - - # credentials: - # secret: - # name: nats-sys-creds - # key: sys.creds -``` - -You can also add volumes to nats-box, for example given a PVC like: - -```yaml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: nsc-pvc -spec: - accessModes: - - ReadWriteOnce - volumeMode: Filesystem - resources: - requests: - storage: 1Gi -``` - -You can give state to nats-box by using the `extraVolumes` and `extraVolumeMounts` options: - -```yaml -natsbox: - enabled: true - extraVolumes: - - name: nsc - persistentVolumeClaim: - claimName: nsc-pvc - extraVolumeMounts: - - mountPath: /nsc - name: nsc -``` - -example: +## Accessing NATS -```sh -$ helm install nats-nsc nats/nats -f examples/nats-box-persistent.yaml -$ kubectl exec -it deployment/nats-nsc-box -- /bin/sh +The chart contains 2 services by default, `service` and `headlessService`. -# cd /nsc -/nsc # curl -fSl https://nats-io.github.io/k8s/setup/nsc-setup.sh | sh -/nsc # source .nsc.env -/nsc # nsc list accounts -``` - -### Configuration Checksum +### `service` -A configuration checksum annotation is enabled by default on StatefulSet Pods in order to force a rollout when the NATS configuration changes. This checksum is only applied by `helm` commands, and will not change if configuration is modified outside of setting `helm` values. +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. -```yaml -nats: - configChecksumAnnotation: true -``` +The `nats`, `websocket`, `leafnodes`, and `mqtt` ports will be exposed through this service by default if they are enabled. -### Configuration Reload sidecar - -The NATS configuration reload sidecar is enabled by default; it passes the configuration reload signal to the NATS server when it detects configuration changes: +Example: change this service type to a `LoadBalancer`: ```yaml -reloader: - enabled: true - image: - tag: X.Y.Z +service: + merge: + spec: + type: LoadBalancer ``` -### Prometheus Exporter sidecar +### `headlessService` -The Prometheus Exporter sidecar is enabled by default; it can be used to feed metrics to Prometheus: +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. -```yaml -exporter: - enabled: true - image: - tag: X.Y.Z -``` +### TLS Considerations -### Prometheus operator ServiceMonitor support - -You can enable prometheus operator ServiceMonitor: - -```yaml -exporter: - # You have to enable exporter first - enabled: true - serviceMonitor: - enabled: true - ## Specify the namespace where Prometheus Operator is running - # namespace: monitoring - # ... -``` +The TLS Certificate used for Client Connections should have a SAN covering DNS Name that clients access the `service` at. -### Pod Customizations +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. -#### Security Context +## Advanced Features -```yaml - # Toggle whether to use setup a Pod Security Context - # ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ -securityContext: - fsGroup: 1000 - runAsUser: 1000 - runAsNonRoot: true -``` +### Templating Values -#### Affinity +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 + ``` -`matchExpressions` must be configured according to your setup +Example - change service name: ```yaml -affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: node.kubernetes.io/purpose - operator: In - values: - - nats - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - nats - - stan - topologyKey: "kubernetes.io/hostname" +service: + name: + $tplYaml: >- + {{ include "nats.fullname" . }}-svc ``` -#### Service topology +### NATS Config Units and Variables -[Service topology](https://kubernetes.io/docs/concepts/services-networking/service-topology/) is disabled by default, but can be enabled by setting `topologyKeys`. For example: +NATS configuration extends JSON, and can represent Units and Variables. They must be wrapped in `<< >>` in order to template correctly. Example: ```yaml -topologyKeys: - - "kubernetes.io/hostname" - - "topology.kubernetes.io/zone" - - "topology.kubernetes.io/region" +config: + merge: + authorization: + # variable + token: << $TOKEN >> + # units + max_payload: << 2MB >> ``` -#### CPU/Memory Resource Requests/Limits -Sets the pods cpu/memory requests/limits +templates to the `nats.conf`: -```yaml -nats: - resources: - requests: - cpu: 4 - memory: 8Gi - limits: - cpu: 6 - memory: 10Gi ``` - -No resources are set by default. It is recommended for NATS JetStream deployments to allocate at least 8Gi of memory and 4 cpus. - -#### Annotations - - - -```yaml -podAnnotations: - key1 : "value1", - key2 : "value2" +{ + "authorization": { + "token": $TOKEN + }, + "max_payload": 2MB, + "port": 4222, + ... +} ``` -### Name Overides +### NATS Config Includes -Can change the name of the resources as needed with: +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 -nameOverride: "my-nats" +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 ``` -### Image Pull Secrets +templates to the `nats.conf`: -```yaml -imagePullSecrets: -- name: myRegistry ``` - -Adds this to the StatefulSet: - -```yaml -spec: - imagePullSecrets: - - name: myRegistry +include auth.conf; +"port": 4222, +... +include params.conf; ``` -### Mixed TLS and non TLS mode +### Extra Resources -You can use the `nats.tls.allowNonTLS` option to allow a cluster to use TLS connections -and plain connections: +Enables adding additional arbitrary resources. Example - expose WebSocket via VirtualService in Istio: ```yaml -nats: - client: - port: 4222 - - tls: - allowNonTLS: true - secret: - name: nats-server-tls - ca: "ca.crt" - cert: "tls.crt" - key: "tls.key" - timeout: "5s" +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/helm/charts/nats/UPGRADING.md b/helm/charts/nats/UPGRADING.md new file mode 100644 index 00000000..9cc17799 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config-map.yaml b/helm/charts/nats/files/config-map.yaml new file mode 100644 index 00000000..89ee3c28 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config/cluster.yaml b/helm/charts/nats/files/config/cluster.yaml new file mode 100644 index 00000000..719cb8ad --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config/config.yaml b/helm/charts/nats/files/config/config.yaml new file mode 100644 index 00000000..cc1849a8 --- /dev/null +++ b/helm/charts/nats/files/config/config.yaml @@ -0,0 +1,115 @@ +{{- $pidFile := ternary "/var/run/nats/nats.pid" "/var/run/nats.pid" .Values.reloader.enabled }} +{{- with .Values.config }} + +server_name: << $SERVER_NAME >> +lame_duck_grace_period: 10s +lame_duck_duration: 30s +pid_file: {{ $pidFile }} + +######################################## +# 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/helm/charts/nats/files/config/gateway.yaml b/helm/charts/nats/files/config/gateway.yaml new file mode 100644 index 00000000..32d4ed9f --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config/jetstream.yaml b/helm/charts/nats/files/config/jetstream.yaml new file mode 100644 index 00000000..17262f64 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config/leafnodes.yaml b/helm/charts/nats/files/config/leafnodes.yaml new file mode 100644 index 00000000..3a1d9a14 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config/mqtt.yaml b/helm/charts/nats/files/config/mqtt.yaml new file mode 100644 index 00000000..e25d8a3e --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config/protocol.yaml b/helm/charts/nats/files/config/protocol.yaml new file mode 100644 index 00000000..288c80d7 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config/resolver.yaml b/helm/charts/nats/files/config/resolver.yaml new file mode 100644 index 00000000..a6761c40 --- /dev/null +++ b/helm/charts/nats/files/config/resolver.yaml @@ -0,0 +1,3 @@ +{{- with .Values.config.resolver }} +dir: {{ .dir }} +{{- end }} diff --git a/helm/charts/nats/files/config/tls.yaml b/helm/charts/nats/files/config/tls.yaml new file mode 100644 index 00000000..26aee015 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/config/websocket.yaml b/helm/charts/nats/files/config/websocket.yaml new file mode 100644 index 00000000..e3cdd4cc --- /dev/null +++ b/helm/charts/nats/files/config/websocket.yaml @@ -0,0 +1,13 @@ +{{- with .Values.config.websocket }} +port: {{ .port }} +compression: true + +{{- 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/helm/charts/nats/files/headless-service.yaml b/helm/charts/nats/files/headless-service.yaml new file mode 100644 index 00000000..da6552b3 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/ingress.yaml b/helm/charts/nats/files/ingress.yaml new file mode 100644 index 00000000..b59f0fa5 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/nats-box/contents-secret.yaml b/helm/charts/nats/files/nats-box/contents-secret.yaml new file mode 100644 index 00000000..6e8fdb26 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/nats-box/contexts-secret/context.yaml b/helm/charts/nats/files/nats-box/contexts-secret/context.yaml new file mode 100644 index 00000000..97e4671b --- /dev/null +++ b/helm/charts/nats/files/nats-box/contexts-secret/context.yaml @@ -0,0 +1,49 @@ +{{- $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 +{{- 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 }} diff --git a/helm/charts/nats/files/nats-box/contexts-secret/contexts-secret.yaml b/helm/charts/nats/files/nats-box/contexts-secret/contexts-secret.yaml new file mode 100644 index 00000000..0ce8d1d8 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/nats-box/deployment/container.yaml b/helm/charts/nats/files/nats-box/deployment/container.yaml new file mode 100644 index 00000000..9c99959f --- /dev/null +++ b/helm/charts/nats/files/nats-box/deployment/container.yaml @@ -0,0 +1,43 @@ +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 sh -ec "$0" +args: +- 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/helm/charts/nats/files/nats-box/deployment/deployment.yaml b/helm/charts/nats/files/nats-box/deployment/deployment.yaml new file mode 100644 index 00000000..bf39dd8d --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/nats-box/deployment/pod-template.yaml b/helm/charts/nats/files/nats-box/deployment/pod-template.yaml new file mode 100644 index 00000000..47e6fbbb --- /dev/null +++ b/helm/charts/nats/files/nats-box/deployment/pod-template.yaml @@ -0,0 +1,37 @@ +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.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/helm/charts/nats/files/nats-box/service-account.yaml b/helm/charts/nats/files/nats-box/service-account.yaml new file mode 100644 index 00000000..c31e52f1 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/pod-disruption-budget.yaml b/helm/charts/nats/files/pod-disruption-budget.yaml new file mode 100644 index 00000000..fd1fdead --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/pod-monitor.yaml b/helm/charts/nats/files/pod-monitor.yaml new file mode 100644 index 00000000..c6c8eae0 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/service-account.yaml b/helm/charts/nats/files/service-account.yaml new file mode 100644 index 00000000..22c18cc7 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/service.yaml b/helm/charts/nats/files/service.yaml new file mode 100644 index 00000000..db08fe5b --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/stateful-set/jetstream-pvc.yaml b/helm/charts/nats/files/stateful-set/jetstream-pvc.yaml new file mode 100644 index 00000000..a43f2005 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/stateful-set/nats-container.yaml b/helm/charts/nats/files/stateful-set/nats-container.yaml new file mode 100644 index 00000000..b89d20e0 --- /dev/null +++ b/helm/charts/nats/files/stateful-set/nats-container.yaml @@ -0,0 +1,98 @@ +{{- $pidFile := ternary "/var/run/nats/nats.pid" "/var/run/nats.pid" .Values.reloader.enabled }} +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={{ $pidFile }} + +{{- if .Values.config.monitor.enabled }} +startupProbe: + httpGet: + path: /healthz + port: monitor + initialDelaySeconds: 10 + timeoutSeconds: 5 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 90 +readinessProbe: + httpGet: + path: /healthz?js-server-only=true + port: monitor + initialDelaySeconds: 10 + timeoutSeconds: 5 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 +livenessProbe: + httpGet: + path: /healthz?js-enabled-only=true + port: monitor + initialDelaySeconds: 10 + timeoutSeconds: 5 + periodSeconds: 30 + successThreshold: 1 + failureThreshold: 3 +{{- end }} + +volumeMounts: +# nats config +- name: config + mountPath: /etc/nats-config +# PID volume +{{- if .Values.reloader.enabled }} +- name: pid + mountPath: /var/run/nats +{{- end}} +# 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/helm/charts/nats/files/stateful-set/pod-template.yaml b/helm/charts/nats/files/stateful-set/pod-template.yaml new file mode 100644 index 00000000..afff14ed --- /dev/null +++ b/helm/charts/nats/files/stateful-set/pod-template.yaml @@ -0,0 +1,66 @@ +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.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 + {{- if .Values.reloader.enabled }} + - name: pid + emptyDir: {} + {{- end }} + # 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/helm/charts/nats/files/stateful-set/prom-exporter-container.yaml b/helm/charts/nats/files/stateful-set/prom-exporter-container.yaml new file mode 100644 index 00000000..c3e1b6fb --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/stateful-set/reloader-container.yaml b/helm/charts/nats/files/stateful-set/reloader-container.yaml new file mode 100644 index 00000000..96722045 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/stateful-set/resolver-pvc.yaml b/helm/charts/nats/files/stateful-set/resolver-pvc.yaml new file mode 100644 index 00000000..3634cd82 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/files/stateful-set/stateful-set.yaml b/helm/charts/nats/files/stateful-set/stateful-set.yaml new file mode 100644 index 00000000..cd8082cb --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/NOTES.txt b/helm/charts/nats/templates/NOTES.txt deleted file mode 100644 index 694dc67c..00000000 --- a/helm/charts/nats/templates/NOTES.txt +++ /dev/null @@ -1,26 +0,0 @@ - -{{- if or .Values.nats.logging.debug .Values.nats.logging.trace }} -*WARNING*: Keep in mind that running the server with -debug and/or trace enabled significantly affects the -performance of the server! -{{- end }} - -You can find more information about running NATS on Kubernetes -in the NATS documentation website: - - https://docs.nats.io/nats-on-kubernetes/nats-kubernetes - -{{- if .Values.natsbox.enabled }} - -NATS Box has been deployed into your cluster, you can -now use the NATS tools within the container as follows: - - kubectl exec -n {{ template "nats.namespace" . }} -it deployment/{{ template "nats.fullname" . }}-box -- /bin/sh -l - - nats-box:~# nats sub test & - nats-box:~# nats pub test hi - nats-box:~# nc {{ template "nats.fullname" . }} {{ .Values.nats.client.port }} - -{{- end }} - -Thanks for using NATS! diff --git a/helm/charts/nats/templates/_helpers.tpl b/helm/charts/nats/templates/_helpers.tpl index 9f177b81..ba831397 100644 --- a/helm/charts/nats/templates/_helpers.tpl +++ b/helm/charts/nats/templates/_helpers.tpl @@ -2,26 +2,26 @@ Expand the name of the chart. */}} {{- define "nats.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{- define "nats.namespace" -}} -{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - +{{- 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 -}} +{{- 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. @@ -31,13 +31,78 @@ Create chart name and version as used by the chart label. {{- end }} {{/* -Common labels +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" . }} -{{- range $name, $value := .Values.commonLabels }} -{{ $name }}: {{ tpl $value $ }} -{{- end }} {{ include "nats.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} @@ -46,211 +111,171 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* -Selector labels +NATS selector labels */}} {{- define "nats.selectorLabels" -}} -{{- if .Values.nats.selectorLabels }} -{{ tpl (toYaml .Values.nats.selectorLabels) . }} -{{- else -}} app.kubernetes.io/name: {{ include "nats.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: nats {{- end }} -{{- end }} - {{/* -Return the proper NATS image name +NATS Box labels */}} -{{- define "nats.clusterAdvertise" -}} -{{- if $.Values.useFQDN }} -{{- printf "$(POD_NAME).%s.$(POD_NAMESPACE).svc.%s" (include "nats.fullname" . ) $.Values.k8sClusterDomain }} -{{- else }} -{{- printf "$(POD_NAME).%s.$(POD_NAMESPACE)" (include "nats.fullname" . ) }} +{{- 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 }} {{/* -Return the NATS cluster auth. +NATS Box selector labels */}} -{{- define "nats.clusterAuth" -}} -{{- if $.Values.cluster.authorization }} -{{- printf "%s:%s@" (urlquery $.Values.cluster.authorization.user) (urlquery $.Values.cluster.authorization.password) -}} -{{- else }} -{{- end }} +{{- define "natsBox.selectorLabels" -}} +app.kubernetes.io/name: {{ include "nats.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: nats-box {{- end }} {{/* -Return the NATS cluster routes. +Print the image */}} -{{- define "nats.clusterRoutes" -}} -{{- $name := (include "nats.fullname" . ) -}} -{{- $namespace := (include "nats.namespace" . ) -}} -{{- $clusterAuth := (include "nats.clusterAuth" . ) -}} -{{- range $i, $e := until (.Values.cluster.replicas | int) -}} -{{- if $.Values.useFQDN }} -{{- printf "nats://%s%s-%d.%s.%s.svc.%s:6222," $clusterAuth $name $i $name $namespace $.Values.k8sClusterDomain -}} -{{- else }} -{{- printf "nats://%s%s-%d.%s.%s:6222," $clusterAuth $name $i $name $namespace -}} -{{- end }} +{{- 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 }} - -{{- define "nats.extraRoutes" -}} -{{- range $i, $url := .Values.cluster.extraRoutes -}} -{{- printf "%s," $url -}} -{{- end -}} {{- end }} -{{- define "nats.tlsConfig" -}} -tls { -{{- if .cert }} - cert_file: {{ .secretPath }}/{{ .secret.name }}/{{ .cert }} +{{- 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 }} -{{- if .key }} - key_file: {{ .secretPath }}/{{ .secret.name }}/{{ .key }} +{{- toJson (dict "secretNames" $secrets) }} {{- end }} -{{- if .ca }} - ca_file: {{ .secretPath }}/{{ .secret.name }}/{{ .ca }} + +{{- 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 }} -{{- if .insecure }} - insecure: {{ .insecure }} +{{- toJson (dict "secretNames" $secrets) }} {{- end }} -{{- if .verify }} - verify: {{ .verify }} + +{{- 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 }} -{{- if .verifyAndMap }} - verify_and_map: {{ .verifyAndMap }} {{- end }} -{{- if .verifyCertAndCheckKnownUrls }} - verify_cert_and_check_known_urls: {{ .verifyCertAndCheckKnownUrls }} {{- end }} -{{- if .curvePreferences }} - curve_preferences: {{ .curvePreferences }} {{- end }} -{{- if .timeout }} - timeout: {{ .timeout }} + +{{- define "nats.tlsCAVolumeMount" -}} +{{- with .Values.tlsCA }} +{{- if and .enabled (or .configMapName .secretName) }} +- name: tls-ca + mountPath: {{ .dir | quote }} {{- end }} -{{- if .cipherSuites }} - cipher_suites: {{ toRawJson .cipherSuites }} {{- end }} -} {{- end }} -{{- define "nats.tlsReloaderArgs" -}} -{{ $secretName := .secret.name }} -{{ $secretPath := .secretPath }} -{{- with .ca }} -- -config -- {{ $secretPath }}/{{ $secretName }}/{{ . }} -{{- end }} -{{- with .cert }} -- -config -- {{ $secretPath }}/{{ $secretName }}/{{ . }} +{{/* +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 }} -{{- with .key }} -- -config -- {{ $secretPath }}/{{ $secretName }}/{{ . }} {{- end }} {{- end }} -{{- define "nats.tlsVolumeMounts" -}} -{{- with .Values.nats.tls }} -####################### -# # -# TLS Volumes Mounts # -# # -####################### -{{ $secretName := tpl .secret.name $ }} -- name: {{ $secretName }}-clients-volume - mountPath: /etc/nats-certs/clients/{{ $secretName }} -{{- end }} -{{- with .Values.mqtt.tls }} -{{ $secretName := tpl .secret.name $ }} -- name: {{ $secretName }}-mqtt-volume - mountPath: /etc/nats-certs/mqtt/{{ $secretName }} -{{- end }} -{{- with .Values.cluster.tls }} -{{- if not .custom }} -{{ $secretName := tpl .secret.name $ }} -- name: {{ $secretName }}-cluster-volume - mountPath: /etc/nats-certs/cluster/{{ $secretName }} -{{- end }} -{{- end }} -{{- with .Values.leafnodes.tls }} -{{- if not .custom }} -{{ $secretName := tpl .secret.name $ }} -- name: {{ $secretName }}-leafnodes-volume - mountPath: /etc/nats-certs/leafnodes/{{ $secretName }} -{{- end }} -{{- end }} -{{- with .Values.gateway.tls }} -{{ $secretName := tpl .secret.name $ }} -- name: {{ $secretName }}-gateways-volume - mountPath: /etc/nats-certs/gateways/{{ $secretName }} -{{- end }} -{{- with .Values.websocket.tls }} -{{ $secretName := tpl .secret.name $ }} -- name: {{ $secretName }}-ws-volume - mountPath: /etc/nats-certs/ws/{{ $secretName }} -{{- 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 }} -{{/* -Return the appropriate apiVersion for networkpolicy. -*/}} -{{- define "networkPolicy.apiVersion" -}} -{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}} -{{- print "extensions/v1beta1" -}} -{{- else -}} -{{- print "networking.k8s.io/v1" -}} -{{- end -}} -{{- end -}} -{{/* -Renders a value that contains template. -Usage: -{{ include "tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} +{{- /* +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 "tplvalues.render" -}} - {{- if typeIs "string" .value }} - {{- tpl .value .context }} - {{- else }} - {{- tpl (toYaml .value) .context }} - {{- end }} +{{- 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 -}} -{{/* -Create the name of the service account to use -*/}} -{{- define "nats.serviceAccountName" -}} -{{- if .Values.nats.serviceAccount.create }} -{{- default (include "nats.fullname" .) .Values.nats.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.nats.serviceAccount.name }} -{{- end }} -{{- end }} - -{{/* -Fix image keys for chart versions <= 0.18.3 -*/}} -{{- define "nats.fixImage" -}} -{{- if kindIs "string" .image }} -{{- $_ := set . "image" (dict "repository" (split ":" .image)._0 "tag" ((split ":" .image)._1 | default "latest") "pullPolicy" "IfNotPresent") }} -{{- end }} -{{- if kindIs "string" .pullPolicy }} -{{- $_ := set .image "pullPolicy" .pullPolicy }} -{{- $_ := unset . "pullPolicy" }} -{{- end }} -{{- end }} - -{{/* -Print the image +{{- /* +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.image" -}} -{{- $image := printf "%s:%s" .repository .tag }} -{{- if .registry }} -{{- $image = printf "%s/%s" .registry $image }} -{{- end }} -{{- $image -}} -{{- end }} +{{- define "nats.formatConfig" -}} + {{- + (regexReplaceAll "\"<<\\s+(.*)\\s+>>\"" + (regexReplaceAll "\".*\\$include\": \"(.*)\",?" (include "toPrettyRawJson" .) "include ${1};") + "${1}") + -}} +{{- end -}} diff --git a/helm/charts/nats/templates/_jsonpatch.tpl b/helm/charts/nats/templates/_jsonpatch.tpl new file mode 100644 index 00000000..cd42c3bb --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/_toPrettyRawJson.tpl b/helm/charts/nats/templates/_toPrettyRawJson.tpl new file mode 100644 index 00000000..612a62f9 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/_tplYaml.tpl b/helm/charts/nats/templates/_tplYaml.tpl new file mode 100644 index 00000000..f42b9c16 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/config-map.yaml b/helm/charts/nats/templates/config-map.yaml new file mode 100644 index 00000000..b95afda2 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/configmap.yaml b/helm/charts/nats/templates/configmap.yaml deleted file mode 100644 index da4959d9..00000000 --- a/helm/charts/nats/templates/configmap.yaml +++ /dev/null @@ -1,614 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "nats.fullname" . }}-config - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} -data: - nats.conf: | - # NATS Clients Port - port: {{ .Values.nats.client.port }} - - # PID file shared with configuration reloader. - pid_file: "/var/run/nats/nats.pid" - - {{- if .Values.nats.config }} - ########### - # # - # Imports # - # # - ########### - {{- range .Values.nats.config }} - include ./{{ .name }}/{{ .name }}.conf - {{- end}} - {{- end }} - - ############### - # # - # Monitoring # - # # - ############### - http: 8222 - server_name: {{ if .Values.nats.serverNamePrefix }}$SERVER_NAME{{ else }}$POD_NAME{{ end }} - - {{- if .Values.nats.serverTags }} - server_tags: [ - {{- range .Values.nats.serverTags }} - "{{ . }}", - {{- end }} - ] - {{- end }} - - {{- if .Values.nats.tls }} - ##################### - # # - # TLS Configuration # - # # - ##################### - {{- with .Values.nats.tls }} - {{- $nats_tls := merge (dict) . }} - {{- $_ := set $nats_tls "secretPath" "/etc/nats-certs/clients" }} - {{- tpl (include "nats.tlsConfig" $nats_tls) $ | nindent 4}} - {{- end }} - - {{- if .Values.nats.tls.allowNonTLS }} - allow_non_tls: {{ .Values.nats.tls.allowNonTLS }} - {{- end }} - - {{- end }} - - {{- if .Values.nats.jetstream.enabled }} - ################################### - # # - # NATS JetStream # - # # - ################################### - jetstream { - {{- if .Values.nats.jetstream.encryption }} - {{- if .Values.nats.jetstream.encryption.key }} - key: {{ .Values.nats.jetstream.encryption.key | quote }} - {{- else if .Values.nats.jetstream.encryption.secret }} - key: $JS_KEY - {{- end}} - {{- if .Values.nats.jetstream.encryption.cipher }} - cipher: {{ .Values.nats.jetstream.encryption.cipher }} - {{- end}} - {{- end}} - - {{- if .Values.nats.jetstream.memStorage.enabled }} - max_mem: {{ .Values.nats.jetstream.memStorage.size }} - {{- end }} - - {{- if .Values.nats.jetstream.domain }} - domain: {{ .Values.nats.jetstream.domain }} - {{- end }} - - {{- if .Values.nats.jetstream.fileStorage.enabled }} - store_dir: {{ .Values.nats.jetstream.fileStorage.storageDirectory }} - - max_file: - {{- if .Values.nats.jetstream.fileStorage.existingClaim }} - {{- .Values.nats.jetstream.fileStorage.claimStorageSize }} - {{- else }} - {{- .Values.nats.jetstream.fileStorage.size }} - {{- end }} - {{- else }} - {{- if .Values.nats.jetstream.store_dir }} - store_dir: {{ .Values.nats.jetstream.store_dir }} - {{- end }} - {{- if .Values.nats.jetstream.max_file }} - max_file: {{ .Values.nats.jetstream.max_file }} - {{- end }} - {{- end }} - - {{- if .Values.nats.jetstream.uniqueTag }} - unique_tag: {{ .Values.nats.jetstream.uniqueTag }} - {{- end }} - - {{- if .Values.nats.jetstream.maxOutstandingCatchup }} - max_outstanding_catchup: {{ .Values.nats.jetstream.maxOutstandingCatchup }} - {{- end }} - } - {{- end }} - - {{- if .Values.nats.mappings }} - ################################### - # # - # Mappings # - # # - ################################### - mappings: {{ toRawJson .Values.nats.mappings }} - {{- end }} - - {{- if .Values.mqtt.enabled }} - ################################### - # # - # NATS MQTT # - # # - ################################### - mqtt { - port: 1883 - - {{- with .Values.mqtt.tls }} - {{- $mqtt_tls := merge (dict) . }} - {{- $_ := set $mqtt_tls "secretPath" "/etc/nats-certs/mqtt" }} - {{- tpl (include "nats.tlsConfig" $mqtt_tls) $ | nindent 6}} - {{- end }} - - {{- if .Values.mqtt.noAuthUser }} - no_auth_user: {{ .Values.mqtt.noAuthUser | quote }} - {{- end }} - - ack_wait: {{ .Values.mqtt.ackWait | quote }} - max_ack_pending: {{ .Values.mqtt.maxAckPending }} - } - {{- end }} - - {{- if .Values.cluster.enabled }} - ################################### - # # - # NATS Full Mesh Clustering Setup # - # # - ################################### - cluster { - port: 6222 - - {{- if .Values.nats.jetstream.enabled }} - {{- if .Values.cluster.name }} - name: {{ .Values.cluster.name }} - {{- else }} - name: {{ template "nats.name" . }} - {{- end }} - {{- else }} - {{- with .Values.cluster.name }} - name: {{ . }} - {{- end }} - {{- end }} - - {{- with .Values.cluster.tls }} - {{- $cluster_tls := merge (dict) . }} - {{- $_ := set $cluster_tls "secretPath" "/etc/nats-certs/cluster" }} - {{- tpl (include "nats.tlsConfig" $cluster_tls) $ | nindent 6}} - {{- end }} - - {{- if .Values.cluster.authorization }} - authorization { - {{- with .Values.cluster.authorization.user }} - user: {{ . }} - {{- end }} - {{- with .Values.cluster.authorization.password }} - password: {{ . }} - {{- end }} - {{- with .Values.cluster.authorization.timeout }} - timeout: {{ . }} - {{- end }} - } - {{- end }} - - routes = [ - {{ include "nats.clusterRoutes" . }} - {{ include "nats.extraRoutes" . }} - ] - cluster_advertise: $CLUSTER_ADVERTISE - - {{- with .Values.cluster.noAdvertise }} - no_advertise: {{ . }} - {{- end }} - - connect_retries: {{ .Values.nats.connectRetries }} - } - {{- end }} - - {{- if and .Values.nats.advertise .Values.nats.externalAccess }} - include "advertise/client_advertise.conf" - {{- end }} - - {{- if or .Values.leafnodes.enabled .Values.leafnodes.remotes }} - ################# - # # - # NATS Leafnode # - # # - ################# - leafnodes { - {{- if .Values.leafnodes.enabled }} - listen: "0.0.0.0:{{ .Values.leafnodes.port }}" - {{- end }} - - {{- if and .Values.nats.advertise .Values.nats.externalAccess }} - include "advertise/gateway_advertise.conf" - {{- end }} - - {{- with .Values.leafnodes.noAdvertise }} - no_advertise: {{ . }} - {{- end }} - - {{- with .Values.leafnodes.authorization }} - authorization: { - {{- with .user }} - user: {{ . }} - {{- end }} - {{- with .password }} - password: {{ . }} - {{- end }} - {{- with .account }} - account: {{ . | quote }} - {{- end }} - {{- with .timeout }} - timeout: {{ . }} - {{- end }} - {{- with .users }} - users: [ - {{- range . }} - {{- toRawJson . | nindent 10 }}, - {{- end }} - ] - {{- end }} - } - {{- end }} - - {{- with .Values.leafnodes.tls }} - {{- if .custom }} - tls { - {{- .custom | nindent 8 }} - } - {{- else }} - {{- $leafnode_tls := merge (dict) . }} - {{- $_ := set $leafnode_tls "secretPath" "/etc/nats-certs/leafnodes" }} - {{- tpl (include "nats.tlsConfig" $leafnode_tls) $ | nindent 6}} - {{- end }} - {{- end }} - - remotes: [ - {{- range .Values.leafnodes.remotes }} - { - {{- with .url }} - url: {{ . | quote }} - {{- end }} - - {{- with .urls }} - urls: {{ toRawJson . }} - {{- end }} - - {{- with .account }} - account: {{ . | quote }} - {{- end }} - - {{- with .credentials }} - credentials: "/etc/nats-creds/{{ .secret.name }}/{{ .secret.key }}" - {{- end }} - - {{- with .tls }} - tls: { - {{- if .custom }} - {{- .custom | nindent 10 }} - {{- else }} - {{ $secretName := tpl .secret.name $ }} - {{- with .cert }} - cert_file: /etc/nats-certs/leafnodes/{{ $secretName }}/{{ . }} - {{- end }} - - {{- with .key }} - key_file: /etc/nats-certs/leafnodes/{{ $secretName }}/{{ . }} - {{- end }} - - {{- with .ca }} - ca_file: /etc/nats-certs/leafnodes/{{ $secretName }}/{{ . }} - {{- end }} - {{- end }} - } - {{- end }} - } - {{- end }} - ] - } - {{- end }} - - {{- if .Values.gateway.enabled }} - ################# - # # - # NATS Gateways # - # # - ################# - gateway { - name: {{ .Values.gateway.name }} - port: {{ .Values.gateway.port }} - - {{- if .Values.gateway.advertise }} - advertise: {{ .Values.gateway.advertise }} - {{- end }} - - {{- if .Values.gateway.rejectUnknownCluster }} - reject_unknown_cluster: {{ .Values.gateway.rejectUnknownCluster }} - {{- end }} - - {{- if .Values.gateway.authorization }} - authorization { - {{- with .Values.gateway.authorization.user }} - user: {{ . }} - {{- end }} - {{- with .Values.gateway.authorization.password }} - password: {{ . }} - {{- end }} - {{- with .Values.gateway.authorization.timeout }} - timeout: {{ . }} - {{- end }} - } - {{- end }} - - {{- if and .Values.nats.advertise .Values.nats.externalAccess }} - include "advertise/gateway_advertise.conf" - {{- end }} - - {{- if .Values.gateway.connectRetries }} - connect_retries: {{ .Values.gateway.connectRetries }} - {{- end }} - - {{- with .Values.gateway.tls }} - {{- $gateway_tls := merge (dict) . }} - {{- $_ := set $gateway_tls "secretPath" "/etc/nats-certs/gateways" }} - {{- tpl (include "nats.tlsConfig" $gateway_tls) $ | nindent 6}} - {{- end }} - - # Gateways array here - gateways: [ - {{- range .Values.gateway.gateways }} - { - {{- with .name }} - name: {{ . }} - {{- end }} - - {{- with .url }} - url: {{ . | quote }} - {{- end }} - - {{- with .urls }} - urls: [{{ join "," . }}] - {{- end }} - }, - {{- end }} - ] - } - {{- end }} - - {{- with .Values.nats.logging.debug }} - debug: {{ . }} - {{- end }} - - {{- with .Values.nats.logging.trace }} - trace: {{ . }} - {{- end }} - - {{- with .Values.nats.logging.logtime }} - logtime: {{ . }} - {{- end }} - - {{- with .Values.nats.logging.connectErrorReports }} - connect_error_reports: {{ . }} - {{- end }} - - {{- with .Values.nats.logging.reconnectErrorReports }} - reconnect_error_reports: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxConnections }} - max_connections: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxSubscriptions }} - max_subscriptions: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxPending }} - max_pending: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxControlLine }} - max_control_line: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxPayload }} - max_payload: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.pingInterval }} - ping_interval: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxPings }} - ping_max: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.writeDeadline }} - write_deadline: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.lameDuckGracePeriod }} - lame_duck_grace_period: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.lameDuckDuration }} - lame_duck_duration: {{ . }} - {{- end }} - - {{- if .Values.websocket.enabled }} - ################## - # # - # Websocket # - # # - ################## - websocket { - port: {{ .Values.websocket.port }} - {{- with .Values.websocket.tls }} - {{ $secretName := tpl .secret.name $ }} - tls { - {{- with .cert }} - cert_file: /etc/nats-certs/ws/{{ $secretName }}/{{ . }} - {{- end }} - - {{- with .key }} - key_file: /etc/nats-certs/ws/{{ $secretName }}/{{ . }} - {{- end }} - - {{- with .ca }} - ca_file: /etc/nats-certs/ws/{{ $secretName }}/{{ . }} - {{- end }} - } - {{- else }} - no_tls: {{ .Values.websocket.noTLS }} - {{- end }} - same_origin: {{ .Values.websocket.sameOrigin }} - {{- with .Values.websocket.allowedOrigins }} - allowed_origins: {{ toRawJson . }} - {{- end }} - {{- with .Values.websocket.advertise }} - advertise: {{ . }} - {{- end }} - {{- with .Values.websocket.handshakeTimeout }} - handshake_timeout: {{ . | quote }} - {{- end }} - } - {{- end }} - - {{- if .Values.auth.enabled }} - ################## - # # - # Authorization # - # # - ################## - {{- if .Values.auth.resolver }} - {{- if eq .Values.auth.resolver.type "memory" }} - resolver: MEMORY - include "accounts/{{ .Values.auth.resolver.configMap.key }}" - {{- end }} - - {{- if eq .Values.auth.resolver.type "full" }} - {{- if .Values.auth.resolver.configMap }} - include "accounts/{{ .Values.auth.resolver.configMap.key }}" - {{- else }} - {{- with .Values.auth.resolver }} - {{- if $.Values.auth.timeout }} - authorization { - timeout: {{ $.Values.auth.timeout }} - } - {{- end }} - - {{- if .operator }} - operator: {{ .operator }} - {{- end }} - - {{- if .systemAccount }} - system_account: {{ .systemAccount | quote }} - {{- end }} - {{- end }} - - resolver: { - type: full - {{- with .Values.auth.resolver }} - dir: {{ .store.dir | quote }} - - allow_delete: {{ .allowDelete }} - - interval: {{ .interval | quote }} - {{- end }} - } - {{- end }} - {{- end }} - - {{- if .Values.auth.resolver.resolverPreload }} - resolver_preload: {{ toRawJson .Values.auth.resolver.resolverPreload }} - {{- end }} - - {{- if eq .Values.auth.resolver.type "URL" }} - {{- with .Values.auth.resolver.url }} - resolver: URL({{ . }}) - {{- end }} - operator: /etc/nats-config/operator/{{ .Values.auth.operatorjwt.configMap.key }} - {{- end }} - {{- end }} - - {{- with .Values.auth.systemAccount }} - system_account: {{ . | quote }} - {{- end }} - - {{- with .Values.auth.token }} - authorization { - token: "{{ . }}" - - - {{- if $.Values.auth.timeout }} - timeout: {{ $.Values.auth.timeout }} - {{- end }} - } - {{- end }} - - {{- with .Values.auth.nkeys }} - {{- with .users }} - authorization { - {{- if $.Values.auth.timeout }} - timeout: {{ $.Values.auth.timeout }} - {{- end }} - - users: [ - {{- range . }} - {{- toRawJson . | nindent 8 }}, - {{- end }} - ] - } - {{- end }} - {{- end }} - - {{- with .Values.auth.basic }} - - {{- with .noAuthUser }} - no_auth_user: {{ . }} - {{- end }} - - {{- if or .users (or .timeout .defaultPermissions) }} - authorization { - {{- if $.Values.auth.timeout }} - timeout: {{ $.Values.auth.timeout }} - {{- end }} - - {{- with .users }} - users: [ - {{- range . }} - {{- toRawJson . | nindent 8 }}, - {{- end }} - ] - {{- end }} - - {{- with $.Values.auth.basic.defaultPermissions }} - default_permissions: { - {{- if .publish }} - publish: [ - {{- range .publish }} - {{- toRawJson . | nindent 10 }}, - {{- end }} - ], - {{- end }} - {{- if .subscribe }} - subscribe: [ - {{- range .subscribe }} - {{- toRawJson . | nindent 10 }}, - {{- end }} - ], - {{- end }} - } - {{- end }} - } - {{- end }} - - {{- with .accounts }} - authorization { - {{- if $.Values.auth.timeout }} - timeout: {{ $.Values.auth.timeout }} - {{- end }} - } - accounts: {{- toRawJson . }} - {{- end }} - - {{- end }} - - {{- end }} diff --git a/helm/charts/nats/templates/extra-resources.yaml b/helm/charts/nats/templates/extra-resources.yaml new file mode 100644 index 00000000..c11f0085 --- /dev/null +++ b/helm/charts/nats/templates/extra-resources.yaml @@ -0,0 +1,5 @@ +{{- include "nats.defaultValues" . }} +{{- range .Values.extraResources }} +--- +{{ . | toYaml }} +{{- end }} diff --git a/helm/charts/nats/templates/headless-service.yaml b/helm/charts/nats/templates/headless-service.yaml new file mode 100644 index 00000000..f11a83d1 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/ingress.yaml b/helm/charts/nats/templates/ingress.yaml new file mode 100644 index 00000000..eccd73ff --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/nats-box.yaml b/helm/charts/nats/templates/nats-box.yaml deleted file mode 100644 index e94362f4..00000000 --- a/helm/charts/nats/templates/nats-box.yaml +++ /dev/null @@ -1,121 +0,0 @@ -{{- if .Values.natsbox.enabled }} -{{- include "nats.fixImage" .Values.natsbox -}} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "nats.fullname" . }}-box - namespace: {{ include "nats.namespace" . }} - labels: - app: {{ include "nats.fullname" . }}-box - chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} - {{- if .Values.natsbox.additionalLabels }} - {{- tpl (toYaml .Values.natsbox.additionalLabels) $ | nindent 4 }} - {{- end }} - {{- if .Values.natsbox.annotations }} - annotations: - {{- toYaml .Values.natsbox.annotations | nindent 4 }} - {{- end }} -spec: - replicas: 1 - selector: - matchLabels: - app: {{ include "nats.fullname" . }}-box - template: - metadata: - labels: - app: {{ include "nats.fullname" . }}-box - {{- if .Values.natsbox.podLabels }} - {{- tpl (toYaml .Values.natsbox.podLabels) $ | nindent 8 }} - {{- end }} - {{- if .Values.natsbox.podAnnotations }} - annotations: - {{- toYaml .Values.natsbox.podAnnotations | nindent 8 }} - {{- end }} - spec: - {{- with .Values.natsbox.affinity }} - affinity: - {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} - {{- with .Values.natsbox.nodeSelector }} - nodeSelector: {{ toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.natsbox.tolerations }} - tolerations: {{ toYaml . | nindent 8 }} - {{- end }} - volumes: - {{- if .Values.natsbox.credentials }} - - name: nats-sys-creds - secret: - secretName: {{ .Values.natsbox.credentials.secret.name }} - {{- end }} - {{- if .Values.natsbox.extraVolumes }} - {{- toYaml .Values.natsbox.extraVolumes | nindent 6}} - {{- end }} - {{- with .Values.nats.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-clients-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.securityContext }} - securityContext: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- if hasKey .Values.natsbox "automountServiceAccountToken" }} - automountServiceAccountToken: {{ .Values.natsbox.automountServiceAccountToken }} - {{- end }} - containers: - - name: nats-box - image: {{ include "nats.image" .Values.natsbox.image }} - imagePullPolicy: {{ .Values.natsbox.image.pullPolicy }} - {{- if .Values.natsbox.securityContext }} - securityContext: - {{- toYaml .Values.natsbox.securityContext | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.natsbox.resources | nindent 10 }} - env: - - name: NATS_URL - value: {{ template "nats.fullname" . }} - {{- if .Values.natsbox.credentials }} - - name: NATS_CREDS - value: /etc/nats-config/creds/{{ .Values.natsbox.credentials.secret.key }} - {{- end }} - {{- with .Values.nats.tls }} - {{ $secretName := tpl .secret.name $ }} - lifecycle: - postStart: - exec: - command: - - /bin/sh - - -c - - cp /etc/nats-certs/clients/{{ $secretName }}/* /usr/local/share/ca-certificates && update-ca-certificates - {{- end }} - command: - - "tail" - - "-f" - - "/dev/null" - volumeMounts: - {{- if .Values.natsbox.credentials }} - - name: nats-sys-creds - mountPath: /etc/nats-config/creds - {{- end }} - {{- if .Values.natsbox.extraVolumeMounts }} - {{- toYaml .Values.natsbox.extraVolumeMounts | nindent 8 }} - {{- end }} - {{- with .Values.nats.tls }} - ####################### - # # - # TLS Volumes Mounts # - # # - ####################### - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-clients-volume - mountPath: /etc/nats-certs/clients/{{ $secretName }} - {{- end }} -{{- end }} diff --git a/helm/charts/nats/templates/nats-box/contents-secret.yaml b/helm/charts/nats/templates/nats-box/contents-secret.yaml new file mode 100644 index 00000000..db629bf7 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/nats-box/contexts-secret.yaml b/helm/charts/nats/templates/nats-box/contexts-secret.yaml new file mode 100644 index 00000000..5ae20f45 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/nats-box/deployment.yaml b/helm/charts/nats/templates/nats-box/deployment.yaml new file mode 100644 index 00000000..a063332a --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/nats-box/service-account.yaml b/helm/charts/nats/templates/nats-box/service-account.yaml new file mode 100644 index 00000000..e11bdd36 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/networkpolicy.yaml b/helm/charts/nats/templates/networkpolicy.yaml deleted file mode 100644 index 9951441e..00000000 --- a/helm/charts/nats/templates/networkpolicy.yaml +++ /dev/null @@ -1,79 +0,0 @@ -{{- if .Values.networkPolicy.enabled }} -kind: NetworkPolicy -apiVersion: {{ template "networkPolicy.apiVersion" . }} -metadata: - name: {{ include "nats.fullname" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} -spec: - podSelector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 6 }} - policyTypes: - - Ingress - - Egress - egress: - # Allow dns resolution - - ports: - - port: 53 - protocol: UDP - # Allow outbound connections to other cluster pods - - ports: - - port: {{ .Values.nats.client.port }} - protocol: TCP - - port: 6222 - protocol: TCP - - port: 8222 - protocol: TCP - - port: 7777 - protocol: TCP - - port: {{ .Values.leafnodes.port }} - protocol: TCP - - port: {{ .Values.gateway.port }} - protocol: TCP - to: - - podSelector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 10 }} - {{- if .Values.networkPolicy.extraEgress }} - {{- include "tplvalues.render" ( dict "value" .Values.networkPolicy.extraEgress "context" $ ) | nindent 2 }} - {{- end }} - ingress: - # Allow inbound connections - - ports: - - port: {{ .Values.nats.client.port }} - protocol: TCP - - port: 6222 - protocol: TCP - - port: 8222 - protocol: TCP - - port: 7777 - protocol: TCP - - port: {{ .Values.leafnodes.port }} - protocol: TCP - - port: {{ .Values.gateway.port }} - protocol: TCP - {{- if not .Values.networkPolicy.allowExternal }} - from: - - podSelector: - matchLabels: - {{ include "nats.fullname" . }}-client: "true" - - podSelector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 10 }} - {{- if .Values.networkPolicy.ingressNSMatchLabels }} - - namespaceSelector: - matchLabels: - {{- toYaml .Values.networkPolicy.ingressNSMatchLabels | nindent 10 }} - {{- if .Values.networkPolicy.ingressNSPodMatchLabels }} - podSelector: - matchLabels: - {{- toYaml .Values.networkPolicy.ingressNSPodMatchLabels | nindent 10 }} - {{- end }} - {{- end }} - {{- end }} - {{- if .Values.networkPolicy.extraIngress }} - {{- include "tplvalues.render" ( dict "value" .Values.networkPolicy.extraIngress "context" $ ) | nindent 2 }} - {{- end }} -{{- end }} diff --git a/helm/charts/nats/templates/pdb.yaml b/helm/charts/nats/templates/pdb.yaml deleted file mode 100644 index 5a7cb438..00000000 --- a/helm/charts/nats/templates/pdb.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.podDisruptionBudget.enabled }} ---- -apiVersion: {{ .Capabilities.APIVersions.Has "policy/v1" | ternary "policy/v1" "policy/v1beta1" }} -kind: PodDisruptionBudget -metadata: - name: {{ include "nats.fullname" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} -spec: - {{- if .Values.podDisruptionBudget.minAvailable }} - minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} - {{- end }} - {{- if .Values.podDisruptionBudget.maxUnavailable }} - maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} - {{- end }} - selector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 6 }} -{{- end }} diff --git a/helm/charts/nats/templates/pod-disruption-budget.yaml b/helm/charts/nats/templates/pod-disruption-budget.yaml new file mode 100644 index 00000000..91172262 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/pod-monitor.yaml b/helm/charts/nats/templates/pod-monitor.yaml new file mode 100644 index 00000000..0e42a43a --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/rbac.yaml b/helm/charts/nats/templates/rbac.yaml deleted file mode 100644 index 7b55aeb6..00000000 --- a/helm/charts/nats/templates/rbac.yaml +++ /dev/null @@ -1,39 +0,0 @@ -{{- if or (.Values.nats.serviceAccount.create) (and .Values.nats.externalAccess .Values.nats.advertise) }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "nats.serviceAccountName" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} - {{- with .Values.nats.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} -{{- if and .Values.nats.externalAccess .Values.nats.advertise }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "nats.serviceAccountName" . }} -rules: -- apiGroups: [""] - resources: - - nodes - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "nats.serviceAccountName" . }}-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "nats.serviceAccountName" . }} -subjects: -- kind: ServiceAccount - name: {{ include "nats.serviceAccountName" . }} - namespace: {{ include "nats.namespace" . }} -{{- end }} diff --git a/helm/charts/nats/templates/service-account.yaml b/helm/charts/nats/templates/service-account.yaml new file mode 100644 index 00000000..6c763bd3 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/service.yaml b/helm/charts/nats/templates/service.yaml index 361e725b..04b0b37e 100644 --- a/helm/charts/nats/templates/service.yaml +++ b/helm/charts/nats/templates/service.yaml @@ -1,74 +1,6 @@ -{{- $appProtocol := semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "nats.fullname" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} - {{- if .Values.serviceAnnotations}} - annotations: - {{- toYaml .Values.serviceAnnotations | nindent 4 }} - {{- end }} -spec: - selector: - {{- include "nats.selectorLabels" . | nindent 4 }} - clusterIP: None - publishNotReadyAddresses: true - {{- if .Values.topologyKeys }} - topologyKeys: - {{- toYaml .Values.topologyKeys | nindent 4 }} - {{- end }} - ports: - {{- if .Values.websocket.enabled }} - - name: websocket - port: {{ .Values.websocket.port }} - {{- if $appProtocol }} - appProtocol: tcp - {{- end }} - {{- end }} - {{- if .Values.nats.profiling.enabled }} - - name: profiling - port: {{ .Values.nats.profiling.port }} - {{- if $appProtocol }} - appProtocol: http - {{- end }} - {{- end }} - - name: {{ .Values.nats.client.portName }} - port: {{ .Values.nats.client.port }} - {{- if $appProtocol }} - appProtocol: tcp - {{- end }} - - name: cluster - port: 6222 - {{- if $appProtocol }} - appProtocol: tcp - {{- end }} - - name: monitor - port: 8222 - {{- if $appProtocol }} - appProtocol: http - {{- end }} - - name: {{ .Values.exporter.portName }} - port: 7777 - {{- if $appProtocol }} - appProtocol: http - {{- end }} - - name: leafnodes - port: {{ .Values.leafnodes.port }} - {{- if $appProtocol }} - appProtocol: tcp - {{- end }} - - name: gateways - port: {{ .Values.gateway.port }} - {{- if $appProtocol }} - appProtocol: tcp - {{- end }} - {{- if .Values.mqtt.enabled }} - - name: mqtt - port: 1883 - {{- if $appProtocol }} - appProtocol: tcp - {{- end }} - {{- end }} +{{- include "nats.defaultValues" . }} +{{- with .Values.service }} +{{- if .enabled }} +{{- include "nats.loadMergePatch" (merge (dict "file" "service.yaml" "ctx" $) .) }} +{{- end }} +{{- end }} diff --git a/helm/charts/nats/templates/serviceMonitor.yaml b/helm/charts/nats/templates/serviceMonitor.yaml deleted file mode 100644 index 282f50f5..00000000 --- a/helm/charts/nats/templates/serviceMonitor.yaml +++ /dev/null @@ -1,36 +0,0 @@ -{{ if and .Values.exporter.enabled .Values.exporter.serviceMonitor.enabled }} -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: {{ template "nats.fullname" . }} - {{- if .Values.exporter.serviceMonitor.namespace }} - namespace: {{ .Values.exporter.serviceMonitor.namespace }} - {{- else }} - namespace: {{ include "nats.namespace" . }} - {{- end }} - {{- if .Values.exporter.serviceMonitor.labels }} - labels: - {{- toYaml .Values.exporter.serviceMonitor.labels | nindent 4 }} - {{- end }} - {{- if .Values.exporter.serviceMonitor.annotations }} - annotations: - {{- toYaml .Values.exporter.serviceMonitor.annotations | nindent 4 }} - {{- end }} -spec: - endpoints: - - port: {{ .Values.exporter.portName }} - {{- if .Values.exporter.serviceMonitor.path }} - path: {{ .Values.exporter.serviceMonitor.path }} - {{- end }} - {{- if .Values.exporter.serviceMonitor.interval }} - interval: {{ .Values.exporter.serviceMonitor.interval }} - {{- end }} - {{- if .Values.exporter.serviceMonitor.scrapeTimeout }} - scrapeTimeout: {{ .Values.exporter.serviceMonitor.scrapeTimeout }} - {{- end }} - namespaceSelector: - any: true - selector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 6 }} -{{- end }} diff --git a/helm/charts/nats/templates/stateful-set.yaml b/helm/charts/nats/templates/stateful-set.yaml new file mode 100644 index 00000000..bb198323 --- /dev/null +++ b/helm/charts/nats/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/helm/charts/nats/templates/statefulset.yaml b/helm/charts/nats/templates/statefulset.yaml deleted file mode 100644 index 1ea285fc..00000000 --- a/helm/charts/nats/templates/statefulset.yaml +++ /dev/null @@ -1,650 +0,0 @@ -{{- include "nats.fixImage" .Values.nats -}} -{{- include "nats.fixImage" .Values.bootconfig -}} -{{- include "nats.fixImage" .Values.reloader -}} -{{- include "nats.fixImage" .Values.exporter -}} ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ include "nats.fullname" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} - {{- if .Values.statefulSetAnnotations }} - annotations: - {{- toYaml .Values.statefulSetAnnotations | nindent 4 }} - {{- end }} -spec: - selector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 6 }} - {{- if .Values.cluster.enabled }} - replicas: {{ .Values.cluster.replicas }} - {{- else }} - replicas: 1 - {{- end }} - serviceName: {{ include "nats.fullname" . }} - - podManagementPolicy: {{ .Values.podManagementPolicy }} - - template: - metadata: - {{- if or .Values.exporter.enabled .Values.nats.configChecksumAnnotation .Values.podAnnotations }} - annotations: - {{- if .Values.exporter.enabled }} - prometheus.io/path: /metrics - prometheus.io/port: "7777" - prometheus.io/scrape: "true" - {{- end }} - {{- if .Values.nats.configChecksumAnnotation }} - checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - {{- end }} - {{- if .Values.podAnnotations }} - {{- toYaml .Values.podAnnotations | nindent 8 }} - {{- end }} - {{- end }} - labels: - {{- include "nats.selectorLabels" . | nindent 8 }} - {{- if .Values.statefulSetPodLabels }} - {{- tpl (toYaml .Values.statefulSetPodLabels) . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.securityContext }} - securityContext: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} - {{- with .Values.nodeSelector }} - nodeSelector: {{ toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: {{ toYaml . | nindent 8 }} - {{- end }} - {{- if .Values.topologySpreadConstraints }} - topologySpreadConstraints: - {{- range .Values.topologySpreadConstraints }} - {{- if and .maxSkew .topologyKey }} - - maxSkew: {{ .maxSkew }} - topologyKey: {{ .topologyKey }} - {{- if .whenUnsatisfiable }} - whenUnsatisfiable: {{ .whenUnsatisfiable }} - {{- end }} - labelSelector: - matchLabels: - {{- include "nats.selectorLabels" $ | nindent 12 }} - {{- end }} - {{- end }} - {{- end }} - {{- if .Values.priorityClassName }} - priorityClassName: {{ .Values.priorityClassName | quote }} - {{- end }} - {{- with .Values.nats.dnsPolicy }} - dnsPolicy: {{ . }} - {{- end }} - {{- with .Values.nats.hostNetwork }} - hostNetwork: {{ . }} - {{- end }} - # Common volumes for the containers. - volumes: - - name: config-volume - {{- if .Values.nats.customConfigSecret }} - secret: - secretName: {{ .Values.nats.customConfigSecret.name }} - {{- else }} - configMap: - name: {{ include "nats.fullname" . }}-config - {{- end }} - - {{- /* User extended config volumes*/}} - {{- if .Values.nats.config }} - # User extended config volumes - {{- with .Values.nats.config }} - {{- toYaml . | nindent 6 }} - {{- end }} - {{- end }} - - # Local volume shared with the reloader. - - name: pid - {{- toYaml .Values.pidVolume | nindent 8 }} - - {{- if and .Values.auth.enabled .Values.auth.resolver }} - {{- if .Values.auth.resolver.configMap }} - - name: resolver-volume - configMap: - name: {{ .Values.auth.resolver.configMap.name }} - {{- end }} - - {{- if eq .Values.auth.resolver.type "URL" }} - - name: operator-jwt-volume - configMap: - name: {{ .Values.auth.operatorjwt.configMap.name }} - {{- end }} - {{- end }} - - {{- if and .Values.nats.externalAccess .Values.nats.advertise }} - # Local volume shared with the advertise config initializer. - - name: advertiseconfig - {{- toYaml .Values.advertiseconfigVolume | nindent 8 }} - {{- end }} - - {{- if and .Values.nats.jetstream.enabled .Values.nats.jetstream.fileStorage.enabled .Values.nats.jetstream.fileStorage.existingClaim }} - # Persistent volume for jetstream running with file storage option - - name: {{ include "nats.fullname" . }}-js-pvc - persistentVolumeClaim: - claimName: {{ .Values.nats.jetstream.fileStorage.existingClaim | quote }} - {{- end }} - - ################# - # # - # TLS Volumes # - # # - ################# - {{- with .Values.nats.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-clients-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.mqtt.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-mqtt-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.cluster.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-cluster-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.leafnodes.tls }} - {{- if not .custom }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-leafnodes-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- end }} - {{- with .Values.gateway.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-gateways-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.websocket.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-ws-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- if .Values.leafnodes.enabled }} - # - # Leafnode credential volumes - # - {{- range .Values.leafnodes.remotes }} - {{- with .credentials }} - - name: {{ .secret.name }}-volume - secret: - secretName: {{ .secret.name }} - {{- end }} - {{- with .tls }} - - name: {{ .secret.name }}-volume - secret: - secretName: {{ .secret.name }} - {{- end }} - {{- end }} - {{- end }} - - {{- if .Values.additionalVolumes }} - {{- toYaml .Values.additionalVolumes | nindent 6 }} - {{- end }} - - serviceAccountName: {{ include "nats.serviceAccountName" . }} - {{- if hasKey .Values.nats "automountServiceAccountToken" }} - automountServiceAccountToken: {{ .Values.nats.automountServiceAccountToken }} - {{- end }} - - # Required to be able to HUP signal and apply config - # reload to the server without restarting the pod. - shareProcessNamespace: true - - {{- if and .Values.nats.externalAccess .Values.nats.advertise }} - # Initializer container required to be able to lookup - # the external ip on which this node is running. - initContainers: - - name: bootconfig - command: - - nats-pod-bootconfig - - -f - - /etc/nats-config/advertise/client_advertise.conf - - -gf - - /etc/nats-config/advertise/gateway_advertise.conf - env: - - name: KUBERNETES_NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - image: {{ include "nats.image" .Values.bootconfig.image }} - imagePullPolicy: {{ .Values.bootconfig.image.pullPolicy }} - {{- if .Values.bootconfig.securityContext }} - securityContext: - {{- toYaml .Values.bootconfig.securityContext | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.bootconfig.resources | nindent 10 }} - volumeMounts: - - mountPath: /etc/nats-config/advertise - name: advertiseconfig - subPath: advertise - {{- end }} - - ################# - # # - # NATS Server # - # # - ################# - terminationGracePeriodSeconds: {{ .Values.nats.terminationGracePeriodSeconds }} - containers: - - name: nats - image: {{ include "nats.image" .Values.nats.image }} - imagePullPolicy: {{ .Values.nats.image.pullPolicy }} - {{- if .Values.nats.securityContext }} - securityContext: - {{- toYaml .Values.nats.securityContext | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.nats.resources | nindent 10 }} - ports: - - containerPort: {{ .Values.nats.client.port }} - name: {{ .Values.nats.client.portName }} - {{- if .Values.nats.externalAccess }} - hostPort: {{ .Values.nats.client.port }} - {{- end }} - {{- if .Values.leafnodes.enabled }} - - containerPort: {{ .Values.leafnodes.port }} - name: leafnodes - {{- if .Values.nats.externalAccess }} - hostPort: {{ .Values.leafnodes.port }} - {{- end }} - {{- end }} - {{- if .Values.gateway.enabled }} - - containerPort: {{ .Values.gateway.port }} - name: gateways - {{- if .Values.nats.externalAccess }} - hostPort: {{ .Values.gateway.port }} - {{- end }} - {{- end }} - - containerPort: 6222 - name: cluster - - containerPort: 8222 - name: monitor - {{- if .Values.mqtt.enabled }} - - containerPort: 1883 - name: mqtt - {{- if .Values.nats.externalAccess }} - hostPort: 1883 - {{- end }} - {{- end }} - {{- if .Values.websocket.enabled }} - - containerPort: {{ .Values.websocket.port }} - name: websocket - {{- if .Values.nats.externalAccess }} - hostPort: {{ .Values.websocket.port }} - {{- end }} - {{- end }} - {{- if .Values.nats.profiling.enabled }} - - containerPort: {{ .Values.nats.profiling.port }} - name: profiling - {{- end }} - - command: - - "nats-server" - - "--config" - - "/etc/nats-config/nats.conf" - {{- if .Values.nats.profiling.enabled }} - - "--profile={{ .Values.nats.profiling.port }}" - {{- end }} - - # Required to be able to define an environment variable - # that refers to other environment variables. This env var - # is later used as part of the configuration file. - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: SERVER_NAME - value: {{ .Values.nats.serverNamePrefix }}$(POD_NAME) - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: CLUSTER_ADVERTISE - value: {{ include "nats.clusterAdvertise" . }} - {{- if .Values.nats.gomemlimit }} - - name: GOMEMLIMIT - value: {{ .Values.nats.gomemlimit | quote }} - {{- end }} - {{- if .Values.nats.extraEnv }} - {{- toYaml .Values.nats.extraEnv | nindent 8 }} - {{- end }} - - {{- if .Values.nats.jetstream.enabled }} - {{- with .Values.nats.jetstream.encryption }} - {{- with .secret }} - - name: JS_KEY - valueFrom: - secretKeyRef: - name: {{ .name }} - key: {{ .key }} - {{- end }} - {{- end }} - {{- end }} - volumeMounts: - - name: config-volume - mountPath: /etc/nats-config - - name: pid - mountPath: /var/run/nats - {{- if and .Values.nats.externalAccess .Values.nats.advertise }} - - mountPath: /etc/nats-config/advertise - name: advertiseconfig - subPath: advertise - {{- end }} - - {{- /* User extended config volumes*/}} - {{- range .Values.nats.config }} - # User extended config volumes - - name: {{ .name }} - mountPath: /etc/nats-config/{{ .name }} - {{- end }} - - - {{- if and .Values.auth.enabled .Values.auth.resolver }} - {{- if eq .Values.auth.resolver.type "memory" }} - - name: resolver-volume - mountPath: /etc/nats-config/accounts - {{- end }} - - {{- if eq .Values.auth.resolver.type "full" }} - {{- if .Values.auth.resolver.configMap }} - - name: resolver-volume - mountPath: /etc/nats-config/accounts - {{- end }} - {{- if and .Values.auth.resolver .Values.auth.resolver.store }} - - name: nats-jwt-pvc - mountPath: {{ .Values.auth.resolver.store.dir }} - {{- end }} - {{- end }} - - {{- if eq .Values.auth.resolver.type "URL" }} - - name: operator-jwt-volume - mountPath: /etc/nats-config/operator - {{- end }} - {{- end }} - - {{- if and .Values.nats.jetstream.enabled .Values.nats.jetstream.fileStorage.enabled }} - - name: {{ include "nats.fullname" . }}-js-pvc - mountPath: {{ .Values.nats.jetstream.fileStorage.storageDirectory }} - {{- end }} - - {{- include "nats.tlsVolumeMounts" . | nindent 8 }} - - {{- if .Values.leafnodes.enabled }} - # - # Leafnode credential volumes - # - {{- range .Values.leafnodes.remotes }} - {{- with .credentials }} - - name: {{ .secret.name }}-volume - mountPath: /etc/nats-creds/{{ .secret.name }} - {{- end }} - {{- with .tls }} - - name: {{ .secret.name }}-volume - mountPath: /etc/nats-certs/leafnodes/{{ .secret.name }} - {{- end }} - {{- end }} - {{- end }} - - {{- if .Values.additionalVolumeMounts }} - {{- toYaml .Values.additionalVolumeMounts | nindent 8 }} - {{- end }} - - ####################### - # # - # Healthcheck Probes # - # # - ####################### - {{- if .Values.nats.healthcheck }} - {{- $serverVersion := .Values.nats.image.tag | regexFind "\\d+(\\.\\d+)?(\\.\\d+)?" | default "2.9.0" }} - {{- $enableHealthzStartup := and .Values.nats.healthcheck.enableHealthz (or (not .Values.nats.healthcheck.detectHealthz) (semverCompare ">=2.7.1" $serverVersion)) }} - {{- $enableHealthzLivenessReadiness := and .Values.nats.healthcheck.enableHealthzLivenessReadiness (or (not .Values.nats.healthcheck.detectHealthz) (semverCompare ">=2.9.0" $serverVersion)) }} - {{- $healthzStartupEndpoint := "/healthz" }} - {{- $healthzLivenessEndpoint := "/healthz?js-enabled-only=true" }} - {{- $healthzReadinessEndpoint := "/healthz?js-server-only=true" }} - - {{- /* healthz options behaved differently in 2.9.0 - 2.9.9 https://github.com/nats-io/nats-server/pull/3704 */}} - {{- if (semverCompare "<=2.9.9" $serverVersion) }} - {{- $healthzLivenessEndpoint = "/healthz?js-server-only=true" }} - {{- $healthzReadinessEndpoint = "/healthz?js-server-only=true" }} - {{- if .Values.nats.jetstream.enabled }} - {{- $healthzLivenessEndpoint = print $healthzLivenessEndpoint "&js-enabled=true" }} - {{- $healthzReadinessEndpoint = print $healthzReadinessEndpoint "&js-enabled=true" }} - {{- end }} - {{- end }} - - {{- with .Values.nats.healthcheck.liveness }} - {{- if .enabled }} - livenessProbe: - {{- $probe := merge (dict) . }} - {{- $_ := unset $probe "enabled" }} - {{- $probeDefault := dict "httpGet" (dict "path" "/" "port" 8222) }} - {{- if $enableHealthzLivenessReadiness }} - # for NATS server versions >=2.9.0, {{ $healthzLivenessEndpoint }} will be enabled - # liveness probe checks that the JS server is enabled - {{- $_ := set $probeDefault.httpGet "path" $healthzLivenessEndpoint }} - {{- end }} - {{- $probe := merge $probe $probeDefault }} - {{- toYaml $probe | nindent 10}} - {{- end }} - {{- end }} - - {{- with .Values.nats.healthcheck.readiness }} - {{- if .enabled }} - readinessProbe: - {{- $probe := merge (dict) . }} - {{- $_ := unset $probe "enabled" }} - {{- $probeDefault := dict "httpGet" (dict "path" "/" "port" 8222) }} - {{- if $enableHealthzLivenessReadiness }} - # for NATS server versions >=2.9.0, {{ $healthzReadinessEndpoint }} will be enabled - # readiness probe checks that the JS server is enabled, and is current with the meta leader - {{- $_ := set $probeDefault.httpGet "path" $healthzReadinessEndpoint }} - {{- end }} - {{- $probe := merge $probe $probeDefault }} - {{- toYaml $probe | nindent 10}} - {{- end }} - {{- end }} - - {{- with .Values.nats.healthcheck.startup }} - {{- if .enabled }} - startupProbe: - {{- $probe := merge (dict) . }} - {{- $_ := unset $probe "enabled" }} - {{- $probeDefault := dict "httpGet" (dict "path" "/" "port" 8222) }} - {{- if $enableHealthzStartup }} - # for NATS server versions >=2.7.1, {{ $healthzStartupEndpoint}} will be enabled - # startup probe checks that the JS server is enabled, is current with the meta leader, - # and that all streams and consumers assigned to this JS server are current - {{- $_ := set $probeDefault.httpGet "path" $healthzStartupEndpoint }} - {{- end }} - {{- $probe := merge $probe $probeDefault }} - {{- toYaml $probe | nindent 10}} - {{- end }} - {{- end }} - - {{- end }} - - # Gracefully stop NATS Server on pod deletion or image upgrade. - # - lifecycle: - preStop: - exec: - # send the lame duck shutdown signal to trigger a graceful shutdown - # nats-server will ignore the TERM signal it receives after this - # - command: - - "nats-server" - - "-sl=ldm=/var/run/nats/nats.pid" - - ################################# - # # - # NATS Configuration Reloader # - # # - ################################# - {{- if .Values.reloader.enabled }} - - name: reloader - image: {{ include "nats.image" .Values.reloader.image }} - imagePullPolicy: {{ .Values.reloader.image.pullPolicy }} - {{- if .Values.reloader.securityContext }} - securityContext: - {{- toYaml .Values.reloader.securityContext | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.reloader.resources | nindent 10 }} - command: - - "nats-server-config-reloader" - - "-pid" - - "/var/run/nats/nats.pid" - - "-config" - - "/etc/nats-config/nats.conf" - {{- with .Values.nats.tls }} - {{- $nats_tls := merge (dict) . }} - {{- $_ := set $nats_tls "secretPath" "/etc/nats-certs/clients" }} - {{- tpl (include "nats.tlsReloaderArgs" $nats_tls) $ | nindent 8}} - {{- end }} - {{- with .Values.cluster.tls }} - {{- $nats_tls := merge (dict) . }} - {{- $_ := set $nats_tls "secretPath" "/etc/nats-certs/cluster" }} - {{- tpl (include "nats.tlsReloaderArgs" $nats_tls) $ | nindent 8}} - {{- end }} - {{- range .Values.reloader.extraConfigs }} - - "-config" - - {{ . | quote }} - {{- end }} - {{- range .Values.nats.config }} - - "-config" - - "/etc/nats-config/{{ .name }}/{{ .name }}.conf" - {{- end}} - volumeMounts: - - name: config-volume - mountPath: /etc/nats-config - - name: pid - mountPath: /var/run/nats - {{- include "nats.tlsVolumeMounts" . | nindent 8 }} - {{- if .Values.additionalVolumeMounts }} - {{- toYaml .Values.additionalVolumeMounts | nindent 8 }} - {{- end }} - {{- /* User extended config volumes*/}} - {{- range .Values.nats.config }} - # User extended config volumes - - name: {{ .name }} - mountPath: /etc/nats-config/{{ .name }} - {{- end }} - {{- end }} - - ############################## - # # - # NATS Prometheus Exporter # - # # - ############################## - {{- if .Values.exporter.enabled }} - - name: metrics - image: {{ include "nats.image" .Values.exporter.image }} - imagePullPolicy: {{ .Values.exporter.image.pullPolicy }} - {{- if .Values.exporter.securityContext }} - securityContext: - {{- toYaml .Values.exporter.securityContext | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.exporter.resources | nindent 10 }} - args: - {{- if .Values.exporter.args }} - {{- toYaml .Values.exporter.args | nindent 8 }} - {{- else }} - - -connz - - -routez - - -subz - - -varz - - -prefix=nats - - -use_internal_server_id - {{- if .Values.nats.jetstream.enabled }} - - -jsz=all - {{- end }} - {{- if .Values.leafnodes.enabled }} - - -leafz - {{- end }} - {{- if .Values.gateway.enabled }} - - -gatewayz - {{- end }} - - http://localhost:8222/ - {{- end }} - ports: - - containerPort: 7777 - name: {{ .Values.exporter.portName }} - {{- end }} - - {{- if .Values.additionalContainers }} - {{- toYaml .Values.additionalContainers | nindent 6 }} - {{- end }} - - volumeClaimTemplates: - {{- if eq .Values.auth.resolver.type "full" }} - {{- if and .Values.auth.resolver .Values.auth.resolver.store }} - ##################################### - # # - # Account Server Embedded JWT # - # # - ##################################### - - metadata: - name: nats-jwt-pvc - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.auth.resolver.store.size }} - {{- if .Values.auth.resolver.store.storageClassName }} - storageClassName: {{ .Values.auth.resolver.store.storageClassName | quote }} - {{- end }} - {{- end }} - {{- end }} - - {{- if and .Values.nats.jetstream.enabled .Values.nats.jetstream.fileStorage.enabled (not .Values.nats.jetstream.fileStorage.existingClaim) }} - ##################################### - # # - # Jetstream New Persistent Volume # - # # - ##################################### - - metadata: - name: {{ include "nats.fullname" . }}-js-pvc - {{- if .Values.nats.jetstream.fileStorage.annotations }} - annotations: - {{- toYaml .Values.nats.jetstream.fileStorage.annotations | nindent 10 }} - {{- end }} - spec: - accessModes: - {{- toYaml .Values.nats.jetstream.fileStorage.accessModes | nindent 10 }} - resources: - requests: - storage: {{ .Values.nats.jetstream.fileStorage.size }} - {{- if .Values.nats.jetstream.fileStorage.storageClassName }} - storageClassName: {{ .Values.nats.jetstream.fileStorage.storageClassName | quote }} - {{- end }} - {{- end }} diff --git a/helm/charts/nats/templates/tests/request-reply.yaml b/helm/charts/nats/templates/tests/request-reply.yaml new file mode 100644 index 00000000..4ce2bf83 --- /dev/null +++ b/helm/charts/nats/templates/tests/request-reply.yaml @@ -0,0 +1,35 @@ +{{- include "nats.defaultValues" . }} +{{- with .Values.natsBox | deepCopy }} +{{- $natsBox := . }} +{{- if .enabled -}} +apiVersion: v1 +kind: Pod +{{- with .container }} +{{- $_ := set . "merge" (dict + "args" (list + "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/helm/charts/nats/templates/tests/test-request-reply.yaml b/helm/charts/nats/templates/tests/test-request-reply.yaml deleted file mode 100644 index 829aca29..00000000 --- a/helm/charts/nats/templates/tests/test-request-reply.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "nats.fullname" . }}-test-request-reply" - labels: - chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} - app: {{ include "nats.fullname" . }}-test-request-reply - annotations: - "helm.sh/hook": test -spec: - containers: - - name: nats-box - image: {{ include "nats.image" .Values.natsbox.image }} - env: - - name: NATS_HOST - value: {{ template "nats.fullname" . }} - command: - - /bin/sh - - -ec - - | - nats reply -s nats://$NATS_HOST:{{ .Values.nats.client.port }} 'name.>' --command "echo {{1}}" & - - | - "&&" - - | - name=$(nats request -s nats://$NATS_HOST:{{ .Values.nats.client.port }} name.test '' 2>/dev/null) - - | - "&&" - - | - [ $name = test ] - - restartPolicy: Never diff --git a/helm/charts/nats/test/chart_test.go b/helm/charts/nats/test/chart_test.go new file mode 100644 index 00000000..bccc7884 --- /dev/null +++ b/helm/charts/nats/test/chart_test.go @@ -0,0 +1,254 @@ +package test + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/ghodss/yaml" + "github.com/gruntwork-io/terratest/modules/helm" + "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/nats-io/nats-server/v2/conf" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + policyv1 "k8s.io/api/policy/v1" +) + +type Resources struct { + Conf Resource[map[string]any] + ConfigMap Resource[corev1.ConfigMap] + HeadlessService Resource[corev1.Service] + Ingress Resource[networkingv1.Ingress] + NatsBoxContentsSecret Resource[corev1.Secret] + NatsBoxContextsSecret Resource[corev1.Secret] + NatsBoxDeployment Resource[appsv1.Deployment] + NatsBoxServiceAccount Resource[corev1.ServiceAccount] + PodDisruptionBudget Resource[policyv1.PodDisruptionBudget] + PodMonitor Resource[monitoringv1.PodMonitor] + Service Resource[corev1.Service] + ServiceAccount Resource[corev1.ServiceAccount] + StatefulSet Resource[appsv1.StatefulSet] + ExtraConfigMap Resource[corev1.ConfigMap] + ExtraService Resource[corev1.Service] +} + +func (r *Resources) Iter() []MutableResource { + return []MutableResource{ + r.Conf.Mutable(), + r.ConfigMap.Mutable(), + r.HeadlessService.Mutable(), + r.Ingress.Mutable(), + r.NatsBoxContentsSecret.Mutable(), + r.NatsBoxContextsSecret.Mutable(), + r.NatsBoxDeployment.Mutable(), + r.NatsBoxServiceAccount.Mutable(), + r.Service.Mutable(), + r.ServiceAccount.Mutable(), + r.StatefulSet.Mutable(), + r.PodMonitor.Mutable(), + r.ExtraConfigMap.Mutable(), + r.ExtraService.Mutable(), + } +} + +type Resource[T any] struct { + ID string + HasValue bool + Value T +} + +func (r *Resource[T]) Mutable() MutableResource { + return MutableResource{ + ID: r.ID, + HasValueP: &r.HasValue, + ValueP: &r.Value, + } +} + +type MutableResource struct { + ID string + HasValueP *bool + ValueP any +} + +type K8sResource struct { + Kind string `yaml:"kind"` + Metadata K8sMetadata `yaml:"metadata"` +} + +type K8sMetadata struct { + Name string `yaml:"name"` +} + +func GenerateResources(fullName string) *Resources { + return &Resources{ + Conf: Resource[map[string]any]{ + ID: "nats.conf", + }, + ConfigMap: Resource[corev1.ConfigMap]{ + ID: "ConfigMap/" + fullName + "-config", + }, + HeadlessService: Resource[corev1.Service]{ + ID: "Service/" + fullName + "-headless", + }, + Ingress: Resource[networkingv1.Ingress]{ + ID: "Ingress/" + fullName + "-ws", + }, + NatsBoxContentsSecret: Resource[corev1.Secret]{ + ID: "Secret/" + fullName + "-box-contents", + }, + NatsBoxContextsSecret: Resource[corev1.Secret]{ + ID: "Secret/" + fullName + "-box-contexts", + }, + NatsBoxDeployment: Resource[appsv1.Deployment]{ + ID: "Deployment/" + fullName + "-box", + }, + NatsBoxServiceAccount: Resource[corev1.ServiceAccount]{ + ID: "ServiceAccount/" + fullName + "-box", + }, + PodDisruptionBudget: Resource[policyv1.PodDisruptionBudget]{ + ID: "PodDisruptionBudget/" + fullName, + }, + PodMonitor: Resource[monitoringv1.PodMonitor]{ + ID: "PodMonitor/" + fullName, + }, + Service: Resource[corev1.Service]{ + ID: "Service/" + fullName, + }, + ServiceAccount: Resource[corev1.ServiceAccount]{ + ID: "ServiceAccount/" + fullName, + }, + StatefulSet: Resource[appsv1.StatefulSet]{ + ID: "StatefulSet/" + fullName, + }, + ExtraConfigMap: Resource[corev1.ConfigMap]{ + ID: "ConfigMap/" + fullName + "-extra", + }, + ExtraService: Resource[corev1.Service]{ + ID: "Service/" + fullName + "-extra", + }, + } +} + +type Test struct { + ChartName string + ReleaseName string + Namespace string + FullName string + Values string +} + +func DefaultTest() *Test { + return &Test{ + ChartName: "nats", + ReleaseName: "nats", + Namespace: "nats", + FullName: "nats", + Values: "{}", + } +} + +func HelmRender(t *testing.T, test *Test) *Resources { + t.Helper() + + helmChartPath, err := filepath.Abs("..") + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "values.*.yaml") + require.NoError(t, err) + defer os.Remove(tmpFile.Name()) + + if _, err := tmpFile.Write([]byte(test.Values)); err != nil { + tmpFile.Close() + require.NoError(t, err) + } + err = tmpFile.Close() + require.NoError(t, err) + + options := &helm.Options{ + ValuesFiles: []string{tmpFile.Name()}, + KubectlOptions: k8s.NewKubectlOptions("", "", test.Namespace), + } + output := helm.RenderTemplate(t, options, helmChartPath, test.ReleaseName, nil) + outputs := strings.Split(output, "---") + + resources := GenerateResources("nats") + for _, o := range outputs { + meta := K8sResource{} + err := yaml.Unmarshal([]byte(o), &meta) + require.NoError(t, err) + + id := meta.Kind + "/" + meta.Metadata.Name + for _, r := range resources.Iter() { + if id == r.ID { + helm.UnmarshalK8SYaml(t, o, r.ValueP) + *r.HasValueP = true + break + } + } + } + + require.True(t, resources.ConfigMap.HasValue) + _, ok := resources.ConfigMap.Value.Data["nats.conf"] + require.True(t, ok) + + confDir, err := os.MkdirTemp("", "") + require.NoError(t, err) + defer os.RemoveAll(confDir) + + for k, v := range resources.ConfigMap.Value.Data { + err := os.WriteFile(filepath.Join(confDir, k), []byte(v), 0o644) + require.NoError(t, err) + } + + _ = os.Setenv("POD_NAME", "nats-0") + _ = os.Setenv("SERVER_NAME", "nats-0") + resources.Conf.Value, err = conf.ParseFile(filepath.Join(confDir, "nats.conf")) + require.NoError(t, err) + resources.Conf.HasValue = true + + return resources +} + +func RenderAndCheck(t *testing.T, test *Test, expected *Resources) { + t.Helper() + actual := HelmRender(t, test) + a := assert.New(t) + + if actual.ConfigMap.Value.Data != nil { + natsConf, ok := actual.ConfigMap.Value.Data["nats.conf"] + if ok { + if expected.ConfigMap.Value.Data == nil { + expected.ConfigMap.Value.Data = map[string]string{} + } + expected.ConfigMap.Value.Data["nats.conf"] = natsConf + } + } + + if actual.StatefulSet.Value.Spec.Template.Annotations != nil { + configMapHash, ok := actual.StatefulSet.Value.Spec.Template.Annotations["checksum/config"] + if ok { + if expected.StatefulSet.Value.Spec.Template.Annotations == nil { + expected.StatefulSet.Value.Spec.Template.Annotations = map[string]string{} + } + expected.StatefulSet.Value.Spec.Template.Annotations["checksum/config"] = configMapHash + } + } + + expectedResources := expected.Iter() + actualResources := actual.Iter() + require.Len(t, actualResources, len(expectedResources)) + + for i := range expectedResources { + expectedResource := expectedResources[i] + actualResource := actualResources[i] + if a.Equal(expectedResource.HasValueP, actualResource.HasValueP) && *actualResource.HasValueP { + a.Equal(expectedResource.ValueP, actualResource.ValueP) + } + } +} diff --git a/helm/charts/nats/test/config_test.go b/helm/charts/nats/test/config_test.go new file mode 100644 index 00000000..ef0eba4b --- /dev/null +++ b/helm/charts/nats/test/config_test.go @@ -0,0 +1,676 @@ +package test + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestConfigDisable(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +config: + monitor: + enabled: false +` + expected := DefaultResources(t, test) + delete(expected.Conf.Value, "http_port") + + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].LivenessProbe = nil + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].ReadinessProbe = nil + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].StartupProbe = nil + + cp := expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].Ports + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{cp[0]} + + hsp := expected.HeadlessService.Value.Spec.Ports + expected.HeadlessService.Value.Spec.Ports = []corev1.ServicePort{hsp[0]} + + RenderAndCheck(t, test, expected) +} + +func TestConfigJetStreamCluster(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +config: + cluster: + enabled: true + jetstream: + enabled: true +` + expected := DefaultResources(t, test) + + expected.Conf.Value["cluster"] = map[string]any{ + "name": "nats", + "no_advertise": true, + "port": int64(6222), + "routes": []any{ + "nats://nats-0.nats-headless:6222", + "nats://nats-1.nats-headless:6222", + "nats://nats-2.nats-headless:6222", + }, + } + expected.Conf.Value["jetstream"] = map[string]any{ + "max_file_store": int64(10737418240), + "max_memory_store": int64(0), + "store_dir": "/data", + } + + replicas3 := int32(3) + expected.StatefulSet.Value.Spec.Replicas = &replicas3 + + vm := expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts = append(vm, corev1.VolumeMount{ + MountPath: "/data", + Name: test.FullName + "-js", + }) + + resource10Gi, _ := resource.ParseQuantity("10Gi") + expected.StatefulSet.Value.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{ + { + ObjectMeta: v1.ObjectMeta{ + Name: test.FullName + "-js", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + "ReadWriteOnce", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "storage": resource10Gi, + }, + }, + }, + }, + } + + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "nats", + ContainerPort: 4222, + }, + { + Name: "cluster", + ContainerPort: 6222, + }, + { + Name: "monitor", + ContainerPort: 8222, + }, + } + + expected.HeadlessService.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "cluster", + Port: 6222, + TargetPort: intstr.FromString("cluster"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "monitor", + Port: 8222, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTP, + }, + } + + RenderAndCheck(t, test, expected) +} + +func TestConfigOptions(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +config: + jetstream: + enabled: true + fileStore: + dir: /mnt + pvc: + size: 5Gi + storageClassName: gp3 + maxSize: 1Gi + memoryStore: + enabled: true + maxSize: 2Gi + cluster: + enabled: true + replicas: 2 + routeURLs: + user: foo + password: bar + useFQDN: true + k8sClusterDomain: foo.bar.local + resolver: + enabled: true + dir: /mnt/resolver + pvc: + size: 5Gi + storageClassName: gp3 + serverNamePrefix: test_ +` + expected := DefaultResources(t, test) + + expected.Conf.Value["cluster"] = map[string]any{ + "authorization": map[string]any{ + "user": "foo", + "password": "bar", + }, + "name": "nats", + "no_advertise": true, + "port": int64(6222), + "routes": []any{ + "nats://foo:bar@nats-0.nats-headless.nats.svc.foo.bar.local:6222", + "nats://foo:bar@nats-1.nats-headless.nats.svc.foo.bar.local:6222", + }, + } + expected.Conf.Value["jetstream"] = map[string]any{ + "max_file_store": int64(1073741824), + "max_memory_store": int64(2147483648), + "store_dir": "/mnt", + } + expected.Conf.Value["resolver"] = map[string]any{ + "dir": "/mnt/resolver", + } + + replicas2 := int32(2) + expected.StatefulSet.Value.Spec.Replicas = &replicas2 + + resource5Gi, _ := resource.ParseQuantity("5Gi") + storageClassGp3 := "gp3" + expected.StatefulSet.Value.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{ + { + ObjectMeta: v1.ObjectMeta{ + Name: test.FullName + "-js", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + "ReadWriteOnce", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "storage": resource5Gi, + }, + }, + StorageClassName: &storageClassGp3, + }, + }, + { + ObjectMeta: v1.ObjectMeta{ + Name: test.FullName + "-resolver", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + "ReadWriteOnce", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "storage": resource5Gi, + }, + }, + StorageClassName: &storageClassGp3, + }, + }, + } + + ctr := &expected.StatefulSet.Value.Spec.Template.Spec.Containers[0] + ctr.Env[1].Value = "test_$(POD_NAME)" + + ctr.VolumeMounts = append(ctr.VolumeMounts, corev1.VolumeMount{ + MountPath: "/mnt", + Name: test.FullName + "-js", + }, corev1.VolumeMount{ + MountPath: "/mnt/resolver", + Name: test.FullName + "-resolver", + }) + + ctr.Ports = []corev1.ContainerPort{ + { + Name: "nats", + ContainerPort: 4222, + }, + { + Name: "cluster", + ContainerPort: 6222, + }, + { + Name: "monitor", + ContainerPort: 8222, + }, + } + + expected.HeadlessService.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "cluster", + Port: 6222, + TargetPort: intstr.FromString("cluster"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "monitor", + Port: 8222, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTP, + }, + } + + RenderAndCheck(t, test, expected) +} + +func TestConfigMergePatch(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +config: + merge: + ping_interval: 5m + patch: [{op: add, path: /ping_max, value: 3}] + cluster: + enabled: true + merge: + no_advertise: false + patch: [{op: add, path: /advertise, value: "demo.nats.io:6222"}] + jetstream: + enabled: true + merge: + max_outstanding_catchup: "<< 64MB >>" + patch: [{op: add, path: /max_file_store, value: "<< 1GB >>"}] + fileStore: + pvc: + merge: + spec: + storageClassName: gp3 + patch: [{op: add, path: /spec/accessModes/-, value: ReadWriteMany}] + leafnodes: + enabled: true + merge: + no_advertise: false + patch: [{op: add, path: /advertise, value: "demo.nats.io:7422"}] + websocket: + enabled: true + merge: + compression: false + patch: [{op: add, path: /same_origin, value: true}] + mqtt: + enabled: true + merge: + ack_wait: 1m + patch: [{op: add, path: /max_ack_pending, value: 100}] + gateway: + enabled: true + merge: + gateways: + - name: nats + url: nats://demo.nats.io:7222 + patch: [{op: add, path: /advertise, value: "demo.nats.io:7222"}] + resolver: + enabled: true + merge: + type: full + patch: [{op: add, path: /allow_delete, value: true}] + pvc: + merge: + spec: + storageClassName: gp3 + patch: [{op: add, path: /spec/accessModes/-, value: ReadWriteMany}] +` + expected := DefaultResources(t, test) + expected.Conf.Value["ping_interval"] = "5m" + expected.Conf.Value["ping_max"] = int64(3) + expected.Conf.Value["cluster"] = map[string]any{ + "name": "nats", + "no_advertise": false, + "advertise": "demo.nats.io:6222", + "port": int64(6222), + "routes": []any{ + "nats://nats-0.nats-headless:6222", + "nats://nats-1.nats-headless:6222", + "nats://nats-2.nats-headless:6222", + }, + } + expected.Conf.Value["jetstream"] = map[string]any{ + "max_memory_store": int64(0), + "store_dir": "/data", + "max_file_store": int64(1073741824), + "max_outstanding_catchup": int64(67108864), + } + expected.Conf.Value["leafnodes"] = map[string]any{ + "port": int64(7422), + "no_advertise": false, + "advertise": "demo.nats.io:7422", + } + expected.Conf.Value["websocket"] = map[string]any{ + "port": int64(8080), + "compression": false, + "no_tls": true, + "same_origin": true, + } + expected.Conf.Value["mqtt"] = map[string]any{ + "port": int64(1883), + "ack_wait": "1m", + "max_ack_pending": int64(100), + } + expected.Conf.Value["gateway"] = map[string]any{ + "port": int64(7222), + "name": "nats", + "advertise": "demo.nats.io:7222", + "gateways": []any{ + map[string]any{ + "name": "nats", + "url": "nats://demo.nats.io:7222", + }, + }, + } + expected.Conf.Value["resolver"] = map[string]any{ + "dir": "/data/resolver", + "type": "full", + "allow_delete": true, + } + + replicas3 := int32(3) + expected.StatefulSet.Value.Spec.Replicas = &replicas3 + + vm := expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts = append(vm, corev1.VolumeMount{ + MountPath: "/data", + Name: test.FullName + "-js", + }, corev1.VolumeMount{ + MountPath: "/data/resolver", + Name: test.FullName + "-resolver", + }) + + resource1Gi, _ := resource.ParseQuantity("1Gi") + resource10Gi, _ := resource.ParseQuantity("10Gi") + storageClassGp3 := "gp3" + expected.StatefulSet.Value.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{ + { + ObjectMeta: v1.ObjectMeta{ + Name: test.FullName + "-js", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + "ReadWriteOnce", + "ReadWriteMany", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "storage": resource10Gi, + }, + }, + StorageClassName: &storageClassGp3, + }, + }, + { + ObjectMeta: v1.ObjectMeta{ + Name: test.FullName + "-resolver", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + "ReadWriteOnce", + "ReadWriteMany", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "storage": resource1Gi, + }, + }, + StorageClassName: &storageClassGp3, + }, + }, + } + + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "nats", + ContainerPort: 4222, + }, + { + Name: "leafnodes", + ContainerPort: 7422, + }, + { + Name: "websocket", + ContainerPort: 8080, + }, + { + Name: "mqtt", + ContainerPort: 1883, + }, + { + Name: "cluster", + ContainerPort: 6222, + }, + { + Name: "gateway", + ContainerPort: 7222, + }, + { + Name: "monitor", + ContainerPort: 8222, + }, + } + + expected.HeadlessService.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "leafnodes", + Port: 7422, + TargetPort: intstr.FromString("leafnodes"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "websocket", + Port: 8080, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTP, + }, + { + Name: "mqtt", + Port: 1883, + TargetPort: intstr.FromString("mqtt"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "cluster", + Port: 6222, + TargetPort: intstr.FromString("cluster"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "gateway", + Port: 7222, + TargetPort: intstr.FromString("gateway"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "monitor", + Port: 8222, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTP, + }, + } + + expected.Service.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "leafnodes", + Port: 7422, + TargetPort: intstr.FromString("leafnodes"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "websocket", + Port: 8080, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTP, + }, + { + Name: "mqtt", + Port: 1883, + TargetPort: intstr.FromString("mqtt"), + AppProtocol: &appProtocolTCP, + }, + } + + RenderAndCheck(t, test, expected) +} + +func TestConfigInclude(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +config: + jetstream: + enabled: true + merge: + zzz$include: "js.conf" + merge: + $include: "my-config.conf" + zzz$include: "my-config-last.conf" +configMap: + merge: + data: + js.conf: | + max_file_store: 1GB + max_outstanding_catchup: 64MB + my-config.conf: | + ping_interval: "5m" + my-config-last.conf: | + ping_max: 3 +` + expected := DefaultResources(t, test) + expected.Conf.Value["ping_interval"] = "5m" + expected.Conf.Value["ping_max"] = int64(3) + expected.Conf.Value["jetstream"] = map[string]any{ + "max_file_store": int64(1073741824), + "max_memory_store": int64(0), + "max_outstanding_catchup": int64(67108864), + "store_dir": "/data", + } + + expected.ConfigMap.Value.Data = map[string]string{ + "js.conf": `max_file_store: 1GB +max_outstanding_catchup: 64MB +`, + "my-config.conf": `ping_interval: "5m" +`, + "my-config-last.conf": `ping_max: 3 +`, + } + + reloaderArgs := expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].Args + reloaderArgs = append(reloaderArgs, "-config", "/etc/nats-config/my-config.conf") + reloaderArgs = append(reloaderArgs, "-config", "/etc/nats-config/js.conf") + reloaderArgs = append(reloaderArgs, "-config", "/etc/nats-config/my-config-last.conf") + expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].Args = reloaderArgs + + vm := expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts = append(vm, corev1.VolumeMount{ + MountPath: "/data", + Name: test.FullName + "-js", + }) + + resource10Gi, _ := resource.ParseQuantity("10Gi") + expected.StatefulSet.Value.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{ + { + ObjectMeta: v1.ObjectMeta{ + Name: test.FullName + "-js", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + "ReadWriteOnce", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "storage": resource10Gi, + }, + }, + }, + }, + } + + RenderAndCheck(t, test, expected) +} + +func TestExtraResources(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +extraResources: +- apiVersion: v1 + kind: Service + metadata: + name: + $tplYaml: > + {{ include "nats.fullname" $ }}-extra + labels: + $tplYaml: | + {{ include "nats.labels" $ }} + spec: + selector: + labels: + $tplYamlSpread: | + {{ include "nats.selectorLabels" $ | nindent 4 }} + ports: + - $tplYamlSpread: | + - name: gateway + port: 7222 + targetPort: gateway + appProtocol: tcp +- $tplYaml: | + apiVersion: v1 + kind: ConfigMap + metadata: + name: {{ include "nats.fullname" $ }}-extra + labels: + {{- include "nats.labels" $ | nindent 4 }} + data: + foo: bar +` + + expected := DefaultResources(t, test) + + expected.ExtraConfigMap.HasValue = true + expected.ExtraConfigMap.Value.Data = map[string]string{ + "foo": "bar", + } + + expected.ExtraService.HasValue = true + expected.ExtraService.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "gateway", + Port: 7222, + TargetPort: intstr.FromString("gateway"), + AppProtocol: &appProtocolTCP, + }, + } + + RenderAndCheck(t, test, expected) +} diff --git a/helm/charts/nats/test/defaults_test.go b/helm/charts/nats/test/defaults_test.go new file mode 100644 index 00000000..d946ee7f --- /dev/null +++ b/helm/charts/nats/test/defaults_test.go @@ -0,0 +1,628 @@ +package test + +import ( + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + policyv1 "k8s.io/api/policy/v1" + "sync" + "testing" + + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +type DynamicDefaults struct { + VersionLabel string + HelmChartLabel string + NatsImage string + PromExporterImage string + ReloaderImage string + NatsBoxImage string +} + +type DynamicDefaultsGetter struct { + mu sync.Mutex + set bool + dd DynamicDefaults +} + +var ( + ddg DynamicDefaultsGetter + appProtocolTCP = "tcp" + appProtocolTLS = "tls" + appProtocolHTTP = "http" + appProtocolHTTPS = "https" +) + +func (d *DynamicDefaultsGetter) Get(t *testing.T) DynamicDefaults { + t.Helper() + + d.mu.Lock() + defer d.mu.Unlock() + if d.set { + return d.dd + } + + test := DefaultTest() + test.Values = ` +promExporter: + enabled: true +` + r := HelmRender(t, test) + + require.True(t, r.StatefulSet.HasValue) + + var ok bool + d.dd.VersionLabel, ok = r.StatefulSet.Value.Labels["app.kubernetes.io/version"] + require.True(t, ok) + d.dd.HelmChartLabel, ok = r.StatefulSet.Value.Labels["helm.sh/chart"] + require.True(t, ok) + + containers := r.StatefulSet.Value.Spec.Template.Spec.Containers + require.Len(t, containers, 3) + d.dd.NatsImage = containers[0].Image + d.dd.ReloaderImage = containers[1].Image + d.dd.PromExporterImage = containers[2].Image + + require.True(t, r.NatsBoxDeployment.HasValue) + containers = r.NatsBoxDeployment.Value.Spec.Template.Spec.Containers + require.Len(t, containers, 1) + d.dd.NatsBoxImage = containers[0].Image + + return d.dd +} + +func DefaultResources(t *testing.T, test *Test) *Resources { + fullName := test.FullName + chartName := test.ChartName + releaseName := test.ReleaseName + + dd := ddg.Get(t) + dr := GenerateResources(fullName) + + natsLabels := func() map[string]string { + return map[string]string{ + "app.kubernetes.io/component": "nats", + "app.kubernetes.io/instance": releaseName, + "app.kubernetes.io/managed-by": "Helm", + "app.kubernetes.io/name": chartName, + "app.kubernetes.io/version": dd.VersionLabel, + "helm.sh/chart": dd.HelmChartLabel, + } + } + natsSelectorLabels := func() map[string]string { + return map[string]string{ + "app.kubernetes.io/component": "nats", + "app.kubernetes.io/instance": releaseName, + "app.kubernetes.io/name": chartName, + } + } + natsBoxLabels := func() map[string]string { + return map[string]string{ + "app.kubernetes.io/component": "nats-box", + "app.kubernetes.io/instance": releaseName, + "app.kubernetes.io/managed-by": "Helm", + "app.kubernetes.io/name": chartName, + "app.kubernetes.io/version": dd.VersionLabel, + "helm.sh/chart": dd.HelmChartLabel, + } + } + natsBoxSelectorLabels := func() map[string]string { + return map[string]string{ + "app.kubernetes.io/component": "nats-box", + "app.kubernetes.io/instance": releaseName, + "app.kubernetes.io/name": chartName, + } + } + + replicas1 := int32(1) + trueBool := true + falseBool := false + exactPath := networkingv1.PathTypeExact + + return &Resources{ + Conf: Resource[map[string]any]{ + ID: dr.Conf.ID, + HasValue: true, + Value: map[string]any{ + "http_port": int64(8222), + "lame_duck_duration": "30s", + "lame_duck_grace_period": "10s", + "pid_file": "/var/run/nats/nats.pid", + "port": int64(4222), + "server_name": "nats-0", + }, + }, + ConfigMap: Resource[corev1.ConfigMap]{ + ID: dr.ConfigMap.ID, + HasValue: true, + Value: corev1.ConfigMap{ + TypeMeta: v1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-config", + Labels: natsLabels(), + }, + }, + }, + HeadlessService: Resource[corev1.Service]{ + ID: dr.HeadlessService.ID, + HasValue: true, + Value: corev1.Service{ + TypeMeta: v1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-headless", + Labels: natsLabels(), + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "monitor", + Port: 8222, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTP, + }, + }, + Selector: natsSelectorLabels(), + ClusterIP: "None", + PublishNotReadyAddresses: true, + }, + }, + }, + Ingress: Resource[networkingv1.Ingress]{ + ID: dr.Ingress.ID, + HasValue: false, + Value: networkingv1.Ingress{ + TypeMeta: v1.TypeMeta{ + Kind: "Ingress", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-ws", + Labels: natsLabels(), + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "demo.nats.io", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &exactPath, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: fullName, + Port: networkingv1.ServiceBackendPort{ + Name: "websocket", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + NatsBoxContentsSecret: Resource[corev1.Secret]{ + ID: dr.NatsBoxContentsSecret.ID, + HasValue: false, + Value: corev1.Secret{ + TypeMeta: v1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-box-contents", + Labels: natsBoxLabels(), + }, + Type: "Opaque", + }, + }, + NatsBoxContextsSecret: Resource[corev1.Secret]{ + ID: dr.NatsBoxContextsSecret.ID, + HasValue: true, + Value: corev1.Secret{ + TypeMeta: v1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-box-contexts", + Labels: natsBoxLabels(), + }, + Type: "Opaque", + StringData: map[string]string{ + "default.json": `{ + "url": "nats://` + fullName + `" +} +`, + }, + }, + }, + NatsBoxDeployment: Resource[appsv1.Deployment]{ + ID: dr.NatsBoxDeployment.ID, + HasValue: true, + Value: appsv1.Deployment{ + TypeMeta: v1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-box", + Labels: natsBoxLabels(), + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas1, + Selector: &v1.LabelSelector{ + MatchLabels: natsBoxSelectorLabels(), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: natsBoxLabels(), + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Args: []string{ + "trap true INT TERM; sleep infinity & wait", + }, + Command: []string{ + "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 ! [ -f context.txt ]; then + echo -n "default" > context.txt +fi +cd "$work_dir" +exec sh -ec "$0" +`, + }, + Image: dd.NatsBoxImage, + Name: "nats-box", + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/etc/nats-contexts", + Name: "contexts", + }, + }, + }, + }, + EnableServiceLinks: &falseBool, + Volumes: []corev1.Volume{ + { + Name: "contexts", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "nats-box-contexts", + }, + }, + }, + }, + }, + }, + }, + }, + }, + NatsBoxServiceAccount: Resource[corev1.ServiceAccount]{ + ID: dr.NatsBoxServiceAccount.ID, + HasValue: false, + Value: corev1.ServiceAccount{ + TypeMeta: v1.TypeMeta{ + Kind: "ServiceAccount", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-box", + Labels: natsBoxLabels(), + }, + }, + }, + PodDisruptionBudget: Resource[policyv1.PodDisruptionBudget]{ + ID: dr.PodDisruptionBudget.ID, + HasValue: true, + Value: policyv1.PodDisruptionBudget{ + TypeMeta: v1.TypeMeta{ + Kind: "PodDisruptionBudget", + APIVersion: "policy/v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName, + Labels: natsLabels(), + }, + Spec: policyv1.PodDisruptionBudgetSpec{ + MaxUnavailable: &intstr.IntOrString{IntVal: 1}, + Selector: &v1.LabelSelector{ + MatchLabels: natsSelectorLabels(), + }, + }, + }, + }, + PodMonitor: Resource[monitoringv1.PodMonitor]{ + ID: dr.PodMonitor.ID, + HasValue: false, + Value: monitoringv1.PodMonitor{ + TypeMeta: v1.TypeMeta{ + Kind: "PodMonitor", + APIVersion: "monitoring.coreos.com/v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName, + Labels: natsLabels(), + }, + Spec: monitoringv1.PodMonitorSpec{ + PodMetricsEndpoints: []monitoringv1.PodMetricsEndpoint{ + { + Port: "prom-metrics", + }, + }, + Selector: v1.LabelSelector{ + MatchLabels: natsSelectorLabels(), + }, + }, + }, + }, + Service: Resource[corev1.Service]{ + ID: dr.Service.ID, + HasValue: true, + Value: corev1.Service{ + TypeMeta: v1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName, + Labels: natsLabels(), + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + }, + Selector: natsSelectorLabels(), + }, + }, + }, + ServiceAccount: Resource[corev1.ServiceAccount]{ + ID: dr.ServiceAccount.ID, + HasValue: false, + Value: corev1.ServiceAccount{ + TypeMeta: v1.TypeMeta{ + Kind: "ServiceAccount", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName, + Labels: natsLabels(), + }, + }, + }, + StatefulSet: Resource[appsv1.StatefulSet]{ + ID: dr.StatefulSet.ID, + HasValue: true, + Value: appsv1.StatefulSet{ + TypeMeta: v1.TypeMeta{ + Kind: "StatefulSet", + APIVersion: "apps/v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName, + Labels: natsLabels(), + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas1, + Selector: &v1.LabelSelector{ + MatchLabels: natsSelectorLabels(), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: natsLabels(), + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Args: []string{ + "--config", + "/etc/nats-config/nats.conf", + }, + Env: []corev1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "SERVER_NAME", + Value: "$(POD_NAME)", + }, + }, + Image: dd.NatsImage, + Lifecycle: &corev1.Lifecycle{ + PreStop: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "nats-server", + "-sl=ldm=/var/run/nats/nats.pid", + }, + }, + }, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz?js-enabled-only=true", + Port: intstr.FromString("monitor"), + }, + }, + InitialDelaySeconds: 10, + TimeoutSeconds: 5, + PeriodSeconds: 30, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + Name: "nats", + Ports: []corev1.ContainerPort{ + { + Name: "nats", + ContainerPort: 4222, + }, + { + Name: "monitor", + ContainerPort: 8222, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz?js-server-only=true", + Port: intstr.FromString("monitor"), + }, + }, + InitialDelaySeconds: 10, + TimeoutSeconds: 5, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + StartupProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromString("monitor"), + }, + }, + InitialDelaySeconds: 10, + TimeoutSeconds: 5, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 90, + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/etc/nats-config", + Name: "config", + }, + { + MountPath: "/var/run/nats", + Name: "pid", + }, + }, + }, + { + Args: []string{ + "-pid", + "/var/run/nats/nats.pid", + "-config", + "/etc/nats-config/nats.conf", + }, + Image: dd.ReloaderImage, + Name: "reloader", + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/var/run/nats", + Name: "pid", + }, + { + MountPath: "/etc/nats-config", + Name: "config", + }, + }, + }, + }, + EnableServiceLinks: &falseBool, + ShareProcessNamespace: &trueBool, + Volumes: []corev1.Volume{ + { + Name: "config", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "nats-config", + }, + }, + }, + }, + { + Name: "pid", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + }, + ServiceName: fullName + "-headless", + PodManagementPolicy: "Parallel", + }, + }, + }, + ExtraConfigMap: Resource[corev1.ConfigMap]{ + ID: dr.ExtraConfigMap.ID, + HasValue: false, + Value: corev1.ConfigMap{ + TypeMeta: v1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-extra", + Labels: natsLabels(), + }, + }, + }, + ExtraService: Resource[corev1.Service]{ + ID: dr.ExtraService.ID, + HasValue: false, + Value: corev1.Service{ + TypeMeta: v1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: fullName + "-extra", + Labels: natsLabels(), + }, + Spec: corev1.ServiceSpec{ + Selector: natsSelectorLabels(), + }, + }, + }, + } +} + +func TestDefaultValues(t *testing.T) { + t.Parallel() + test := DefaultTest() + expected := DefaultResources(t, test) + RenderAndCheck(t, test, expected) +} diff --git a/helm/charts/nats/test/go.mod b/helm/charts/nats/test/go.mod new file mode 100644 index 00000000..9a9709a4 --- /dev/null +++ b/helm/charts/nats/test/go.mod @@ -0,0 +1,71 @@ +module github.com/nats-io/k8s/helm/charts/nats-next + +go 1.20 + +require ( + github.com/ghodss/yaml v1.0.0 + github.com/gruntwork-io/terratest v0.41.18 + github.com/nats-io/nats-server/v2 v2.9.15 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.64.0 + github.com/stretchr/testify v1.8.2 + k8s.io/api v0.26.3 + k8s.io/apimachinery v0.26.3 +) + +require ( + github.com/aws/aws-sdk-go v1.44.217 // indirect + github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.10.1 // indirect + github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-sql-driver/mysql v1.4.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gruntwork-io/go-commons v0.8.0 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pquerna/otp v1.2.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/urfave/cli v1.22.2 // indirect + golang.org/x/crypto v0.6.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/oauth2 v0.6.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/client-go v0.26.3 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d // indirect + k8s.io/utils v0.0.0-20230308161112-d77c459e9343 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/helm/charts/nats/test/go.sum b/helm/charts/nats/test/go.sum new file mode 100644 index 00000000..a93d0868 --- /dev/null +++ b/helm/charts/nats/test/go.sum @@ -0,0 +1,338 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/aws/aws-sdk-go v1.44.217 h1:FcWC56MRl+k756aH3qeMQTylSdeJ58WN0iFz3fkyRz0= +github.com/aws/aws-sdk-go v1.44.217/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 h1:yY9rWGoXv1U5pl4gxqlULARMQD7x0QG85lqEXTWysik= +github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= +github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 h1:skJKxRtNmevLqnayafdLe2AsenqRupVmzZSqrvb5caU= +github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/gruntwork-io/go-commons v0.8.0 h1:k/yypwrPqSeYHevLlEDmvmgQzcyTwrlZGRaxEM6G0ro= +github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78= +github.com/gruntwork-io/terratest v0.41.18 h1:xFLF9c6bQ/dpcRera3Bbn1D5RJE+NyRXRndtV/Mjiuc= +github.com/gruntwork-io/terratest v0.41.18/go.mod h1:O6gajNBjO1wvc7Wl9WtbO+ORcdnhAV2GQiBE71ycwIk= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= +github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg= +github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nats-io/nats-server/v2 v2.9.15 h1:MuwEJheIwpvFgqvbs20W8Ish2azcygjf4Z0liVu2I4c= +github.com/nats-io/nats-server/v2 v2.9.15/go.mod h1:QlCTy115fqpx4KSOPFIxSV7DdI6OxtZsGOL1JLdeRlE= +github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= +github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.64.0 h1:bqFOzWYCuSZEcuFx/ez8DFW+fqGiUEATrgezynCjpP4= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.64.0/go.mod h1:cfNgxpCPGyIydmt3HcwDqKDt0nYdlGRhzftl+DZH7WA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= +k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= +k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= +k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= +k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d h1:VcFq5n7wCJB2FQMCIHfC+f+jNcGgNMar1uKd6rVlifU= +k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= +k8s.io/utils v0.0.0-20230308161112-d77c459e9343 h1:m7tbIjXGcGIAtpmQr7/NAi7RsWoW3E7Zcm4jI1HicTc= +k8s.io/utils v0.0.0-20230308161112-d77c459e9343/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/helm/charts/nats/test/ports_test.go b/helm/charts/nats/test/ports_test.go new file mode 100644 index 00000000..6e7753fd --- /dev/null +++ b/helm/charts/nats/test/ports_test.go @@ -0,0 +1,284 @@ +package test + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestPorts(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +config: + cluster: + enabled: true + port: 1005 + nats: + port: 1001 + leafnodes: + enabled: true + port: 1002 + websocket: + enabled: true + port: 1003 + mqtt: + enabled: true + port: 1004 + gateway: + enabled: true + port: 1006 + monitor: + port: 1007 + profiling: + enabled: true + port: 1008 + +container: + ports: + nats: + hostPort: 2001 + leafnodes: + hostPort: 2002 + websocket: + hostPort: 2003 + mqtt: + hostPort: 2004 + cluster: + hostPort: 2005 + gateway: + hostPort: 2006 + monitor: + hostPort: 2007 + profiling: + hostPort: 2008 + +service: + merge: + spec: + type: NodePort + ports: + nats: + enabled: true + port: 3001 + nodePort: 4001 + leafnodes: + enabled: true + port: 3002 + nodePort: 4002 + websocket: + enabled: true + port: 3003 + nodePort: 4003 + mqtt: + enabled: true + port: 3004 + nodePort: 4004 + cluster: + enabled: true + port: 3005 + nodePort: 4005 + gateway: + enabled: true + port: 3006 + nodePort: 4006 + monitor: + enabled: true + port: 3007 + nodePort: 4007 + profiling: + enabled: true + port: 3008 + nodePort: 4008 +` + expected := DefaultResources(t, test) + expected.Conf.Value["port"] = int64(1001) + expected.Conf.Value["leafnodes"] = map[string]any{ + "port": int64(1002), + "no_advertise": true, + } + expected.Conf.Value["websocket"] = map[string]any{ + "port": int64(1003), + "compression": true, + "no_tls": true, + } + expected.Conf.Value["mqtt"] = map[string]any{ + "port": int64(1004), + } + expected.Conf.Value["cluster"] = map[string]any{ + "name": "nats", + "no_advertise": true, + "port": int64(1005), + "routes": []any{ + "nats://nats-0.nats-headless:1005", + "nats://nats-1.nats-headless:1005", + "nats://nats-2.nats-headless:1005", + }, + } + expected.Conf.Value["gateway"] = map[string]any{ + "port": int64(1006), + "name": "nats", + } + expected.Conf.Value["http_port"] = int64(1007) + expected.Conf.Value["prof_port"] = int64(1008) + + replicas3 := int32(3) + expected.StatefulSet.Value.Spec.Replicas = &replicas3 + + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "nats", + ContainerPort: 1001, + HostPort: 2001, + }, + { + Name: "leafnodes", + ContainerPort: 1002, + HostPort: 2002, + }, + { + Name: "websocket", + ContainerPort: 1003, + HostPort: 2003, + }, + { + Name: "mqtt", + ContainerPort: 1004, + HostPort: 2004, + }, + { + Name: "cluster", + ContainerPort: 1005, + HostPort: 2005, + }, + { + Name: "gateway", + ContainerPort: 1006, + HostPort: 2006, + }, + { + Name: "monitor", + ContainerPort: 1007, + HostPort: 2007, + }, + { + Name: "profiling", + ContainerPort: 1008, + HostPort: 2008, + }, + } + + expected.HeadlessService.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 1001, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "leafnodes", + Port: 1002, + TargetPort: intstr.FromString("leafnodes"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "websocket", + Port: 1003, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTP, + }, + { + Name: "mqtt", + Port: 1004, + TargetPort: intstr.FromString("mqtt"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "cluster", + Port: 1005, + TargetPort: intstr.FromString("cluster"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "gateway", + Port: 1006, + TargetPort: intstr.FromString("gateway"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "monitor", + Port: 1007, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTP, + }, + { + Name: "profiling", + Port: 1008, + TargetPort: intstr.FromString("profiling"), + AppProtocol: &appProtocolTCP, + }, + } + + expected.Service.Value.Spec.Type = "NodePort" + expected.Service.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 3001, + NodePort: 4001, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "leafnodes", + Port: 3002, + NodePort: 4002, + TargetPort: intstr.FromString("leafnodes"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "websocket", + Port: 3003, + NodePort: 4003, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTP, + }, + { + Name: "mqtt", + Port: 3004, + NodePort: 4004, + TargetPort: intstr.FromString("mqtt"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "cluster", + Port: 3005, + NodePort: 4005, + TargetPort: intstr.FromString("cluster"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "gateway", + Port: 3006, + NodePort: 4006, + TargetPort: intstr.FromString("gateway"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "monitor", + Port: 3007, + NodePort: 4007, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTP, + }, + { + Name: "profiling", + Port: 3008, + NodePort: 4008, + TargetPort: intstr.FromString("profiling"), + AppProtocol: &appProtocolTCP, + }, + } + + RenderAndCheck(t, test, expected) +} diff --git a/helm/charts/nats/test/resources_test.go b/helm/charts/nats/test/resources_test.go new file mode 100644 index 00000000..5ae20392 --- /dev/null +++ b/helm/charts/nats/test/resources_test.go @@ -0,0 +1,653 @@ +package test + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/resource" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestResourceOptions(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +global: + image: + pullPolicy: Always + registry: docker.io + labels: + global: global +namespaceOverride: foo +config: + jetstream: + enabled: true + websocket: + enabled: true + ingress: + enabled: true + hosts: + - demo.nats.io + tlsSecretName: ws-tls +container: + image: + pullPolicy: IfNotPresent + registry: gcr.io + env: + GOMEMLIMIT: 1GiB + TOKEN: + valueFrom: + secretKeyRef: + name: token + key: token +reloader: + env: + GOMEMLIMIT: 1GiB + TOKEN: + valueFrom: + secretKeyRef: + name: token + key: token + natsVolumeMountPrefixes: + - /etc/ + - /data +promExporter: + enabled: true + port: 7778 + env: + GOMEMLIMIT: 1GiB + TOKEN: + valueFrom: + secretKeyRef: + name: token + key: token +podTemplate: + configChecksumAnnotation: false + topologySpreadConstraints: + kubernetes.io/hostname: + maxSkew: 1 +natsBox: + contexts: + loadedSecret: + creds: + secretName: loaded-creds + key: nats.creds + nkey: + secretName: loaded-nkey + key: nats.nk + tls: + secretName: loaded-tls + merge: + ca: /etc/my-ca/ca.crt + loadedContents: + creds: + contents: aabbcc + nkey: + contents: ddeeff + token: + merge: + token: foo + container: + env: + GOMEMLIMIT: 1GiB + TOKEN: + valueFrom: + secretKeyRef: + name: token + key: token +` + expected := DefaultResources(t, test) + + expected.Conf.Value["jetstream"] = map[string]any{ + "max_file_store": int64(10737418240), + "max_memory_store": int64(0), + "store_dir": "/data", + } + + expected.Conf.Value["websocket"] = map[string]any{ + "port": int64(8080), + "no_tls": true, + "compression": true, + } + + env := []corev1.EnvVar{ + { + Name: "GOMEMLIMIT", + Value: "1GiB", + }, + { + Name: "TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "token", + }, + Key: "token", + }, + }, + }, + } + + expected.StatefulSet.Value.ObjectMeta.Labels["global"] = "global" + expected.StatefulSet.Value.ObjectMeta.Namespace = "foo" + expected.StatefulSet.Value.Spec.Template.ObjectMeta.Labels["global"] = "global" + + dd := ddg.Get(t) + ctr := expected.StatefulSet.Value.Spec.Template.Spec.Containers + + // nats + ctr[0].Env = append(ctr[0].Env, env...) + ctr[0].Image = "gcr.io/" + ctr[0].Image + ctr[0].ImagePullPolicy = "IfNotPresent" + ctr[0].VolumeMounts = append(ctr[0].VolumeMounts, corev1.VolumeMount{ + Name: test.FullName + "-js", + MountPath: "/data", + }) + + // reloader + ctr[1].Env = env + ctr[1].Image = "docker.io/" + ctr[1].Image + ctr[1].ImagePullPolicy = "Always" + ctr[1].VolumeMounts = append(ctr[1].VolumeMounts, corev1.VolumeMount{ + Name: test.FullName + "-js", + MountPath: "/data", + }) + + // promExporter + ctr = append(ctr, corev1.Container{ + Args: []string{ + "-port=7778", + "-connz", + "-routez", + "-subz", + "-varz", + "-prefix=nats", + "-use_internal_server_id", + "-jsz=all", + "http://localhost:8222/", + }, + Env: env, + Image: "docker.io/" + dd.PromExporterImage, + ImagePullPolicy: "Always", + Name: "prom-exporter", + Ports: []corev1.ContainerPort{ + { + Name: "prom-metrics", + ContainerPort: 7778, + }, + }, + }) + + expected.StatefulSet.Value.Spec.Template.Spec.Containers = ctr + expected.StatefulSet.Value.Spec.Template.Spec.TopologySpreadConstraints = []corev1.TopologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "kubernetes.io/hostname", + LabelSelector: expected.StatefulSet.Value.Spec.Selector, + }, + } + + expected.NatsBoxDeployment.Value.ObjectMeta.Labels["global"] = "global" + expected.NatsBoxDeployment.Value.ObjectMeta.Namespace = "foo" + expected.NatsBoxDeployment.Value.Spec.Template.ObjectMeta.Labels["global"] = "global" + nbCtr := expected.NatsBoxDeployment.Value.Spec.Template.Spec.Containers[0] + // nats-box + nbCtr.Env = env + nbCtr.Image = "docker.io/" + nbCtr.Image + nbCtr.ImagePullPolicy = "Always" + nbCtr.VolumeMounts = append(nbCtr.VolumeMounts, + corev1.VolumeMount{ + MountPath: "/etc/nats-contents", + Name: "contents", + }, + corev1.VolumeMount{ + Name: "ctx-loadedSecret-creds", + MountPath: "/etc/nats-creds/loadedSecret", + }, + corev1.VolumeMount{ + Name: "ctx-loadedSecret-nkey", + MountPath: "/etc/nats-nkeys/loadedSecret", + }, + corev1.VolumeMount{ + Name: "ctx-loadedSecret-tls", + MountPath: "/etc/nats-certs/loadedSecret", + }, + ) + expected.NatsBoxDeployment.Value.Spec.Template.Spec.Containers[0] = nbCtr + + nbVol := expected.NatsBoxDeployment.Value.Spec.Template.Spec.Volumes + nbVol = append(nbVol, + corev1.Volume{ + Name: "contents", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "nats-box-contents", + }, + }, + }, + corev1.Volume{ + Name: "ctx-loadedSecret-creds", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "loaded-creds", + }, + }, + }, + corev1.Volume{ + Name: "ctx-loadedSecret-nkey", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "loaded-nkey", + }, + }, + }, + corev1.Volume{ + Name: "ctx-loadedSecret-tls", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "loaded-tls", + }, + }, + }, + ) + expected.NatsBoxDeployment.Value.Spec.Template.Spec.Volumes = nbVol + + expected.NatsBoxContextsSecret.Value.ObjectMeta.Labels["global"] = "global" + expected.NatsBoxContextsSecret.Value.ObjectMeta.Namespace = "foo" + expected.NatsBoxContextsSecret.Value.StringData["loadedSecret.json"] = `{ + "ca": "/etc/my-ca/ca.crt", + "cert": "/etc/nats-certs/loadedSecret/tls.crt", + "creds": "/etc/nats-creds/loadedSecret/nats.creds", + "key": "/etc/nats-certs/loadedSecret/tls.key", + "nkey": "/etc/nats-nkeys/loadedSecret/nats.nk", + "url": "nats://` + test.FullName + `" +} +` + expected.NatsBoxContextsSecret.Value.StringData["loadedContents.json"] = `{ + "creds": "/etc/nats-contents/loadedContents.creds", + "nkey": "/etc/nats-contents/loadedContents.nk", + "url": "nats://` + test.FullName + `" +} +` + expected.NatsBoxContextsSecret.Value.StringData["token.json"] = `{ + "token": "foo", + "url": "nats://` + test.FullName + `" +} +` + + expected.NatsBoxContentsSecret.HasValue = true + expected.NatsBoxContentsSecret.Value.ObjectMeta.Labels["global"] = "global" + expected.NatsBoxContentsSecret.Value.ObjectMeta.Namespace = "foo" + expected.NatsBoxContentsSecret.Value.StringData = map[string]string{ + "loadedContents.creds": "aabbcc", + "loadedContents.nk": "ddeeff", + } + + expected.Ingress.HasValue = true + expected.Ingress.Value.ObjectMeta.Labels["global"] = "global" + expected.Ingress.Value.ObjectMeta.Namespace = "foo" + expected.Ingress.Value.Spec.TLS = []networkingv1.IngressTLS{ + { + Hosts: []string{"demo.nats.io"}, + SecretName: "ws-tls", + }, + } + + resource10Gi, _ := resource.ParseQuantity("10Gi") + expected.StatefulSet.Value.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{ + { + ObjectMeta: v1.ObjectMeta{ + Name: test.FullName + "-js", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + "ReadWriteOnce", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "storage": resource10Gi, + }, + }, + }, + }, + } + + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "nats", + ContainerPort: 4222, + }, + { + Name: "websocket", + ContainerPort: 8080, + }, + { + Name: "monitor", + ContainerPort: 8222, + }, + } + + expected.HeadlessService.Value.ObjectMeta.Labels["global"] = "global" + expected.HeadlessService.Value.ObjectMeta.Namespace = "foo" + expected.HeadlessService.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "websocket", + Port: 8080, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTP, + }, + { + Name: "monitor", + Port: 8222, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTP, + }, + } + + expected.Service.Value.ObjectMeta.Labels["global"] = "global" + expected.Service.Value.ObjectMeta.Namespace = "foo" + expected.Service.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "websocket", + Port: 8080, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTP, + }, + } + + expected.ConfigMap.Value.ObjectMeta.Labels["global"] = "global" + expected.ConfigMap.Value.ObjectMeta.Namespace = "foo" + + RenderAndCheck(t, test, expected) +} + +func TestResourcesMergePatch(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +config: + websocket: + enabled: true + ingress: + enabled: true + hosts: + - demo.nats.io + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +container: + merge: + stdin: true + patch: [{op: add, path: /tty, value: true}] +reloader: + merge: + stdin: true + patch: [{op: add, path: /tty, value: true}] +promExporter: + enabled: true + merge: + stdin: true + patch: [{op: add, path: /tty, value: true}] + podMonitor: + enabled: true + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +service: + enabled: true + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +statefulSet: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +podTemplate: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +headlessService: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +configMap: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +podDisruptionBudget: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +serviceAccount: + enabled: true + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +natsBox: + contexts: + default: + merge: + user: foo + patch: [{op: add, path: /password, value: "bar"}] + container: + merge: + stdin: true + patch: [{op: add, path: /tty, value: true}] + podTemplate: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] + deployment: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] + contextsSecret: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] + contentsSecret: + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] + serviceAccount: + enabled: true + merge: + metadata: + annotations: + test: test + patch: [{op: add, path: /metadata/labels/test, value: "test"}] +` + expected := DefaultResources(t, test) + + expected.Conf.Value["websocket"] = map[string]any{ + "port": int64(8080), + "no_tls": true, + "compression": true, + } + + annotations := func() map[string]string { + return map[string]string{ + "test": "test", + } + } + + dd := ddg.Get(t) + ctr := expected.StatefulSet.Value.Spec.Template.Spec.Containers + ctr[0].Stdin = true + ctr[0].TTY = true + ctr[1].Stdin = true + ctr[1].TTY = true + ctr = append(ctr, corev1.Container{ + Args: []string{ + "-port=7777", + "-connz", + "-routez", + "-subz", + "-varz", + "-prefix=nats", + "-use_internal_server_id", + "http://localhost:8222/", + }, + Image: dd.PromExporterImage, + Name: "prom-exporter", + Ports: []corev1.ContainerPort{ + { + Name: "prom-metrics", + ContainerPort: 7777, + }, + }, + Stdin: true, + TTY: true, + }) + expected.StatefulSet.Value.Spec.Template.Spec.Containers = ctr + expected.StatefulSet.Value.Spec.Template.Spec.ServiceAccountName = test.FullName + + expected.NatsBoxDeployment.Value.Spec.Template.Spec.Containers[0].Stdin = true + expected.NatsBoxDeployment.Value.Spec.Template.Spec.Containers[0].TTY = true + + expected.StatefulSet.Value.ObjectMeta.Annotations = annotations() + expected.StatefulSet.Value.ObjectMeta.Labels["test"] = "test" + + expected.StatefulSet.Value.Spec.Template.ObjectMeta.Annotations = annotations() + expected.StatefulSet.Value.Spec.Template.ObjectMeta.Labels["test"] = "test" + + expected.NatsBoxDeployment.Value.ObjectMeta.Annotations = annotations() + expected.NatsBoxDeployment.Value.ObjectMeta.Labels["test"] = "test" + + expected.NatsBoxDeployment.Value.Spec.Template.ObjectMeta.Annotations = annotations() + expected.NatsBoxDeployment.Value.Spec.Template.ObjectMeta.Labels["test"] = "test" + expected.NatsBoxDeployment.Value.Spec.Template.Spec.ServiceAccountName = test.FullName + "-box" + + expected.PodMonitor.HasValue = true + expected.PodMonitor.Value.ObjectMeta.Annotations = annotations() + expected.PodMonitor.Value.ObjectMeta.Labels["test"] = "test" + + expected.Ingress.HasValue = true + expected.Ingress.Value.ObjectMeta.Annotations = annotations() + expected.Ingress.Value.ObjectMeta.Labels["test"] = "test" + + expected.NatsBoxContextsSecret.Value.ObjectMeta.Annotations = annotations() + expected.NatsBoxContextsSecret.Value.ObjectMeta.Labels["test"] = "test" + expected.NatsBoxContextsSecret.Value.StringData["default.json"] = `{ + "password": "bar", + "url": "nats://` + test.FullName + `", + "user": "foo" +} +` + + expected.NatsBoxContentsSecret.Value.ObjectMeta.Annotations = annotations() + expected.NatsBoxContentsSecret.Value.ObjectMeta.Labels["test"] = "test" + + expected.NatsBoxServiceAccount.HasValue = true + expected.NatsBoxServiceAccount.Value.ObjectMeta.Annotations = annotations() + expected.NatsBoxServiceAccount.Value.ObjectMeta.Labels["test"] = "test" + + expected.Service.Value.ObjectMeta.Annotations = annotations() + expected.Service.Value.ObjectMeta.Labels["test"] = "test" + + expected.PodDisruptionBudget.Value.ObjectMeta.Annotations = annotations() + expected.PodDisruptionBudget.Value.ObjectMeta.Labels["test"] = "test" + + expected.ServiceAccount.HasValue = true + expected.ServiceAccount.Value.ObjectMeta.Annotations = annotations() + expected.ServiceAccount.Value.ObjectMeta.Labels["test"] = "test" + + expected.HeadlessService.Value.ObjectMeta.Annotations = annotations() + expected.HeadlessService.Value.ObjectMeta.Labels["test"] = "test" + + expected.ConfigMap.Value.ObjectMeta.Annotations = annotations() + expected.ConfigMap.Value.ObjectMeta.Labels["test"] = "test" + + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "nats", + ContainerPort: 4222, + }, + { + Name: "websocket", + ContainerPort: 8080, + }, + { + Name: "monitor", + ContainerPort: 8222, + }, + } + + expected.HeadlessService.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "websocket", + Port: 8080, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTP, + }, + { + Name: "monitor", + Port: 8222, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTP, + }, + } + + expected.Service.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTCP, + }, + { + Name: "websocket", + Port: 8080, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTP, + }, + } + + RenderAndCheck(t, test, expected) +} diff --git a/helm/charts/nats/test/tls_test.go b/helm/charts/nats/test/tls_test.go new file mode 100644 index 00000000..1dcae1b1 --- /dev/null +++ b/helm/charts/nats/test/tls_test.go @@ -0,0 +1,380 @@ +package test + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "testing" +) + +func TestConfigTls(t *testing.T) { + t.Parallel() + test := DefaultTest() + test.Values = ` +config: + cluster: + enabled: true + tls: + enabled: true + secretName: cluster-tls + nats: + tls: + enabled: true + secretName: nats-tls + merge: + ca_file: /etc/my-ca/ca.crt + verify_cert_and_check_known_urls: true + patch: [{op: add, path: /verify_and_map, value: true}] + leafnodes: + enabled: true + tls: + enabled: true + secretName: leafnodes-tls + websocket: + enabled: true + tls: + enabled: true + secretName: websocket-tls + mqtt: + enabled: true + tls: + enabled: true + secretName: mqtt-tls + gateway: + enabled: true + tls: + enabled: true + secretName: gateway-tls + monitor: + tls: + enabled: true +` + expected := DefaultResources(t, test) + expected.Conf.Value["cluster"] = map[string]any{ + "name": "nats", + "no_advertise": true, + "port": int64(6222), + "routes": []any{ + "tls://nats-0.nats-headless:6222", + "tls://nats-1.nats-headless:6222", + "tls://nats-2.nats-headless:6222", + }, + } + expected.Conf.Value["leafnodes"] = map[string]any{ + "port": int64(7422), + "no_advertise": true, + } + expected.Conf.Value["websocket"] = map[string]any{ + "port": int64(8080), + "compression": true, + } + expected.Conf.Value["mqtt"] = map[string]any{ + "port": int64(1883), + } + expected.Conf.Value["gateway"] = map[string]any{ + "port": int64(7222), + "name": "nats", + } + expected.Conf.Value["https_port"] = expected.Conf.Value["http_port"] + delete(expected.Conf.Value, "http_port") + + replicas3 := int32(3) + expected.StatefulSet.Value.Spec.Replicas = &replicas3 + + volumes := expected.StatefulSet.Value.Spec.Template.Spec.Volumes + natsVm := expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts + reloaderVm := expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].VolumeMounts + for _, protocol := range []string{"nats", "leafnodes", "websocket", "mqtt", "cluster", "gateway"} { + tls := map[string]any{ + "cert_file": "/etc/nats-certs/" + protocol + "/tls.crt", + "key_file": "/etc/nats-certs/" + protocol + "/tls.key", + } + if protocol == "nats" { + tls["ca_file"] = "/etc/my-ca/ca.crt" + tls["verify_cert_and_check_known_urls"] = true + tls["verify_and_map"] = true + expected.Conf.Value["tls"] = tls + } else { + expected.Conf.Value[protocol].(map[string]any)["tls"] = tls + } + + volumes = append(volumes, corev1.Volume{ + Name: protocol + "-tls", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: protocol + "-tls", + }, + }, + }) + + natsVm = append(natsVm, corev1.VolumeMount{ + MountPath: "/etc/nats-certs/" + protocol, + Name: protocol + "-tls", + }) + + reloaderVm = append(reloaderVm, corev1.VolumeMount{ + MountPath: "/etc/nats-certs/" + protocol, + Name: protocol + "-tls", + }) + } + + expected.StatefulSet.Value.Spec.Template.Spec.Volumes = volumes + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts = natsVm + expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].VolumeMounts = reloaderVm + + // reloader certs are alphabetized + reloaderArgs := expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].Args + for _, protocol := range []string{"cluster", "gateway", "leafnodes", "mqtt", "nats", "websocket"} { + if protocol == "nats" { + reloaderArgs = append(reloaderArgs, "-config", "/etc/my-ca/ca.crt") + } + reloaderArgs = append(reloaderArgs, "-config", "/etc/nats-certs/"+protocol+"/tls.crt", "-config", "/etc/nats-certs/"+protocol+"/tls.key") + } + + expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].Args = reloaderArgs + + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "nats", + ContainerPort: 4222, + }, + { + Name: "leafnodes", + ContainerPort: 7422, + }, + { + Name: "websocket", + ContainerPort: 8080, + }, + { + Name: "mqtt", + ContainerPort: 1883, + }, + { + Name: "cluster", + ContainerPort: 6222, + }, + { + Name: "gateway", + ContainerPort: 7222, + }, + { + Name: "monitor", + ContainerPort: 8222, + }, + } + + expected.HeadlessService.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTLS, + }, + { + Name: "leafnodes", + Port: 7422, + TargetPort: intstr.FromString("leafnodes"), + AppProtocol: &appProtocolTLS, + }, + { + Name: "websocket", + Port: 8080, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTPS, + }, + { + Name: "mqtt", + Port: 1883, + TargetPort: intstr.FromString("mqtt"), + AppProtocol: &appProtocolTLS, + }, + { + Name: "cluster", + Port: 6222, + TargetPort: intstr.FromString("cluster"), + AppProtocol: &appProtocolTLS, + }, + { + Name: "gateway", + Port: 7222, + TargetPort: intstr.FromString("gateway"), + AppProtocol: &appProtocolTLS, + }, + { + Name: "monitor", + Port: 8222, + TargetPort: intstr.FromString("monitor"), + AppProtocol: &appProtocolHTTPS, + }, + } + + expected.Service.Value.Spec.Ports = []corev1.ServicePort{ + { + Name: "nats", + Port: 4222, + TargetPort: intstr.FromString("nats"), + AppProtocol: &appProtocolTLS, + }, + { + Name: "leafnodes", + Port: 7422, + TargetPort: intstr.FromString("leafnodes"), + AppProtocol: &appProtocolTLS, + }, + { + Name: "websocket", + Port: 8080, + TargetPort: intstr.FromString("websocket"), + AppProtocol: &appProtocolHTTPS, + }, + { + Name: "mqtt", + Port: 1883, + TargetPort: intstr.FromString("mqtt"), + AppProtocol: &appProtocolTLS, + }, + } + + RenderAndCheck(t, test, expected) +} + +func TestTlsCA(t *testing.T) { + t.Parallel() + for _, tt := range []struct { + name string + key string + dir string + secret bool + }{ + { + name: "ConfigMap", + secret: false, + }, + { + name: "Secret", + secret: true, + key: "my-ca.crt", + dir: "/etc/nats-ca-cert-custom", + }, + } { + t.Run(tt.name, func(t *testing.T) { + test := DefaultTest() + test.Values = ` +config: + nats: + tls: + enabled: true + secretName: nats-tls +tlsCA: + enabled: true` + if tt.secret { + test.Values += ` + secretName: nats-ca` + } else { + test.Values += ` + configMapName: nats-ca` + } + if tt.key != "" { + test.Values += ` + key: ` + tt.key + } + if tt.dir != "" { + test.Values += ` + dir: ` + tt.dir + } + expected := DefaultResources(t, test) + + key := tt.key + if key == "" { + key = "ca.crt" + } + dir := tt.dir + if dir == "" { + dir = "/etc/nats-ca-cert" + } + expected.Conf.Value["tls"] = map[string]any{ + "cert_file": "/etc/nats-certs/nats/tls.crt", + "key_file": "/etc/nats-certs/nats/tls.key", + "ca_file": dir + "/" + key, + } + + expected.NatsBoxContextsSecret.Value.StringData["default.json"] = `{ + "ca": "` + dir + "/" + key + `", + "url": "nats://` + test.FullName + `" +} +` + expected.Service.Value.Spec.Ports[0].AppProtocol = &appProtocolTLS + expected.HeadlessService.Value.Spec.Ports[0].AppProtocol = &appProtocolTLS + + // reloader certs are alphabetized + reloaderArgs := expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].Args + reloaderArgs = append(reloaderArgs, + "-config", dir+"/"+key, + "-config", "/etc/nats-certs/nats/tls.crt", + "-config", "/etc/nats-certs/nats/tls.key") + expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].Args = reloaderArgs + + tlsCAVol := corev1.Volume{ + Name: "tls-ca", + } + if tt.secret { + tlsCAVol.VolumeSource = corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "nats-ca", + }, + } + } else { + tlsCAVol.VolumeSource = corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "nats-ca", + }, + }, + } + } + + tlsCAVm := corev1.VolumeMount{ + Name: "tls-ca", + MountPath: dir, + } + + stsVols := expected.StatefulSet.Value.Spec.Template.Spec.Volumes + natsVm := expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts + reloaderVm := expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].VolumeMounts + + stsVols = append(stsVols, tlsCAVol, corev1.Volume{ + Name: "nats-tls", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "nats-tls", + }, + }, + }) + + natsVm = append(natsVm, tlsCAVm, corev1.VolumeMount{ + MountPath: "/etc/nats-certs/nats", + Name: "nats-tls", + }) + + reloaderVm = append(reloaderVm, tlsCAVm, corev1.VolumeMount{ + MountPath: "/etc/nats-certs/nats", + Name: "nats-tls", + }) + + expected.StatefulSet.Value.Spec.Template.Spec.Volumes = stsVols + expected.StatefulSet.Value.Spec.Template.Spec.Containers[0].VolumeMounts = natsVm + expected.StatefulSet.Value.Spec.Template.Spec.Containers[1].VolumeMounts = reloaderVm + + natsBoxVols := expected.NatsBoxDeployment.Value.Spec.Template.Spec.Volumes + natsBoxVms := expected.NatsBoxDeployment.Value.Spec.Template.Spec.Containers[0].VolumeMounts + + natsBoxVols = append(natsBoxVols, tlsCAVol) + natsBoxVms = append(natsBoxVms, tlsCAVm) + + expected.NatsBoxDeployment.Value.Spec.Template.Spec.Volumes = natsBoxVols + expected.NatsBoxDeployment.Value.Spec.Template.Spec.Containers[0].VolumeMounts = natsBoxVms + + RenderAndCheck(t, test, expected) + }) + } +} diff --git a/helm/charts/nats/values.yaml b/helm/charts/nats/values.yaml index 71938683..56a63fcb 100644 --- a/helm/charts/nats/values.yaml +++ b/helm/charts/nats/values.yaml @@ -1,827 +1,665 @@ -############################### -# # -# NATS Server Configuration # -# # -############################### -nats: +################################################################################ +# Global options +################################################################################ +global: image: - repository: nats - tag: 2.9.20-alpine - pullPolicy: IfNotPresent - # registry: docker.io - - # The servers name prefix, must be used for example when we want a NATS cluster - # spanning multiple Kubernetes clusters. - serverNamePrefix: "" - - # Server Tags - serverTags: - # - "foo" - # - "bar" - - # Sets GOMEMLIMIT environment variable which makes the Go GC be aware of memory limits - # from the container. Recommended to be set to about 90% of the resource memory limits. - # - # More info about the Go GC: https://go.dev/doc/gc-guide - # - # gomemlimit: "4GiB" - - # Toggle profiling. - # This enables nats-server pprof (profiling) port, so you can see goroutines - # stacks, memory heap sizes, etc. - profiling: + # global image pull policy to use for all container images in the chart + # can be overridden by individual image pullPolicy + pullPolicy: + # 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: 6000 - - # Toggle using health check probes to better detect failures. - healthcheck: - # /healthz health check endpoint was introduced in NATS Server 2.7.1 - # Attempt to detect /healthz support by inspecting if tag is >=2.7.1 - detectHealthz: true - # Enable /healthz startupProbe for controlled upgrades of NATS JetStream - enableHealthz: true - # Enable /healthz liveness and readiness probes (supported in >=2.9.0) - # This is a feature flag and will be removed in future releases - enableHealthzLivenessReadiness: false - - # Enable liveness checks. If this fails, then the NATS Server will restarted. - liveness: - enabled: true - - initialDelaySeconds: 10 - timeoutSeconds: 5 - # NOTE: liveness check + terminationGracePeriodSeconds can introduce unnecessarily long outages - # due to the coupling between liveness probe and terminationGracePeriodSeconds. - # To avoid this, we make the periodSeconds of the liveness check to be about half the default - # time that it takes for lame duck graceful stop. - # - # In case of using Kubernetes +1.22 with probe-level terminationGracePeriodSeconds - # we could revise this but for now keep a minimal liveness check. - # - # More info: - # - # https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#probe-level-terminationgraceperiodseconds - # https://github.com/kubernetes/kubernetes/issues/64715 - # - periodSeconds: 30 - successThreshold: 1 - failureThreshold: 3 - - # Override the health check path - # httpGet: - # path: /healthz?js-enabled=true - - # Only for Kubernetes +1.22 that have pod level probes enabled. - # terminationGracePeriodSeconds: 5 - - # Periodically check for the server to be ready for connections while - # the NATS container is running. - readiness: - enabled: true + 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: [] - initialDelaySeconds: 10 - timeoutSeconds: 5 - periodSeconds: 10 - successThreshold: 1 - failureThreshold: 3 - - # Override the health check path - # httpGet: - # path: /healthz?js-server-only=true + jetstream: + enabled: false - # Enable startup checks to confirm server is ready for traffic. - # This is recommended for JetStream deployments since in cluster mode - # it will try to ensure that the server is ready to serve streams. - startup: + fileStore: enabled: true - - initialDelaySeconds: 10 - timeoutSeconds: 5 - periodSeconds: 10 - successThreshold: 1 - failureThreshold: 90 - - # Override the health check path - # httpGet: - # path: /healthz - - ## hostNetwork - hostNetwork: false - - ## Pod Dns Policy. Default is ClusterFirst - ## ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy - dnsPolicy: ClusterFirst - - # Adds a hash of the ConfigMap as a pod annotation - # This will cause the StatefulSet to roll when the ConfigMap is updated - configChecksumAnnotation: true - - # securityContext for the nats container - securityContext: {} - - # Toggle whether to enable external access. - # This binds a host port for clients, gateways and leafnodes. - externalAccess: false - - # Toggle to disable client advertisements (connect_urls), - # in case of running behind a load balancer - # it might be required to disable advertisements. - advertise: true - - # In case both external access and advertise are enabled - # then a service account would be required to be able to - # gather the public ip from a node. - serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - - # Toggle whether to automatically mount Service Account token in the pod - # not set means default value, boolean true/false overrides default value - # automountServiceAccountToken: true - - # The number of connect attempts against discovered routes. - connectRetries: 120 - - # selector matchLabels for the server and service. - # If left empty defaults are used. - # This is helpful if you are updating from Chart version <=7.4 - selectorLabels: {} - - # Resources to add to the container - # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: {} - - client: + 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 - portName: "client" - - # extraEnv is the list of environment variables to add to the nats-server container - extraEnv: [] - - # Server settings. - limits: - maxConnections: - maxSubscriptions: - maxControlLine: - maxPayload: - - writeDeadline: - maxPending: - maxPings: - - # How many seconds should pass before sending a PING - # to a client that has no activity. - pingInterval: - - # grace period after pod begins shutdown before starting to close client connections - lameDuckGracePeriod: "10s" - - # duration over which to slowly close close client connections after lameDuckGracePeriod has passed - lameDuckDuration: "30s" - - # terminationGracePeriodSeconds determines how long to wait for graceful shutdown - # this should be at least `lameDuckGracePeriod` + `lameDuckDuration` + 20s shutdown overhead - terminationGracePeriodSeconds: 60 - - logging: - debug: - trace: - logtime: - connectErrorReports: - reconnectErrorReports: - - # customConfigSecret can be used to use an custom secret for the config - # of the NATS Server. - # NOTE: For this to work the name of the configuration has to be - # called `nats.conf`. - # - # e.g. kubectl create secret generic custom-nats-conf --from-file nats.conf - # - # customConfigSecret: - # name: - # - # Alternately, the generated config can be extended with extra imports using the below syntax. - # The benefit of this is that cluster settings can be built up via helm values, but external - # secrets can be referenced and imported alongside it. - # - # config: - # : - # - # name: "" - # - # e.g: - # - # config: - # - name: ssh-key - # secret: - # secretName: ssh-key - # - name: config-vol - # configMap: - # name: log-config - - # mappings is used to configure subject mapping - # https://docs.nats.io/running-a-nats-service/configuration/configuring_subject_mapping - # e.g: - # mappings: - # foo: bar - # foo.cluster.scoped: - # - destination: bar.cluster.scoped - # weight: 70% - # cluster: us-west-1 - # - destination: foobar.cluster.scoped - # weight: 30% - # cluster: us-east-1 - mappings: {} + 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 - jetstream: + profiling: enabled: false + port: 65432 - # Jetstream Domain - domain: - - # Jetstream Unique Tag prevent placing a stream in the same availability zone twice. - uniqueTag: - - maxOutstandingCatchup: - - ########################## - # # - # Jetstream Encryption # - # # - ########################## - encryption: - # Use key if you want to provide the key via Helm Values - # key: random_key - - # Use a secret reference if you want to get a key from a secret - # secret: - # name: "nats-jetstream-encryption" - # key: "key" - - # Use cipher if you want to choose a different cipher from the default. - # cipher: aes - - ############################# - # # - # Jetstream Memory Storage # - # # - ############################# - memStorage: - enabled: true - size: 1Gi + resolver: + enabled: false + dir: /data/resolver - ############################ - # # - # Jetstream File Storage # - # # - ############################ - fileStorage: + ############################################################ + # stateful set -> volume claim templates -> resolver pvc + ############################################################ + pvc: enabled: true - storageDirectory: /data - - # Set for use with existing PVC - # existingClaim: jetstream-pvc - # claimStorageSize: 10Gi - - # Use below block to create new persistent volume - # only used if existingClaim is not specified - size: 10Gi - # storageClassName: "" - accessModes: - - ReadWriteOnce - annotations: - # key: "value" - - # Use below if fileStorage is not enabled but you are persisting - # data using an alternative to PVC (e.g. hostPath) - # These set the corresponding jetstream configuration in nats.conf. - # store_dir: "/data" - # max_file: "10Gi" - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - - # tls: - # allowNonTLS: false - # secret: - # name: nats-client-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - -mqtt: - enabled: false - ackWait: 1m - maxAckPending: 100 - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - - # - # tls: - # secret: - # name: nats-mqtt-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - -nameOverride: "" -namespaceOverride: "" - -# An array of imagePullSecrets, and they have to be created manually in the same namespace -# ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ -imagePullSecrets: [] - -# Toggle whether to use setup a Pod Security Context -# ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ -securityContext: {} -# securityContext: -# fsGroup: 1000 -# runAsUser: 1000 -# runAsNonRoot: true - -# Affinity for pod assignment -# ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity -affinity: {} - -## Pod priority class name -## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass -priorityClassName: null - -# Service topology -# ref: https://kubernetes.io/docs/concepts/services-networking/service-topology/ -topologyKeys: [] - -# Pod Topology Spread Constraints -# ref https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ -topologySpreadConstraints: [] -# - maxSkew: 1 -# topologyKey: zone -# whenUnsatisfiable: DoNotSchedule - -# Annotations to add to the NATS pods -# ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ -podAnnotations: {} -# key: "value" - -# Define a Pod Disruption Budget for the stateful set -# ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ -podDisruptionBudget: - enabled: true - maxUnavailable: 1 - # minAvailable: 1 - -# Node labels for pod assignment -# Ref: https://kubernetes.io/docs/user-guide/node-selection/ -nodeSelector: {} - -# Node tolerations for server scheduling to nodes with taints -# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ -# -tolerations: [] -# - key: "key" -# operator: "Equal|Exists" -# value: "value" -# effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" - -# Annotations to add to the NATS StatefulSet -statefulSetAnnotations: {} - -# Labels to add to the pods of the NATS StatefulSet -statefulSetPodLabels: {} - -# Annotations to add to the NATS Service -serviceAnnotations: {} - -# additionalContainers are the sidecar containers to add to the NATS StatefulSet -additionalContainers: [] - -# additionalVolumes are the additional volumes to add to the NATS StatefulSet -additionalVolumes: [] - -# additionalVolumeMounts are the additional volume mounts to add to the nats-server and nats-server-config-reloader containers -additionalVolumeMounts: [] - -cluster: - enabled: false - replicas: 3 - noAdvertise: false - - # Explicitly set routes for clustering. - # When JetStream is enabled, the serverName must be unique in the cluster. - extraRoutes: [] - - # authorization: - # user: foo - # password: pwd - # timeout: 0.5 - -# Leafnode connections to extend a cluster: -# -# https://docs.nats.io/nats-server/configuration/leafnodes -# -leafnodes: - enabled: false - port: 7422 - noAdvertise: false - # remotes: - # - url: "tls://connect.ngs.global:7422" - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # + 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: # - # tls: - # secret: - # name: nats-client-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - -# Gateway connections to create a super cluster -# -# https://docs.nats.io/nats-server/configuration/gateways -# -gateway: - enabled: false - port: 7522 - name: "default" - # authorization: - # user: foo - # password: pwd - # timeout: 0.5 - # rejectUnknownCluster: false - - # You can add an implicit advertise address instead of using from Node's IP - # could also be a fqdn address - # advertise: "nats.example.com" - - ############################# - # # - # List of remote gateways # - # # - ############################# - # gateways: - # - name: other - # url: nats://my-gateway-url:7522 - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls + # merge: + # $include: ./my-config.conf + # zzz$include: ./my-config-last.conf + # server_name: nats + # authorization: + # token: << $TOKEN >> + # jetstream: + # max_memory_store: << 1GB >> # - # tls: - # secret: - # name: nats-client-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - -# In case of both external access and advertisements being -# enabled, an initializer container will be used to gather -# the public ips. -bootconfig: + # 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: natsio/nats-boot-config - tag: 0.12.0 - pullPolicy: IfNotPresent - # registry: docker.io - - securityContext: {} - - # Resources to add to the container - # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: {} - -# NATS Box -# -# https://github.com/nats-io/nats-box -# -natsbox: - enabled: true - image: - repository: natsio/nats-box - tag: 0.13.8 - pullPolicy: IfNotPresent - # registry: docker.io - - securityContext: {} - - # Resources to add to the container - # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: {} - - # Annotations to add to the natsbox deployment - # ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - annotations: {} - # key: "value" - - # Labels to add to the natsbox deployment - # ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - additionalLabels: {} - - # An array of imagePullSecrets, and they have to be created manually in the same namespace - # ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - imagePullSecrets: [] - # - name: dockerhub - - # credentials: - # secret: - # name: nats-sys-creds - # key: sys.creds - - # Annotations to add to the box pods - # ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - podAnnotations: {} - # key: "value" - - # Labels to add to the box pods - # ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - podLabels: {} - # key: "value" - - # Affinity for nats box pod assignment - # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - affinity: {} - - # Node labels for pod assignment - # Ref: https://kubernetes.io/docs/user-guide/node-selection/ - nodeSelector: {} - - # Node tolerations for server scheduling to nodes with taints - # Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + repository: nats + tag: 2.9.20-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: # - tolerations: [] - # - key: "key" - # operator: "Equal|Exists" - # value: "value" - # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" - - # Additional nats-box server Volume mounts - extraVolumeMounts: [] - - # Additional nats-box server Volumes - extraVolumes: [] - - # Toggle whether to automatically mount Service Account token in the pod - # not set means default value, boolean true/false overrides default value - # automountServiceAccountToken: true - -# The NATS config reloader image to use. + # 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.12.0 - pullPolicy: IfNotPresent - # registry: docker.io - - securityContext: {} - - # Resources to add to the container - # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: {} - - extraConfigs: [] - -# Prometheus NATS Exporter configuration. -exporter: - enabled: true + tag: 0.11.0 + 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.12.0 - pullPolicy: IfNotPresent - # registry: docker.io - - portName: metrics - securityContext: {} - - # Resources to add to the container - # ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: {} - - # override the default args passed to the exporter - # see https://github.com/nats-io/prometheus-nats-exporter#usage - # make sure to pass HTTP monitoring port URL as last arg, e.g ["-connz", "http://localhost:8222/"] - args: [] - # Prometheus operator ServiceMonitor support. Exporter has to be enabled - serviceMonitor: + 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 - ## Specify the namespace where Prometheus Operator is running - ## - # namespace: monitoring - labels: {} - annotations: {} - path: /metrics - # interval: - # scrapeTimeout: - -# Authentication setup -auth: - enabled: false - - # basic: - # noAuthUser: - # # List of users that can connect with basic auth, - # # that belong to the global account. - # users: - - # defaultPermissions: - # publish: ["SANDBOX.*"] - # subscribe: ["SANDBOX.>"] - - # # List of accounts with users that can connect - # # using basic auth. - # accounts: - - # Reference to the Operator JWT. - # operatorjwt: - # configMap: - # name: operator-jwt - # key: KO.jwt - # Token authentication - # token: - - # NKey authentication - # nkeys: - # users: - - # Public key of the System Account - # systemAccount: - - resolver: - # Disables the resolver by default - type: none + # 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: - ########################################## - # # - # Embedded NATS Account Server Resolver # - # # - ########################################## - # type: full - # If the resolver type is 'full', delete when enabled will rename the jwt. - allowDelete: false - - # Interval at which a nats-server with a nats based account resolver will compare - # it's state with one random nats based account resolver in the cluster and if needed, - # exchange jwt and converge on the same set of jwt. - interval: 2m - - # Operator JWT - operator: - - # System Account Public NKEY - systemAccount: - - # resolverPreload: - # : - - # Directory in which the account JWTs will be stored. - store: - dir: "/accounts/jwt" +############################################################ +# service +############################################################ +service: + enabled: true - # Size of the account JWT storage. - size: 1Gi + # 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 - # StorageClass of JWT storage claim. - # storageClassName: "" - - ############################## - # # - # Memory resolver settings # - # # - ############################## - # type: memory - # - # Use a configmap reference which will be mounted - # into the container. - # - # configMap: - # name: nats-accounts - # key: resolver.conf - - ########################## - # # - # URL resolver settings # - # # - ########################## - # type: URL - # url: "http://nats-account-server:9090/jwt/v1/accounts/" - -websocket: + # 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 - port: 443 - noTLS: true + # 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: - sameOrigin: false - allowedOrigins: [] - # This will optionally specify what host:port for websocket - # connections to be advertised in the cluster. - # advertise: "host:port" +############################################################ +# natsBox +# +# NATS Box Deployment and associated resources +############################################################ +natsBox: + enabled: true - # Set the handshake timeout for websocket connections - # handshakeTimeout: 5s + ############################################################ + # 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.13.5 + 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: -# Network Policy configuration -networkPolicy: - enabled: false - # Don't require client label for connections - # When set to false, only pods with the correct client label will have network access to the ports - # NATS is listening on. When true, NATS will accept connections from any source - # (with the correct destination port). - allowExternal: true - # Add extra ingress rules to the NetworkPolicy - # e.g: - # extraIngress: - # - ports: - # - port: 1234 - # from: - # - podSelector: - # - matchLabels: - # - role: frontend - # - podSelector: - # - matchExpressions: - # - key: role - # operator: In - # values: - # - frontend - extraIngress: [] - # Add extra ingress rules to the NetworkPolicy - # e.g: - # extraEgress: - # - ports: - # - port: 1234 - # to: - # - podSelector: - # - matchLabels: - # - role: frontend - # - podSelector: - # - matchExpressions: - # - key: role - # operator: In - # values: - # - frontend - extraEgress: [] - # Labels to match to allow traffic from other namespaces - ingressNSMatchLabels: {} - # Pod labels to match to allow traffic from other namespaces - ingressNSPodMatchLabels: {} - -# Cluster Domain configured on the kubelets -# https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/ -k8sClusterDomain: cluster.local - -# Define if NATS is using FQDN name for clustering (i.e. nats-0.nats.default.svc.cluster.local) or short name (i.e. nats-0.nats.default). -useFQDN: true - -# Add labels to all the deployed resources -commonLabels: {} - -# podManagementPolicy controls how pods are created during initial scale up, -# when replacing pods on nodes, or when scaling down. -podManagementPolicy: Parallel - -# Shared volume to be mounted in pods for pid -pidVolume: - emptyDir: {} - -# Shared volume to be mounted in pods for advertiseconfig -advertiseconfigVolume: - emptyDir: {} + +################################################################################ +# 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: []