diff --git a/.gitignore b/.gitignore index b32534288..cc9c4a8d9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ charts/**/charts/ # helm unittest plugin __snapshot__ +.debug/ bin/** !bin/README.md diff --git a/charts/posit-chronicle/.helmignore b/charts/posit-chronicle/.helmignore index 93f437e0a..eb2b97368 100644 --- a/charts/posit-chronicle/.helmignore +++ b/charts/posit-chronicle/.helmignore @@ -27,3 +27,6 @@ ci/ lint/ tests/ + +# helm unittest debug files +.debug/ diff --git a/charts/posit-chronicle/Chart.yaml b/charts/posit-chronicle/Chart.yaml index 3f3d89a7d..301a9ee6e 100644 --- a/charts/posit-chronicle/Chart.yaml +++ b/charts/posit-chronicle/Chart.yaml @@ -1,27 +1,37 @@ apiVersion: v2 name: posit-chronicle description: Official Helm chart for Posit Chronicle Server -version: 0.3.8 -appVersion: 2025.03.0 -icon: https://rstudio.com/wp-content/uploads/2018/10/RStudio-Logo-Flat.png +version: 0.4.0 +appVersion: 2025.05.1 +icon: https://posit.co/wp-content/themes/Posit/dist/images/favicon/apple-touch-icon-180x180.png home: https://www.posit.co sources: - https://github.com/rstudio/helm maintainers: - - name: sol-eng + - name: rstudio email: docker@posit.co url: https://github.com/rstudio/helm annotations: + artifacthub.io/images: | + - name: chronicle + image: ghcr.io/rstudio/chronicle:2025.05.1 + platforms: + - linux/amd64 artifacthub.io/license: MIT artifacthub.io/links: | - name: Chronicle Documentation url: https://docs.posit.co/chronicle + - name: Posit Helm Documentation + url: https://docs.posit.co/helm - name: Docker Images url: https://github.com/rstudio/rstudio-docker-products - name: Posit Community url: https://forum.posit.co/c/posit-professional-hosted/5 - name: About Posit Team url: https://posit.co/products/enterprise/team/ + artifacthub.io/recommendations: | + - url: https://artifacthub.io/packages/helm/rstudio/rstudio-connect + - url: https://artifacthub.io/packages/helm/rstudio/rstudio-workbench keywords: - "rstudio" - "posit" diff --git a/charts/posit-chronicle/NEWS.md b/charts/posit-chronicle/NEWS.md index fc0887889..d3ab72ca0 100644 --- a/charts/posit-chronicle/NEWS.md +++ b/charts/posit-chronicle/NEWS.md @@ -1,5 +1,36 @@ # Changelog +## 0.4.0 + +- Improvements for chart annotations. +- Values changes. + - Allow name and namespace overrides in chart values. + - Add common labels and annotations values to apply to all resources. + - Moves default tag source to appVersion, image.tag changed to a blank override. + - Separated an image.registry value from the image.repository value. + - Improve documentation of values.yaml and add a values.schema.json definition for input validation. + - An S3 bucket must now be specified in S3 Storage backend is enabled. + - Remove deprecated value `config.LocalStorage.RetentionPeriod`. +- Changes to chart behavior. + - Resource names are now applied dynamically based on the release name. + - Additional default recommended Kubernetes labels have been applied to all resources. + - Storage configuration is now validated and requires at least one of local or s3 storage be enabled. + - `extraSecretMounts` can now be specified to mount additional secrets, such as certificates, into the pod. + - Storage class can now be overridden on the pod's volume claim template. + - Selector labels definitions between pod and service are now merged into a single definition. Removed the ability to override these values. + - Add support for additional custom manifest input via `extraObjects` value. + - `securityContext` is now specified for both the pod and container. The default values are set to prevent privilege escalation, running as root, and set the `fsGroup` to match Chronicle's service account. +- Add unittests for chart templates. +- Various Chart.yaml metadata changes. + - Fix logo URL. + - Add suggestions for compatible product charts. + - Add annotation to include source image used in pod. +- Update README.md and other documentation to reflect changes. + +## 0.3.8 + +- Update documentation and support links. + ## 0.3.7 - Bump Chronicle to version 2025.03.0 diff --git a/charts/posit-chronicle/README.md b/charts/posit-chronicle/README.md index 991e2afbc..1aa9d3e6c 100644 --- a/charts/posit-chronicle/README.md +++ b/charts/posit-chronicle/README.md @@ -1,6 +1,6 @@ # Posit Chronicle -![Version: 0.3.8](https://img.shields.io/badge/Version-0.3.8-informational?style=flat-square) ![AppVersion: 2025.03.0](https://img.shields.io/badge/AppVersion-2025.03.0-informational?style=flat-square) +![Version: 0.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![AppVersion: 2025.05.1](https://img.shields.io/badge/AppVersion-2025.05.1-informational?style=flat-square) #### _Official Helm chart for Posit Chronicle Server_ @@ -25,11 +25,11 @@ To ensure a stable production deployment: ## Installing the chart -To install the chart with the release name `my-release` at version 0.3.8: +To install the chart with the release name `my-release` at version 0.4.0: ```{.bash} helm repo add rstudio https://helm.rstudio.com -helm upgrade --install my-release rstudio/posit-chronicle --version=0.3.8 +helm upgrade --install my-release rstudio/posit-chronicle --version=0.4.0 ``` To explore other chart versions, look at: @@ -40,77 +40,110 @@ helm search repo rstudio/posit-chronicle -l ## Usage -This chart deploys only the Chronicle server and is meant to be used in tandem -with the Workbench and Connect charts. To actually send data to the server, you -will need to run the Chronicle agent as a sidecar container on your -Workbench or Connect server pods by setting `pod.sidecar` in their respective `values.yaml` files +This chart deploys the Chronicle server and is intended to be used in tandem +with the Workbench and Connect charts. For the server to receive data, +the Chronicle agent must be deployed as a sidecar container alongside +Workbench or Connect server pods. -Here is an example of Helm values to run the agent sidecar in **Workbench**, -where we set up a shared volume between containers for audit logs: +Both [Workbench](https://docs.posit.co/helm/charts/rstudio-workbench/README.html#chronicle-agent) +(`>=0.9.2`) and [Connect](https://docs.posit.co/helm/charts/rstudio-connect/README.html#chronicle-agent) +(`>=0.7.26`) charts include out of the box support for Chronicle agent sidecars. +The sidecar can be enabled by setting the `chronicleAgent.enabled` value to `true` +in either product's chart. -```yaml -pod: - # We will need to create a new volume to share audit logs between - # the rstudio (workbench) and chronicle-agent containers - volumes: - - name: logs - emptyDir: {} - volumeMounts: - - name: logs - mountPath: "/var/lib/rstudio-server/audit" - sidecar: - - name: chronicle-agent - image: ghcr.io/rstudio/chronicle-agent:2025.03.0 - volumeMounts: - - name: logs - mountPath: "/var/lib/rstudio-server/audit" - env: - - name: CHRONICLE_SERVER_ADDRESS - value: "http://chronicle-server.default" -``` +For additional information on deploying and configuring Chronicle agents, +see the [Workbench](https://docs.posit.co/helm/charts/rstudio-workbench/README.html#chronicle-agent) +or [Connect](https://docs.posit.co/helm/charts/rstudio-connect/README.html#chronicle-agent) +chart documentation. + +## HTTPS Configuration -And here is an example of Helm values for Connect, where a **Connect** -API key from a Kubernetes Secret is used to unlock more detailed metrics: +Chronicle can be configured to use HTTPS for secure communication. The +`config.HTTPS` section of the configuration allows you to specify the certificate +and key files to use for HTTPS. Both `config.HTTPS.Certificate` and +`config.HTTPS.Key` are expected to be paths to files accessible by Chronicle. +The `extraSecretMounts` value can be used to mount the certificate and key files +into the Chronicle pod. Here is an example of how to do this, assuming that +the certificate and key files are stored together in a Kubernetes TLS secret: ```yaml -pod: - sidecar: - - name: chronicle-agent - image: ghcr.io/rstudio/chronicle-agent:2025.03.0 - env: - - name: CHRONICLE_SERVER_ADDRESS - value: "http://chronicle-server.default" - - name: CONNECT_API_KEY - valueFrom: - secretKeyRef: - name: connect - key: apikey +extraSecretMounts: + - name: chronicle-https + mountPath: /etc/chronicle/ssl + secretName: chronicle-https + items: + - key: tls.crt + - key: tls.key +config: + HTTPS: + Enabled: true + Certificate: "/etc/chronicle/ssl/tls.crt" + Key: "/etc/chronicle/ssl/tls.key" ``` -Note that it is up to the user to provision this Kubernetes Secret for the -Connect API key. - ## Storage Configuration -Chronicle can be configured to persist data to a local Kubernetes Volume, AWS S3, or both. +Chronicle can be configured to persist data to local storage, AWS S3, or both. + +### Local Storage -The default configuration uses a local volume, which is suitable if you'd like to -access and analyze the data within your cluster: +The default configuration will save data to a persistent volume, which +is suitable if you'd like to access and analyze the data within your cluster. +The below values show the default configuration for storage: ```yaml +persistence: + enabled: true + accessModes: + - ReadWriteOnce + size: 10Gi config: LocalStorage: Enabled: true - Location: "/chronicle-data" - RetentionPeriod: "30d" + Location: "/opt/chronicle-data" +``` + +The `persistence` section configures the persistent volume claim in the +cluster while the `config.LocalStorage` section directly applies to Chronicle's +configuration file. The persistent volume will always mount to the path specified +by `config.LocalStorage.Path` to avoid potential misconfiguration and data loss. + +By default, Chronicle requests 10Gi of storage. In most cases, this amount of +storage should be sufficient for thirty days of monitoring data. + +::: {.callout-important} +Users are responsible for managing the size of the persistent volume, retention +of stored data, and controlling access to the data from other pods. Consider +utilizing a dynamic volume provisioner to avoid storage-related service +interruptions. +::: + +While attaching the volume to Workbench is a valid method of accessing the data, +keep in mind that some data captured by Chronicle may be considered sensitive and +should be handled with care. + +#### Alternate Storage Class + +Depending on the environment or cloud hosting Chronicle, many CSI drivers may +be available to use as the persistent volume's storage class. While Chronicle +only natively supports local storage or S3, CSI drivers may be used to provide +support for other storage backends such as Azure Blob Storage, Azure Files, Google +Cloud Storage, or other object storage solutions. The storage class for persistent +volumes can be set with the following value: + +```yaml +persistence: + storageClass: "alternate-storage-class" ``` -`retentionPeriod` controls how long usage data are kept. For example, `"120m"` -for 120 minutes, `"36h"` for 36 hours, `14d` for two weeks, or `"0"` for unbounded retention. -(Units smaller than seconds or larger than days are not supported.) +Please report and performance or stability issues with alternate storage configurations +to the [issue tracker](https://github.com/rstudio/helm/issues/new?template=chronicle.md). + +### S3 Storage -You can also persist data to AWS S3 instead of (or in addition to) local -storage: +Chronicle can also be configured to store data in an S3 bucket. This can be +useful for controlling access to data or taking advantage of S3 features +such as lifecycle management. ```yaml config: @@ -120,13 +153,13 @@ config: Region: "us-east-2" ``` -### Using Iam for S3 +#### Using IAM roles for S3 access -If you are running on EKS, you can use [IAM Roles for Service +If Chronicle is running on EKS, [IAM Roles for Service Accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) -to manage the credentials needed to access S3. In this scenario, once you have [created an IAM -role](https://docs.aws.amazon.com/eks/latest/userguide/create-service-account-iam-policy-and-role.html), -you can use this role as an annotation on the existing Service Account: +can be utilized to manage the credentials needed to access S3. Once [an IAM role has been +created](https://docs.aws.amazon.com/eks/latest/userguide/create-service-account-iam-policy-and-role.html), +the role can be attached as an annotation on Chronicle's Service Account: ```yaml serviceaccount: @@ -135,8 +168,7 @@ serviceaccount: eks.amazonaws.com/role-arn: arn:aws:iam::123456789000:role/iam-role-name-here ``` -If you are unable to use IAM Roles for Service Accounts, there are any number of -alternatives for injecting AWS credentials into a container. As a fallback, +There are alternatives for injecting AWS credentials into a container. As a fallback, the S3 storage config allows specifying a profile: ```yaml @@ -148,7 +180,7 @@ config: Region: "us-east-2" ``` -### Needed S3 Policy Permissions +#### Needed S3 Policy Permissions The credentials Chronicle uses for S3 storage must have the following permissions enabled: @@ -157,50 +189,74 @@ The credentials Chronicle uses for S3 storage must have the following permission - `s3:PutObject` - `s3:DeleteObject` +## Additional Configuration + +Chronicle has additional configuration options not specifically mentioned in this +README. For additional information on administrating or using Posit Chronicle, see +the [Chronicle documentation](https://docs.posit.co/chronicle/). + +For details on server configuration options, see the [advanced server configuration +reference page](https://docs.posit.co/chronicle/appendix/library/advanced-server.html). + ## Values | Key | Type | Default | Description | |-----|------|---------|-------------| -| config.HTTPS.Certificate | string | `""` | | -| config.HTTPS.Enabled | bool | `false` | | -| config.HTTPS.Key | string | `""` | | -| config.LocalStorage.Enabled | bool | `true` | | -| config.LocalStorage.Location | string | `"./chronicle-data"` | | -| config.LocalStorage.RetentionPeriod | string | `"30d"` | | -| config.Logging.ServiceLog | string | `"STDOUT"` | | -| config.Logging.ServiceLogFormat | string | `"TEXT"` | | -| config.Logging.ServiceLogLevel | string | `"INFO"` | | -| config.Metrics.Enabled | bool | `true` | | -| config.Profiling.Enabled | bool | `false` | | -| config.S3Storage.Bucket | string | `"posit-chronicle"` | | -| config.S3Storage.Enabled | bool | `false` | | -| config.S3Storage.Prefix | string | `""` | | -| config.S3Storage.Profile | string | `""` | | -| config.S3Storage.Region | string | `"us-east-2"` | | -| image.imagePullPolicy | string | `"IfNotPresent"` | | -| image.repository | string | `"ghcr.io/rstudio/chronicle"` | | -| image.tag | string | `"2025.03.0"` | | -| nodeSelector | object | `{}` | A map used verbatim as the pod's "nodeSelector" definition | -| pod.affinity | object | `{}` | A map used verbatim as the pod's "affinity" definition | -| pod.annotations | object | `{}` | Additional annotations to add to the chronicle-server pods | -| pod.args[0] | string | `"start"` | | -| pod.args[1] | string | `"-c"` | | -| pod.args[2] | string | `"/etc/posit-chronicle/posit-chronicle.gcfg"` | | -| pod.command | string | `"/chronicle"` | The command and args to run in the chronicle-server container | -| pod.env | list | `[]` | Optional environment variables | -| pod.labels | object | `{}` | Additional labels to add to the chronicle-server pods | -| pod.selectorLabels | object | `{}` | Additional selector labels to add to the chronicle-server pods | +| commonAnnotations | object | `{}` | Common annotations to add to all resources | +| commonLabels | object | `{}` | Common labels to add to all resources | +| config.HTTPS.Certificate | string | `""` | Path to a PEM encoded certificate file, required if `HTTPS.Enabled=true` | +| config.HTTPS.Enabled | bool | `false` | If set to true, Chronicle will use HTTPS instead of HTTP | +| config.HTTPS.Key | string | `""` | Path to a PEM encoded private key file corresponding to the specified certificate, required if `HTTPS.Enabled=true` | +| config.LocalStorage.Enabled | bool | `true` | Use `config.LocalStorage.Path` for data storage if true, use in conjunction with `persistence.enabled=true` for persistent data storage | +| config.LocalStorage.Path | string | `"/opt/chronicle-data"` | The path to use for local storage | +| config.Logging.ServiceLog | string | `"STDOUT"` | Specifies the output for log messages, can be one of "STDOUT", "STDERR", or a file path | +| config.Logging.ServiceLogFormat | string | `"TEXT"` | The log format for the service, can be one of "TEXT" or "JSON" | +| config.Logging.ServiceLogLevel | string | `"INFO"` | The log level for the service, can be one of "TRACE", "DEBUG", "INFO", "WARN", or "ERROR" | +| config.Metrics.Enabled | bool | `false` | Exposes a metrics endpoint for Prometheus if true | +| config.Profiling.Enabled | bool | `false` | Exposes a pprof profiling server if true | +| config.Profiling.Port | int | `3030` | The port to use for the profiling server | +| config.S3Storage.Bucket | string | `""` | The S3 bucket to use for storage, required if `S3Storage.Enabled=true` | +| config.S3Storage.Enabled | bool | `false` | Use S3 for data storage if true | +| config.S3Storage.Prefix | string | `""` | An optional prefix path to use when writing to the S3 bucket | +| config.S3Storage.Profile | string | `""` | An IAM Profile to use for accessing the S3 bucket, default is to read from the `AWS_PROFILE` env var | +| config.S3Storage.Region | string | `""` | Region of the S3 bucket, default is to read from the `AWS_REGION` env var | +| extraObjects | list | `[]` | Additional manifests to deploy with the chart with template value rendering | +| extraSecretMounts | list | `[]` | Additional secrets to mount to the Chronicle server pod | +| fullnameOverride | string | `""` | Override for the full name of the release | +| image.pullPolicy | string | `"IfNotPresent"` | The image pull policy | +| image.registry | string | `"ghcr.io"` | The image registry | +| image.repository | string | `"rstudio/chronicle"` | The image repository | +| image.securityContext | object | `{"allowPrivilegeEscalation":false,"runAsNonRoot":true}` | The container-level security context ([reference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#securitycontext-v1-core)) | +| image.sha | string | `""` | The image digest | +| image.tag | string | `""` | The image tag, defaults to the chart app version | +| nameOverride | string | `""` | Override for the name of the release | +| namespaceOverride | string | `""` | Override for the namespace of the chart deployment | +| persistence.accessModes | list | `["ReadWriteOnce"]` | Persistent Volume Access Modes | +| persistence.annotations | object | `{}` | Additional annotations for the PVC | +| persistence.enabled | bool | `true` | Enable persistence using Persistent Volume Claims | +| persistence.finalizers | list | `["kubernetes.io/pvc-protection"]` | Finalizers for the PVC | +| persistence.labels | object | `{}` | Additional labels for the PVC | +| persistence.selectorLabels | object | `{}` | Selector to match an existing Persistent Volume for the data PVC | +| persistence.size | string | `"10Gi"` | Size of the data volume | +| persistence.storageClassName | string | `""` | Persistent Volume Storage Class, defaults to the default Storage Class for the cluster | +| pod.affinity | object | `{}` | A map used verbatim as the pod "affinity" definition | +| pod.annotations | object | `{}` | Additional annotations for pods | +| pod.args | list | `[]` | The arguments to pass to the command, defaults to the image `CMD` values | +| pod.command | list | `[]` | The command to run in the Chronicle server container, defaults to the image `ENTRYPOINT` value | +| pod.env | list | `[]` | Additional environment variables to set on the Chronicle server container | +| pod.labels | object | `{}` | Additional labels for pods | +| pod.nodeSelector | object | `{}` | A map used verbatim as the pod "nodeSelector" definition | +| pod.securityContext | object | `{"fsGroup":1000,"fsGroupChangePolicy":"OnRootMismatch"}` | The pod-level security context ([reference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#podsecuritycontext-v1-core)) | | pod.terminationGracePeriodSeconds | int | `30` | The termination grace period seconds allowed for the pod before shutdown | -| pod.tolerations | list | `[]` | An array used verbatim as the pod's "tolerations" definition | -| replicas | int | `1` | The number of replica pods to maintain for this service | -| service.annotations | object | `{}` | Additional annotations to add to the chronicle-server service | -| service.labels | object | `{}` | Additional labels to add to the chronicle-server service | -| service.port | int | `80` | The port to use for the REST service | -| service.selectorLabels | object | `{}` | Additional selector labels to add to the chronicle-server service | -| serviceaccount.annotations | object | `{}` | Additional annotations to add to the chronicle-server serviceaccount | -| serviceaccount.create | bool | `false` | | -| serviceaccount.labels | object | `{}` | Additional labels to add to the chronicle-server serviceaccount | -| storage.persistentVolumeSize | string | `"1Gi"` | | +| pod.tolerations | list | `[]` | An array used verbatim as the pod "tolerations" definition | +| replicas | int | `1` | The number of replica pods to maintain | +| service.annotations | object | `{}` | Annotations to add to the service | +| service.labels | object | `{}` | Labels to add to the service | +| service.port | int | `80` | The port to use for the REST API service | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| serviceAccount.create | bool | `false` | Creates a service account for Posit Chronicle if true | +| serviceAccount.labels | object | `{}` | Labels to add to the service account | +| serviceAccount.name | string | `""` | Override for the service account name, defaults to fullname | ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.13.1](https://github.com/norwoodj/helm-docs/releases/v1.13.1) diff --git a/charts/posit-chronicle/README.md.gotmpl b/charts/posit-chronicle/README.md.gotmpl index eedebb761..3083837f5 100644 --- a/charts/posit-chronicle/README.md.gotmpl +++ b/charts/posit-chronicle/README.md.gotmpl @@ -10,77 +10,110 @@ ## Usage -This chart deploys only the Chronicle server and is meant to be used in tandem -with the Workbench and Connect charts. To actually send data to the server, you -will need to run the Chronicle agent as a sidecar container on your -Workbench or Connect server pods by setting `pod.sidecar` in their respective `values.yaml` files - -Here is an example of Helm values to run the agent sidecar in **Workbench**, -where we set up a shared volume between containers for audit logs: +This chart deploys the Chronicle server and is intended to be used in tandem +with the Workbench and Connect charts. For the server to receive data, +the Chronicle agent must be deployed as a sidecar container alongside +Workbench or Connect server pods. + +Both [Workbench](https://docs.posit.co/helm/charts/rstudio-workbench/README.html#chronicle-agent) +(`>=0.9.2`) and [Connect](https://docs.posit.co/helm/charts/rstudio-connect/README.html#chronicle-agent) +(`>=0.7.26`) charts include out of the box support for Chronicle agent sidecars. +The sidecar can be enabled by setting the `chronicleAgent.enabled` value to `true` +in either product's chart. + +For additional information on deploying and configuring Chronicle agents, +see the [Workbench](https://docs.posit.co/helm/charts/rstudio-workbench/README.html#chronicle-agent) +or [Connect](https://docs.posit.co/helm/charts/rstudio-connect/README.html#chronicle-agent) +chart documentation. + +## HTTPS Configuration + +Chronicle can be configured to use HTTPS for secure communication. The +`config.HTTPS` section of the configuration allows you to specify the certificate +and key files to use for HTTPS. Both `config.HTTPS.Certificate` and +`config.HTTPS.Key` are expected to be paths to files accessible by Chronicle. +The `extraSecretMounts` value can be used to mount the certificate and key files +into the Chronicle pod. Here is an example of how to do this, assuming that +the certificate and key files are stored together in a Kubernetes TLS secret: ```yaml -pod: - # We will need to create a new volume to share audit logs between - # the rstudio (workbench) and chronicle-agent containers - volumes: - - name: logs - emptyDir: {} - volumeMounts: - - name: logs - mountPath: "/var/lib/rstudio-server/audit" - sidecar: - - name: chronicle-agent - image: ghcr.io/rstudio/chronicle-agent:2025.03.0 - volumeMounts: - - name: logs - mountPath: "/var/lib/rstudio-server/audit" - env: - - name: CHRONICLE_SERVER_ADDRESS - value: "http://chronicle-server.default" +extraSecretMounts: + - name: chronicle-https + mountPath: /etc/chronicle/ssl + secretName: chronicle-https + items: + - key: tls.crt + - key: tls.key +config: + HTTPS: + Enabled: true + Certificate: "/etc/chronicle/ssl/tls.crt" + Key: "/etc/chronicle/ssl/tls.key" ``` -And here is an example of Helm values for Connect, where a **Connect** -API key from a Kubernetes Secret is used to unlock more detailed metrics: +## Storage Configuration + +Chronicle can be configured to persist data to local storage, AWS S3, or both. + +### Local Storage + +The default configuration will save data to a persistent volume, which +is suitable if you'd like to access and analyze the data within your cluster. +The below values show the default configuration for storage: ```yaml -pod: - sidecar: - - name: chronicle-agent - image: ghcr.io/rstudio/chronicle-agent:2025.03.0 - env: - - name: CHRONICLE_SERVER_ADDRESS - value: "http://chronicle-server.default" - - name: CONNECT_API_KEY - valueFrom: - secretKeyRef: - name: connect - key: apikey +persistence: + enabled: true + accessModes: + - ReadWriteOnce + size: 10Gi +config: + LocalStorage: + Enabled: true + Location: "/opt/chronicle-data" ``` -Note that it is up to the user to provision this Kubernetes Secret for the -Connect API key. +The `persistence` section configures the persistent volume claim in the +cluster while the `config.LocalStorage` section directly applies to Chronicle's +configuration file. The persistent volume will always mount to the path specified +by `config.LocalStorage.Path` to avoid potential misconfiguration and data loss. -## Storage Configuration +By default, Chronicle requests 10Gi of storage. In most cases, this amount of +storage should be sufficient for thirty days of monitoring data. -Chronicle can be configured to persist data to a local Kubernetes Volume, AWS S3, or both. +::: {.callout-important} +Users are responsible for managing the size of the persistent volume, retention +of stored data, and controlling access to the data from other pods. Consider +utilizing a dynamic volume provisioner to avoid storage-related service +interruptions. +::: -The default configuration uses a local volume, which is suitable if you'd like to -access and analyze the data within your cluster: +While attaching the volume to Workbench is a valid method of accessing the data, +keep in mind that some data captured by Chronicle may be considered sensitive and +should be handled with care. + +#### Alternate Storage Class + +Depending on the environment or cloud hosting Chronicle, many CSI drivers may +be available to use as the persistent volume's storage class. While Chronicle +only natively supports local storage or S3, CSI drivers may be used to provide +support for other storage backends such as Azure Blob Storage, Azure Files, Google +Cloud Storage, or other object storage solutions. The storage class for persistent +volumes can be set with the following value: ```yaml -config: - LocalStorage: - Enabled: true - Location: "/chronicle-data" - RetentionPeriod: "30d" +persistence: + storageClass: "alternate-storage-class" ``` -`retentionPeriod` controls how long usage data are kept. For example, `"120m"` -for 120 minutes, `"36h"` for 36 hours, `14d` for two weeks, or `"0"` for unbounded retention. -(Units smaller than seconds or larger than days are not supported.) +Please report and performance or stability issues with alternate storage configurations +to the [issue tracker](https://github.com/rstudio/helm/issues/new?template=chronicle.md). -You can also persist data to AWS S3 instead of (or in addition to) local -storage: +### S3 Storage + +Chronicle can also be configured to store data in an S3 bucket. This can be +useful for controlling access to data or taking advantage of S3 features +such as lifecycle management. ```yaml config: @@ -90,13 +123,13 @@ config: Region: "us-east-2" ``` -### Using Iam for S3 +#### Using IAM roles for S3 access -If you are running on EKS, you can use [IAM Roles for Service +If Chronicle is running on EKS, [IAM Roles for Service Accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) -to manage the credentials needed to access S3. In this scenario, once you have [created an IAM -role](https://docs.aws.amazon.com/eks/latest/userguide/create-service-account-iam-policy-and-role.html), -you can use this role as an annotation on the existing Service Account: +can be utilized to manage the credentials needed to access S3. Once [an IAM role has been +created](https://docs.aws.amazon.com/eks/latest/userguide/create-service-account-iam-policy-and-role.html), +the role can be attached as an annotation on Chronicle's Service Account: ```yaml serviceaccount: @@ -105,8 +138,7 @@ serviceaccount: eks.amazonaws.com/role-arn: arn:aws:iam::123456789000:role/iam-role-name-here ``` -If you are unable to use IAM Roles for Service Accounts, there are any number of -alternatives for injecting AWS credentials into a container. As a fallback, +There are alternatives for injecting AWS credentials into a container. As a fallback, the S3 storage config allows specifying a profile: ```yaml @@ -118,8 +150,7 @@ config: Region: "us-east-2" ``` - -### Needed S3 Policy Permissions +#### Needed S3 Policy Permissions The credentials Chronicle uses for S3 storage must have the following permissions enabled: @@ -128,6 +159,15 @@ The credentials Chronicle uses for S3 storage must have the following permission - `s3:PutObject` - `s3:DeleteObject` +## Additional Configuration + +Chronicle has additional configuration options not specifically mentioned in this +README. For additional information on administrating or using Posit Chronicle, see +the [Chronicle documentation](https://docs.posit.co/chronicle/). + +For details on server configuration options, see the [advanced server configuration +reference page](https://docs.posit.co/chronicle/appendix/library/advanced-server.html). + {{ template "chart.valuesSection" . }} {{ template "helm-docs.versionFooter" . }} diff --git a/charts/posit-chronicle/lint/complex-values.yaml b/charts/posit-chronicle/lint/complex-values.yaml index 1ce6f22b3..cf2699b88 100644 --- a/charts/posit-chronicle/lint/complex-values.yaml +++ b/charts/posit-chronicle/lint/complex-values.yaml @@ -23,7 +23,7 @@ service: pod: replicas: 4 - command: "/bash" + command: ["/bash"] args: ["echo", "hello world"] # -- Optional environment variables env: diff --git a/charts/posit-chronicle/templates/NOTES.txt b/charts/posit-chronicle/templates/NOTES.txt new file mode 100644 index 000000000..53ec92a8e --- /dev/null +++ b/charts/posit-chronicle/templates/NOTES.txt @@ -0,0 +1,20 @@ +{{ include "posit-chronicle.fullname" . }} successfully deployed to namespace {{ .Release.Namespace }} + +Please visit https://docs.posit.co/chronicle/getting-started/installation/on-kubernetes.html#agent-sidecar for +additional information on deploying Chronicle agents to monitor Posit products in your cluster. +{{ if and (not .Values.config.LocalStorage.Enabled) .Values.persistence.enabled }} +WARNING: Persistence is enabled, but Chronicle local storage is not configured. This may lead to data loss if the pod +is restarted or rescheduled. +{{- end }} +{{ if and .Values.config.LocalStorage.Enabled (not .Values.persistence.enabled) }} +WARNING: Local storage is enabled, but persistence is not enabled. This may lead to data loss if the pod is restarted or +rescheduled. +{{- end }} +{{ if and (not .Values.config.LocalStorage.Enabled) (not .Values.config.S3Storage.Enabled) }} +WARNING: No storage backend is configured. Chronicle will not be able to store any data in a persistent or accessible +location. Consider redeploying with `.Values.config.LocalStorage` or `.Values.config.S3Storage` set to true. +{{- end }} +{{ if and .Values.config.LocalStorage.Enabled .Values.config.S3Storage.Enabled }} +WARNING: Both local and S3 storage are currently enabled. Data will be saved both locally and in S3 which could +result in inflated costs. It is recommended to only enable one storage backend. +{{- end }} diff --git a/charts/posit-chronicle/templates/_helpers.tpl b/charts/posit-chronicle/templates/_helpers.tpl index 2268c0692..c22349901 100644 --- a/charts/posit-chronicle/templates/_helpers.tpl +++ b/charts/posit-chronicle/templates/_helpers.tpl @@ -1,73 +1,98 @@ {{/* vim: set filetype=mustache: */}} {{/* -Generate annotations for various resources +Expand the chart name. */}} - -{{- define "posit-chronicle.pod.annotations" -}} -{{- range $key,$value := $.Values.pod.annotations -}} -{{ $key }}: {{ $value | quote }} -{{ end }} -{{- if .Values.config.Metrics.Enabled }} -prometheus.io/scrape: "true" -{{- if .Values.config.HTTPS.Enabled }} -prometheus.io/port: "443" -{{- else}} -prometheus.io/port: "5252" +{{- define "posit-chronicle.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "posit-chronicle.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 }} -{{- end -}} - -{{- define "posit-chronicle.serviceaccount.annotations" -}} -{{- range $key,$value := $.Values.serviceaccount.annotations -}} -{{ $key }}: {{ $value | quote }} -{{ end }} -{{- end -}} - -{{- define "posit-chronicle.service.annotations" -}} -{{- range $key,$value := $.Values.service.annotations -}} -{{ $key }}: {{ $value | quote }} -{{ end }} -{{- end -}} {{/* -Generate labels for various resources +Create chart name and version as used by the chart label. */}} +{{- define "posit-chronicle.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} -{{- define "posit-chronicle.pod.labels" -}} -{{- range $key,$value := $.Values.pod.labels -}} -{{ $key }}: {{ $value | quote }} -{{ end }} -{{- end -}} - -{{- define "posit-chronicle.serviceaccount.labels" -}} -{{- range $key,$value := $.Values.serviceaccount.labels -}} -{{ $key }}: {{ $value | quote }} -{{ end }} -{{- end -}} - -{{- define "posit-chronicle.service.labels" -}} -{{- range $key,$value := $.Values.service.labels -}} -{{ $key }}: {{ $value | quote }} -{{ end }} -{{- end -}} +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "posit-chronicle.namespace" -}} +{{- if .Values.namespaceOverride }} +{{- .Values.namespaceOverride }} +{{- else }} +{{- .Release.Namespace }} +{{- end }} +{{- end }} {{/* -Generate selector labels for various resources +Create the Service Account name */}} +{{- define "posit-chronicle.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "posit-chronicle.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} -{{- define "posit-chronicle.pod.selectorLabels" -}} -{{- range $key,$value := $.Values.pod.selectorLabels -}} -{{ $key }}: {{ $value | quote }} -{{ end }} -app: chronicle-server -{{- end -}} +{{/* +Selector labels +*/}} +{{- define "posit-chronicle.selectorLabels" -}} +app.kubernetes.io/name: {{ include "posit-chronicle.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} -{{- define "posit-chronicle.service.selectorLabels" -}} -{{- range $key,$value := $.Values.service.selectorLabels -}} -{{ $key }}: {{ $value | quote }} -{{ end }} -app: chronicle-server -{{- end -}} +{{/* +Common labels +*/}} +{{- define "posit-chronicle.labels" }} +helm.sh/chart: {{ include "posit-chronicle.chart" . }} +app.kubernetes.io/managed-by: {{ .Release.Service | quote }} +app.kubernetes.io/part-of: {{ .Chart.Name | quote }} +app.kubernetes.io/component: server +{{ include "posit-chronicle.selectorLabels" . }} +{{- if or .Chart.AppVersion .Values.image.tag }} +app.kubernetes.io/version: {{ mustRegexReplaceAllLiteral "@sha.*" .Values.image.tag "" | default .Chart.AppVersion | trunc 63 | trimSuffix "-" | quote }} +{{- end }} +{{- with .Values.commonLabels }} +{{ toYaml . }} +{{- end }} +{{- end }} +{{/* +Generate annotations for various resources +*/}} +{{- define "posit-chronicle.pod.annotations" }} +{{- $podAnnotations := merge .Values.pod.annotations .Values.commonAnnotations }} +{{- if .Values.config.Metrics.Enabled }} +{{- $_ := set $podAnnotations "prometheus.io/scrape" "true" }} +{{- if .Values.config.HTTPS.Enabled }} +{{- $_ := set $podAnnotations "prometheus.io/port" "443" }} +{{- else }} +{{- $_ := set $podAnnotations "prometheus.io/port" "5252" }} +{{- end }} +{{- end }} +{{- with $podAnnotations }} +{{- toYaml . }} +{{- end }} +{{- end }} diff --git a/charts/posit-chronicle/templates/configmap.yaml b/charts/posit-chronicle/templates/configmap.yaml index 97fed1357..1ef04c6fd 100644 --- a/charts/posit-chronicle/templates/configmap.yaml +++ b/charts/posit-chronicle/templates/configmap.yaml @@ -2,16 +2,21 @@ apiVersion: v1 kind: ConfigMap metadata: - name: chronicle-server-config + name: {{ include "posit-chronicle.fullname" . }} + namespace: {{ include "posit-chronicle.namespace" . }} + labels: + {{ include "posit-chronicle.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{ toYaml . | nindent 4 }} + {{- end }} data: - server: | - - # switch between http and https + posit-chronicle.gcfg: | {{- if .Values.config.HTTPS.Enabled }} [HTTPS] Listen = :443 - Key = {{ .Values.config.HTTPS.Key }} - Certificate = {{ .Values.config.HTTPS.Certificate }} + Certificate = {{ required ".Values.config.HTTPS.Certificate must be specified when .Values.config.HTTPS.Enabled is true." .Values.config.HTTPS.Certificate }} + Key = {{ required ".Values.config.HTTPS.Key must be specified when .Values.config.HTTPS.Enabled is true." .Values.config.HTTPS.Key }} {{- else}} [HTTP] Listen = :5252 @@ -27,18 +32,28 @@ data: [Profiling] Enabled = {{ .Values.config.Profiling.Enabled }} - Listen = :3030 + {{- if .Values.config.Profiling.Enabled }} + Listen = :{{ .Values.config.Profiling.Port }} + {{- end }} [LocalStorage] Enabled = {{ .Values.config.LocalStorage.Enabled }} - Location = {{ .Values.config.LocalStorage.Location }} - RetentionPeriod = {{ .Values.config.LocalStorage.RetentionPeriod }} + {{- if .Values.config.LocalStorage.Enabled }} + Location = {{ .Values.config.LocalStorage.Path }} + {{- end }} [S3Storage] Enabled = {{ .Values.config.S3Storage.Enabled }} - Bucket = {{ .Values.config.S3Storage.Bucket }} + {{- if .Values.config.S3Storage.Enabled }} + Bucket = {{ required "A .Values.config.S3Storage.Bucket must be specified when S3 storage is enabled." .Values.config.S3Storage.Bucket }} + {{- if .Values.config.S3Storage.Prefix }} Prefix = {{ .Values.config.S3Storage.Prefix }} + {{- end }} + {{- if .Values.config.S3Storage.Profile }} Profile = {{ .Values.config.S3Storage.Profile }} + {{- end }} + {{- if .Values.config.S3Storage.Region }} Region = {{ .Values.config.S3Storage.Region }} + {{- end }} + {{- end }} --- - diff --git a/charts/posit-chronicle/templates/extra-manifests.yaml b/charts/posit-chronicle/templates/extra-manifests.yaml new file mode 100644 index 000000000..aed6ba3cb --- /dev/null +++ b/charts/posit-chronicle/templates/extra-manifests.yaml @@ -0,0 +1,8 @@ +{{ range .Values.extraObjects }} +--- +{{- if typeIs "string" . }} +{{ tpl . $ }} +{{ else }} +{{ tpl (. | toYaml) $ }} +{{- end }} +{{- end }} diff --git a/charts/posit-chronicle/templates/serviceaccount.yaml b/charts/posit-chronicle/templates/serviceaccount.yaml index d7951c4bd..7530652ac 100644 --- a/charts/posit-chronicle/templates/serviceaccount.yaml +++ b/charts/posit-chronicle/templates/serviceaccount.yaml @@ -1,10 +1,19 @@ -{{- if .Values.serviceaccount.create -}} +{{- if .Values.serviceAccount.create }} apiVersion: v1 kind: ServiceAccount metadata: - name: chronicle-sa + name: {{ include "posit-chronicle.serviceAccountName" . }} + namespace: {{ include "posit-chronicle.namespace" . }} labels: - {{ include "posit-chronicle.serviceaccount.labels" . | nindent 4 }} + {{ include "posit-chronicle.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.labels }} + {{ toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.serviceAccount.annotations .Values.commonAnnotations }} + {{- $annotations := merge .Values.serviceAccount.annotations .Values.commonAnnotations }} annotations: - {{ include "posit-chronicle.serviceaccount.annotations" . | nindent 4 }} -{{- end -}} + {{- with $annotations }} + {{ toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/posit-chronicle/templates/stateful-set.yaml b/charts/posit-chronicle/templates/stateful-set.yaml index 1ee662ddc..91ea366e5 100644 --- a/charts/posit-chronicle/templates/stateful-set.yaml +++ b/charts/posit-chronicle/templates/stateful-set.yaml @@ -1,47 +1,65 @@ --- +{{- $root := . }} apiVersion: apps/v1 kind: StatefulSet metadata: - name: chronicle-server - namespace: {{ $.Release.Namespace }} + name: {{ include "posit-chronicle.fullname" . }} + namespace: {{ include "posit-chronicle.namespace" . }} + labels: + {{- include "posit-chronicle.labels" . | nindent 4 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} spec: - serviceName: chronicle-server replicas: {{ .Values.replicas }} + serviceName: {{ include "posit-chronicle.fullname" . }} selector: matchLabels: - {{- include "posit-chronicle.pod.selectorLabels" . | trim | nindent 6 }} + {{- include "posit-chronicle.selectorLabels" . | trim | nindent 6 }} template: metadata: labels: - {{- include "posit-chronicle.pod.labels" . | trim | nindent 8 }} - {{- include "posit-chronicle.pod.selectorLabels" . | trim | nindent 8 }} + {{- include "posit-chronicle.labels" . | trim | nindent 8 }} + {{- with .Values.pod.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} annotations: - {{- include "posit-chronicle.pod.annotations" . | trim | nindent 8 }} + checksum/config: {{ print .Values.config | sha256sum }} + {{- include "posit-chronicle.pod.annotations" . | trim | nindent 8 }} spec: {{- with .Values.pod.affinity }} affinity: - {{- toYaml . | nindent 8 }} + {{- tpl (toYaml .) $root | nindent 8 }} {{- end }} {{- with .Values.pod.nodeSelector }} nodeSelector: - {{- toYaml . | nindent 8 }} + {{- tpl (toYaml .) $root | nindent 8 }} {{- end }} {{- with .Values.pod.tolerations }} tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- if .Values.serviceaccount.create }} - serviceAccountName: chronicle-sa + {{- tpl (toYaml .) $root | nindent 6 }} {{- end }} + serviceAccountName: {{ include "posit-chronicle.serviceAccountName" . }} containers: - - name: chronicle-server - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} - imagePullPolicy: {{ .Values.image.imagePullPolicy }} + - name: {{ .Chart.Name }} + {{- if .Values.image.sha }} + image: "{{ required "An image registry must be specified" .Values.image.registry }}/{{ required "An image repository must be specified" .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }}@sha256:{{ .Values.image.sha }}" + {{- else }} + image: "{{ required "An image registry must be specified" .Values.image.registry }}/{{ required "An image repository must be specified" .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }}" + {{- end}} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.pod.command }} command: - - {{ .Values.pod.command }} - {{- if .Values.pod.args }} + {{- tpl (toYaml .) $root | nindent 8 }} + {{- end }} + {{- with .Values.pod.args }} args: - {{- toYaml .Values.pod.args | nindent 8 }} + {{- tpl (toYaml .) $root | nindent 8 }} + {{- end }} + {{- with .Values.image.securityContext }} + securityContext: + {{- toYaml . | nindent 10 }} {{- end }} ports: {{- if .Values.config.HTTPS.Enabled }} @@ -52,30 +70,87 @@ spec: name: http {{- end }} volumeMounts: - {{- if .Values.config.LocalStorage.Enabled }} - - name: data - mountPath: {{ .Values.config.LocalStorage.Location }} + {{- if .Values.persistence.enabled }} + - name: {{ include "posit-chronicle.fullname" . }} + mountPath: {{ .Values.config.LocalStorage.Path }} {{- end }} - - name: chronicle-server-config + - name: config mountPath: /etc/posit-chronicle/posit-chronicle.gcfg - subPath: server - {{- if .Values.pod.env }} + subPath: posit-chronicle.gcfg + {{- range .Values.extraSecretMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- with .Values.pod.env }} env: - {{- toYaml .Values.pod.env | nindent 8 }} + {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.pod.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} terminationGracePeriodSeconds: {{ .Values.pod.terminationGracePeriodSeconds }} volumes: - - name: chronicle-server-config + - name: config configMap: - name: chronicle-server-config -{{- if .Values.config.LocalStorage.Enabled }} + name: {{ include "posit-chronicle.fullname" . }} + items: + - key: posit-chronicle.gcfg + path: "posit-chronicle.gcfg" + {{- range .Values.extraSecretMounts }} + {{- if .secretName }} + - name: {{ .name }} + secret: + secretName: {{ .secretName }} + {{- with .defaultMode }} + defaultMode: {{ . }} + {{- end }} + {{- with .optional }} + optional: {{ . }} + {{- end }} + {{- with .items }} + items: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- else if .projected }} + - name: {{ .name }} + projected: + {{- toYaml .projected | nindent 8 }} + {{- else if .csi }} + - name: {{ .name }} + csi: + {{- toYaml .csi | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.persistence.enabled }} volumeClaimTemplates: - - metadata: - name: data + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: {{ include "posit-chronicle.fullname" . }} + namespace: {{ include "posit-chronicle.namespace" . }} + labels: + {{- include "posit-chronicle.labels" . | nindent 8 }} + {{- with .Values.commonAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} spec: - accessModes: [ "ReadWriteOnce" ] + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} resources: requests: - storage: {{ .Values.storage.persistentVolumeSize }} -{{- end }} + storage: {{ .Values.persistence.size | quote }} + {{- with .Values.persistence.storageClassName }} + storageClassName: {{ . }} + {{- end }} + {{- with .Values.persistence.selectorLabels }} + selector: + matchLabels: + {{ toYaml . | nindent 10 }} + {{- end }} + {{- end }} --- diff --git a/charts/posit-chronicle/templates/svc.yaml b/charts/posit-chronicle/templates/svc.yaml index d5c5eb064..cf6779499 100644 --- a/charts/posit-chronicle/templates/svc.yaml +++ b/charts/posit-chronicle/templates/svc.yaml @@ -2,21 +2,29 @@ apiVersion: v1 kind: Service metadata: - name: chronicle-server + name: {{ include "posit-chronicle.fullname" . }} + namespace: {{ include "posit-chronicle.namespace" . }} labels: - {{- include "posit-chronicle.service.labels" . | trim | nindent 4 }} - {{- include "posit-chronicle.service.selectorLabels" . | trim | nindent 4 }} + {{ include "posit-chronicle.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.service.annotations .Values.commonAnnotations }} + {{- $annotations := merge .Values.service.annotations .Values.commonAnnotations }} annotations: - {{- include "posit-chronicle.service.annotations" . | trim | nindent 4 }} + {{- with $annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} spec: selector: - {{- include "posit-chronicle.service.selectorLabels" . | trim | nindent 4 }} + {{ include "posit-chronicle.selectorLabels" . | trim | nindent 4 }} ports: {{- if .Values.config.HTTPS.Enabled }} - port: {{ .Values.service.port }} targetPort: 443 name: https - {{- else}} + {{- else }} - port: {{ .Values.service.port }} targetPort: 5252 name: http diff --git a/charts/posit-chronicle/tests/configmap_fail_test.yaml b/charts/posit-chronicle/tests/configmap_fail_test.yaml new file mode 100644 index 000000000..402dc91f3 --- /dev/null +++ b/charts/posit-chronicle/tests/configmap_fail_test.yaml @@ -0,0 +1,46 @@ +suite: Configmap tests +templates: + - configmap.yaml +tests: + - it: should fail if https is enabled but no certificate is specified + set: + config: + HTTPS: + Enabled: true + Key: /etc/ssl/ssl.key + asserts: + - failedTemplate: + errorPattern: ".*Certificate must be specified.*" + - it: should fail if https is enabled but no key is specified + set: + config: + HTTPS: + Enabled: true + Certificate: /etc/ssl/ssl.crt + asserts: + - failedTemplate: + errorPattern: ".*Key must be specified.*" + - it: should fail for invalid log level values + set: + config: + Logging: + ServiceLogLevel: INVALID + asserts: + - failedTemplate: + errorPattern: ".*ServiceLogLevel: Does not match pattern.*" + - it: should fail for invalid log level values + set: + config: + Logging: + ServiceLogFormat: INVALID + asserts: + - failedTemplate: + errorPattern: ".*ServiceLogFormat: Does not match pattern.*" + - it: should fail if S3 is enabled but no bucket is specified + set: + config: + S3Storage: + Enabled: true + asserts: + - failedTemplate: + errorPattern: ".*Bucket must be specified when S3 storage is enabled.*" diff --git a/charts/posit-chronicle/tests/configmap_test.yaml b/charts/posit-chronicle/tests/configmap_test.yaml new file mode 100644 index 000000000..3cdf97413 --- /dev/null +++ b/charts/posit-chronicle/tests/configmap_test.yaml @@ -0,0 +1,183 @@ +suite: Configmap tests +templates: + - configmap.yaml +tests: + - it: should always create a config map + asserts: + - hasDocuments: + count: 1 + - isKind: + of: ConfigMap + - it: should use http by default + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[HTTP\] + Listen = :5252 + - it: should properly configure https when enabled + set: + config: + HTTPS: + Enabled: true + Certificate: /etc/ssl/ssl.crt + Key: /etc/ssl/ssl.key + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[HTTPS\] + Listen = :443 + Certificate = \/etc\/ssl\/ssl.crt + Key = \/etc\/ssl\/ssl.key + - it: should set a default logging configuration + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[Logging\] + ServiceLog = STDOUT + ServiceLogLevel = INFO + ServiceLogFormat = TEXT + - it: should set values for a custom logging configuration + set: + config: + Logging: + ServiceLog: STDERR + ServiceLogLevel: DEBUG + ServiceLogFormat: JSON + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[Logging\] + ServiceLog = STDERR + ServiceLogLevel = DEBUG + ServiceLogFormat = JSON + - it: should disable metrics by default + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[Metrics\] + Enabled = false + - it: should enable metrics when specified + set: + config: + Metrics: + Enabled: true + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[Metrics\] + Enabled = true + - it: should disable profiling by default + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[Profiling\] + Enabled = false + - it: should enable profiling when specified + set: + config: + Profiling: + Enabled: true + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[Profiling\] + Enabled = true + Listen = :3030 + - it: should set the profiling listening port when specified + set: + config: + Profiling: + Enabled: true + Port: 3131 + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[Profiling\] + Enabled = true + Listen = :3131 + - it: should enable and configure local storage by default + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[LocalStorage\] + Enabled = true + Location = \/opt\/chronicle-data + - it: should set values for a custom local storage configuration + set: + config: + LocalStorage: + Path: /custom/data/path + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[LocalStorage\] + Enabled = true + Location = \/custom\/data\/path + - it: should disable local storage when specified + set: + config: + LocalStorage: + Enabled: false + # One of these must be set to true + S3Storage: + Enabled: true + Bucket: test + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[LocalStorage\] + Enabled = false + - notMatchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + Location = \/opt\/chronicle-data + - it: should enable and configure S3 storage when specified + set: + config: + S3Storage: + Enabled: true + Bucket: test + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[S3Storage\] + Enabled = true + Bucket = test + - notMatchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + Prefix = .* + Profile = .* + Region = .* + - it: should add extra options to S3 when specified + set: + config: + S3Storage: + Enabled: true + Bucket: test + Prefix: test-prefix + Profile: test-profile + Region: test-region + asserts: + - matchRegex: + path: data["posit-chronicle.gcfg"] + pattern: | + \[S3Storage\] + Enabled = true + Bucket = test + Prefix = test-prefix + Profile = test-profile + Region = test-region diff --git a/charts/posit-chronicle/tests/extra-manifests_test.yaml b/charts/posit-chronicle/tests/extra-manifests_test.yaml new file mode 100644 index 000000000..8f2438274 --- /dev/null +++ b/charts/posit-chronicle/tests/extra-manifests_test.yaml @@ -0,0 +1,28 @@ +suite: Extra manifests tests +templates: + - extra-manifests.yaml +tests: + - it: should create extra manifests if specified + set: + extraObjects: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: test-configmap + data: + test-key: test-value + - apiVersion: v1 + kind: Secret + metadata: + name: test-secret + data: + test-key: dGVzdC12YWx1ZQ== + asserts: + - hasDocuments: + count: 2 + - isKind: + of: ConfigMap + documentIndex: 0 + - isKind: + of: Secret + documentIndex: 1 diff --git a/charts/posit-chronicle/tests/metadata_test.yaml b/charts/posit-chronicle/tests/metadata_test.yaml new file mode 100644 index 000000000..bdb5639b1 --- /dev/null +++ b/charts/posit-chronicle/tests/metadata_test.yaml @@ -0,0 +1,80 @@ +suite: Generic metadata tests +excludeTemplates: + - extra-manifests.yaml + - NOTES.txt +set: + serviceAccount: + create: true +# Unittest cannot use templating for chart values so we need this to be set to a fixed value for testing +chart: + version: 9.9.9+test + appVersion: 9999.9.9 +release: + name: test-release + namespace: test-namespace +tests: + - it: should always set the default resource name to the posit-chronicle.fullname + asserts: + - equal: + path: metadata.name + value: test-release-posit-chronicle + - it: should use an override for the resource name if specified + set: + fullnameOverride: custom-name + asserts: + - equal: + path: metadata.name + value: custom-name + - it: should set the default resource namespace to the release namespace + asserts: + - equal: + path: metadata.namespace + value: test-namespace + - it: should use an override for the resource namespace if specified + set: + namespaceOverride: custom-namespace + asserts: + - equal: + path: metadata.namespace + value: custom-namespace + - it: should set the default resource labels + asserts: + - isSubset: + path: metadata.labels + content: + helm.sh/chart: posit-chronicle-9.9.9_test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: posit-chronicle + app.kubernetes.io/component: server + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/instance: test-release + app.kubernetes.io/version: 9999.9.9 + - it: should apply custom labels if specified + set: + commonLabels: + another: label + asserts: + - isSubset: + path: metadata.labels + content: + helm.sh/chart: posit-chronicle-9.9.9_test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: posit-chronicle + app.kubernetes.io/component: server + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/instance: test-release + app.kubernetes.io/version: 9999.9.9 + another: label + - it: should skip annotations by default + asserts: + - notExists: + path: metadata.annotations + - it: should apply custom annotations if specified + set: + commonAnnotations: + another: annotation + asserts: + - isSubset: + path: metadata.annotations + content: + another: annotation diff --git a/charts/posit-chronicle/tests/serviceaccount_test.yaml b/charts/posit-chronicle/tests/serviceaccount_test.yaml new file mode 100644 index 000000000..d9209fc69 --- /dev/null +++ b/charts/posit-chronicle/tests/serviceaccount_test.yaml @@ -0,0 +1,41 @@ +suite: Service account tests +templates: + - serviceaccount.yaml +tests: + - it: should skip creation by default + asserts: + - hasDocuments: + count: 0 + - it: should create a service account when specified + set: + serviceAccount: + create: true + release: + name: test-release + namespace: test-namespace + asserts: + - isKind: + of: ServiceAccount + - equal: + path: metadata.name + value: test-release-posit-chronicle + - equal: + path: metadata.namespace + value: test-namespace + - it: should set annotations if given with service account annotations favored during merge + set: + serviceAccount: + create: true + annotations: + test-merge: value2 + test-sa: value + commonAnnotations: + test-merge: value1 + test-common: value + asserts: + - isSubset: + path: metadata.annotations + content: + test-merge: value2 + test-common: value + test-sa: value diff --git a/charts/posit-chronicle/tests/statefulset_test.yaml b/charts/posit-chronicle/tests/statefulset_test.yaml new file mode 100644 index 000000000..3f1110c7e --- /dev/null +++ b/charts/posit-chronicle/tests/statefulset_test.yaml @@ -0,0 +1,371 @@ +suite: Stateful set tests +templates: + - stateful-set.yaml +# Unittest cannot use templating for chart values so we need this to be set to a fixed value for testing +chart: + version: 9.9.9+test + appVersion: 9999.9.9 +release: + name: test-release + namespace: test-namespace +tests: + - it: should create a statefulset with the correct name and namespace + release: + name: test-release + namespace: test-namespace + asserts: + - isKind: + of: StatefulSet + - equal: + path: metadata.name + value: test-release-posit-chronicle + - equal: + path: metadata.namespace + value: test-namespace + - equal: + path: spec.serviceName + value: test-release-posit-chronicle + - equal: + path: spec.replicas + value: 1 + - it: should apply common selector labels + asserts: + - isSubset: + path: spec.selector.matchLabels + content: + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/instance: test-release + - it: should apply common pod labels + set: + commonLabels: + another: label + asserts: + - isSubset: + path: spec.template.metadata.labels + content: + helm.sh/chart: posit-chronicle-9.9.9_test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: posit-chronicle + app.kubernetes.io/component: server + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/instance: test-release + app.kubernetes.io/version: 9999.9.9 + another: label + - it: should apply custom pod labels if specified + set: + pod: + labels: + another: label + asserts: + - isSubset: + path: spec.template.metadata.labels + content: + helm.sh/chart: posit-chronicle-9.9.9_test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: posit-chronicle + app.kubernetes.io/component: server + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/instance: test-release + app.kubernetes.io/version: 9999.9.9 + another: label + - it: should set checksum annotation by default for pods to ensure changes apply + asserts: + - exists: + path: spec.template.metadata.annotations.checksum/config + - it: should set the annotations if specified with pod annotations favored during merge + set: + commonAnnotations: + test-merge: value1 + test-common: value + pod: + annotations: + test-merge: value2 + test-pod: value + asserts: + - isSubset: + path: spec.template.metadata.annotations + content: + test-merge: value2 + test-common: value + test-pod: value + - it: should set prometheus pod annotations when metrics are enabled + set: + config: + Metrics: + Enabled: true + asserts: + - isSubset: + path: spec.template.metadata.annotations + content: + prometheus.io/scrape: "true" + prometheus.io/port: "5252" + - it: should set the pod affinity when specified + set: + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: disktype + operator: In + values: + - ssd + asserts: + - isSubset: + path: spec.template.spec.affinity + content: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: disktype + operator: In + values: + - ssd + - it: should set the pod nodeSelector when specified + set: + pod: + nodeSelector: + disktype: ssd + asserts: + - isSubset: + path: spec.template.spec.nodeSelector + content: + disktype: ssd + - it: should set the pod tolerations when specified + set: + pod: + tolerations: + - key: "key" + operator: "Equal" + value: "value" + effect: "NoSchedule" + asserts: + - contains: + path: spec.template.spec.tolerations + content: + key: "key" + operator: "Equal" + value: "value" + effect: "NoSchedule" + - it: should set the pod serviceAccountName to default when the service account creation is disabled + set: + serviceAccount: + create: false + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: default + - it: should set the pod serviceAccountName to the service account if service account creation is enabled + set: + serviceAccount: + create: true + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: test-release-posit-chronicle + - it: should generate a default chronicle server container + asserts: + - contains: + path: spec.template.spec.containers + content: + name: posit-chronicle + image: "ghcr.io/rstudio/chronicle:9999.9.9" + imagePullPolicy: IfNotPresent + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + ports: + - containerPort: 5252 + name: http + volumeMounts: + - name: test-release-posit-chronicle + mountPath: /opt/chronicle-data + - name: config + mountPath: /etc/posit-chronicle/posit-chronicle.gcfg + subPath: posit-chronicle.gcfg + - it: should set an overridden image if given + set: + image: + registry: docker.io + repository: rstudio/posit-chronicle + tag: 2025.03.0 + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "docker.io/rstudio/posit-chronicle:2025.03.0" + - it: should set an image digest sha if provided on the image + set: + image: + sha: 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "ghcr.io/rstudio/chronicle:9999.9.9@sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + - it: should set the image pull policy to Always if given + set: + image: + pullPolicy: Always + asserts: + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: Always + - it: should set an alternate command and args if given + set: + pod: + command: + - /bin/bash + args: + - -c + - echo "Hello world" + asserts: + - equal: + path: spec.template.spec.containers[0].command + value: ["/bin/bash"] + - equal: + path: spec.template.spec.containers[0].args + value: ["-c", "echo \"Hello world\""] + - it: should set additional container-level securityContext options if specified + set: + image: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + asserts: + - isSubset: + path: spec.template.spec.containers[0].securityContext + content: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + - it: should open https port on container if enabled + set: + config: + HTTPS: + Enabled: true + asserts: + - equal: + path: spec.template.spec.containers[0].ports + value: + - containerPort: 443 + name: https + - it: should set a pod-level securityContext by default that sets permissions for volumes + asserts: + - isSubset: + path: spec.template.spec.securityContext + content: + fsGroup: 1000 + fsGroupChangePolicy: "OnRootMismatch" + - it: should set additional pod-level securityContext options if specified + set: + pod: + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + asserts: + - isSubset: + path: spec.template.spec.securityContext + content: + fsGroup: 1000 + fsGroupChangePolicy: "OnRootMismatch" + runAsUser: 1001 + runAsGroup: 1001 + - it: should not mount a data volume if persistence is disabled + set: + persistence: + enabled: false + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: config + mountPath: /etc/posit-chronicle/posit-chronicle.gcfg + subPath: posit-chronicle.gcfg + - notContains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: test-release-posit-chronicle + mountPath: /opt/chronicle-data + - it: should mount extra secrets to container if specified + set: + extraSecretMounts: + - name: test-ssl-secret + secretName: chronicle-ssl + mountPath: /etc/ssl + readOnly: true + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: test-ssl-secret + mountPath: /etc/ssl + readOnly: true + - contains: + path: spec.template.spec.volumes + content: + name: test-ssl-secret + secret: + secretName: chronicle-ssl + - it: should define a volume for the config map + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: config + configMap: + name: test-release-posit-chronicle + items: + - key: posit-chronicle.gcfg + path: "posit-chronicle.gcfg" + - it: should define a volume claim template when persistence is enabled + asserts: + - contains: + path: spec.volumeClaimTemplates + content: + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: test-release-posit-chronicle + namespace: test-namespace + labels: + app.kubernetes.io/component: server + app.kubernetes.io/instance: test-release + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/part-of: posit-chronicle + app.kubernetes.io/version: 9999.9.9 + helm.sh/chart: posit-chronicle-9.9.9_test + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + - it: should define no volume claim templates if persistence is disabled + set: + persistence: + enabled: false + asserts: + - notExists: + path: spec.volumeClaimTemplates + - it: should set a storage class if specified + set: + persistence: + storageClassName: my-storage-class + asserts: + - equal: + path: spec.volumeClaimTemplates[0].spec.storageClassName + value: my-storage-class + - it: should set volume claim template selector labels if specified + set: + persistence: + selectorLabels: + app: chronicle + asserts: + - equal: + path: spec.volumeClaimTemplates[0].spec.selector.matchLabels + value: + app: chronicle + diff --git a/charts/posit-chronicle/tests/svc_test.yaml b/charts/posit-chronicle/tests/svc_test.yaml new file mode 100644 index 000000000..ba0007043 --- /dev/null +++ b/charts/posit-chronicle/tests/svc_test.yaml @@ -0,0 +1,145 @@ +suite: Service tests +templates: + - svc.yaml +tests: + - it: should always create a service + asserts: + - hasDocuments: + count: 1 + - isKind: + of: Service + - it: should apply service labels if given + set: + service: + labels: + test: label + asserts: + - isSubset: + path: metadata.labels + content: + test: label + - it: should have no annotations by default + asserts: + - notExists: + path: metadata.annotations + - it: should set annotations if specified with service annotations favored during merge + set: + service: + annotations: + test-merge: value2 + test-svc: value + commonAnnotations: + test-merge: value1 + test-common: value + asserts: + - isSubset: + path: metadata.annotations + content: + test-merge: value2 + test-common: value + test-svc: value + - it: should create a service targeting http by default + release: + name: test-release + namespace: test-namespace + asserts: + - isKind: + of: Service + - equal: + path: metadata.name + value: test-release-posit-chronicle + - equal: + path: metadata.namespace + value: test-namespace + - isSubset: + path: spec.selector + content: + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/instance: test-release + - contains: + path: spec.ports + content: + name: http + port: 80 + targetPort: 5252 + - notContains: + path: spec.ports + content: + name: https + port: 80 + targetPort: 443 + - it: should create a service targeting https when enabled + release: + name: test-release + namespace: test-namespace + set: + config: + HTTPS: + Enabled: true + Certificate: test-cert + Key: test-key + asserts: + - isKind: + of: Service + - equal: + path: metadata.name + value: test-release-posit-chronicle + - equal: + path: metadata.namespace + value: test-namespace + - isSubset: + path: spec.selector + content: + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/instance: test-release + - contains: + path: spec.ports + content: + name: https + port: 80 + targetPort: 443 + - notContains: + path: spec.ports + content: + name: http + port: 80 + targetPort: 5252 + - it: should use an alternate port when specified by user + release: + name: test-release + namespace: test-namespace + set: + service: + port: 8787 + asserts: + - isKind: + of: Service + - equal: + path: metadata.name + value: test-release-posit-chronicle + - equal: + path: metadata.namespace + value: test-namespace + - isSubset: + path: spec.selector + content: + app.kubernetes.io/name: posit-chronicle + app.kubernetes.io/instance: test-release + - contains: + path: spec.ports + content: + name: http + port: 8787 + targetPort: 5252 + - notContains: + path: spec.ports + content: + name: http + port: 80 + targetPort: 5252 + - notContains: + path: spec.ports + content: + name: https + port: 80 + targetPort: 443 diff --git a/charts/posit-chronicle/values.schema.json b/charts/posit-chronicle/values.schema.json new file mode 100644 index 000000000..bc7813047 --- /dev/null +++ b/charts/posit-chronicle/values.schema.json @@ -0,0 +1,438 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "properties": { + "nameOverride": { + "description": "Overrides the name of the chart", + "type": "string" + }, + "fullnameOverride": { + "description": "Overrides the full name of the release", + "type": "string" + }, + "namespaceOverride": { + "description": "Overrides the namespace used for the release", + "type": "string" + }, + "commonLabels": { + "description": "Labels to be added to all resources", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "commonAnnotations": { + "description": "Annotations to be added to all resources", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "extraObjects": { + "description": "Additional Kubernetes objects to be created", + "type": "array", + "items": { + "type": "object" + } + }, + "image": { + "description": "Container image specification", + "type": "object", + "properties": { + "registry": { + "description": "Container image registry", + "type": "string", + "default": "ghcr.io" + }, + "repository": { + "description": "Container image repository", + "type": "string", + "default": "rstudio/chronicle" + }, + "tag": { + "description": "Container image tag (defaults to .Chart.AppVersion)", + "type": "string" + }, + "sha": { + "description": "Container image digest", + "type": "string", + "pattern": "^$|^[a-fA-F0-9]{64}$" + }, + "pullPolicy": { + "description": "Container image pull policy", + "type": "string" + }, + "securityContext": { + "description": "Security context to apply at the container-level", + "type": "object" + } + }, + "required": [ + "repository", + "registry", + "tag", + "pullPolicy" + ] + }, + "serviceAccount": { + "description": "Service account configuration", + "type": "object", + "properties": { + "create": { + "description": "Boolean flag to create a service account for the chart", + "type": "boolean" + }, + "name": { + "description": "Name of the service account to use, defaults to fullname if blank", + "type": "string" + }, + "annotations": { + "description": "Annotations to add to the service account", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "description": "Labels to add to the service account", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "service": { + "description": "Service configuration", + "type": "object", + "properties": { + "port": { + "description": "Port to expose the service on", + "type": "integer", + "default": 80, + "minimum": 1, + "maximum": 65535 + }, + "annotations": { + "description": "Annotations to add to the service", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "description": "Labels to add to the service", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "replicas": { + "description": "Number of replicas for the deployment", + "type": "integer", + "default": 1, + "minimum": 1 + }, + "pod": { + "description": "Pod configuration", + "type": "object", + "properties": { + "command": { + "description": "Command to run in the container", + "type": "array", + "items": { + "type": "string" + } + }, + "args": { + "description": "Arguments to pass to the command", + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "description": "Environment variables to set in the container", + "type": "array" + }, + "annotations": { + "description": "Annotations to add to the pod", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "description": "Labels to add to the pod", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "affinity": { + "description": "Affinity rules for the pod", + "type": "object" + }, + "nodeSelector": { + "description": "Node selector for the pod", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "tolerations": { + "description": "Tolerations for the pod", + "type": "array", + "items": { + "type": "object" + } + }, + "terminationGracePeriodSeconds": { + "description": "Termination grace period for the pod", + "type": "integer", + "default": 30, + "minimum": 0 + }, + "securityContext": { + "description": "Security context to apply at the pod-level", + "type": "object" + } + } + }, + "persistence": { + "description": "Persistence configuration", + "type": "object", + "properties": { + "enabled": { + "description": "Enable persistent storage", + "type": "boolean" + }, + "size": { + "description": "Size of the persistent volume claim", + "type": "string" + }, + "storageClass": { + "description": "Storage class for the persistent volume claim", + "type": "string" + }, + "accessModes": { + "description": "Access modes for the persistent volume claim", + "type": "array", + "items": { + "type": "string" + } + }, + "selectorLabels": { + "description": "Labels to select the persistent volume", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "annotations": { + "description": "Annotations to add to the persistent volume claim", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "labels": { + "description": "Labels to add to the persistent volume claim", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "finalizers": { + "description": "Finalizers to add to the persistent volume claim", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "extraSecretMounts": { + "description": "Additional secret mounts for the pod", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Name of the secret", + "type": "string" + }, + "mountPath": { + "description": "Path to mount the secret at", + "type": "string" + }, + "readOnly": { + "description": "Boolean flag to make the mount read-only", + "type": "boolean", + "default": true + } + }, + "additionalProperties": true, + "required": [ + "name", + "mountPath" + ] + } + }, + "config": { + "description": "Chronicle server configuration", + "type": "object", + "properties": { + "HTTPS": { + "description": "Configuration for using HTTPS", + "type": "object", + "properties": { + "Enabled": { + "description": "Enable HTTPS", + "type": "boolean" + }, + "Certificate": { + "description": "Path to the certificate file", + "type": "string" + }, + "Key": { + "description": "Path to the key file", + "type": "string" + } + } + }, + "Metrics": { + "description": "Configuration for Prometheus metrics", + "type": "object", + "properties": { + "Enabled": { + "description": "Enable metrics", + "type": "boolean" + } + } + }, + "Profiling": { + "description": "Configuration for profiling server", + "type": "object", + "properties": { + "Enabled": { + "description": "Enable profiling", + "type": "boolean" + }, + "Port": { + "description": "Port for profiling server", + "type": "integer", + "default": 3030, + "minimum": 1, + "maximum": 65535 + } + } + }, + "Logging": { + "description": "Configuration for logging", + "type": "object", + "properties": { + "ServiceLog": { + "description": "Logging output destination", + "type": "string" + }, + "ServiceLogLevel": { + "description": "Logging level", + "type": "string", + "pattern": "(?i)^(trace|debug|info|warn|error)$" + }, + "ServiceLogFormat": { + "description": "Logging format", + "type": "string", + "pattern": "(?i)^(json|text)$" + } + } + }, + "LocalStorage": { + "description": "Configuration for local storage", + "type": "object", + "properties": { + "enabled": { + "description": "Enable local storage", + "type": "boolean", + "default": true + }, + "path": { + "description": "Path to the local storage directory", + "type": "string", + "default": "/opt/chronicle-data" + } + } + }, + "S3Storage": { + "description": "Configuration for storage", + "type": "object", + "properties": { + "Enabled": { + "description": "Enable S3 storage", + "type": "boolean", + "default": false + }, + "Bucket": { + "description": "S3 bucket name", + "type": "string" + }, + "Prefix": { + "description": "S3 bucket path prefix for storing data", + "type": "string" + }, + "Profile": { + "description": "S3 secret access key", + "type": "string" + }, + "Region": { + "description": "S3 region", + "type": "string" + }, + "if": { + "properties": { + "enabled": { + "const": true + } + } + }, + "then": { + "properties": { + "bucket": { + "minLength": 3 + } + } + } + } + } + }, + "anyOf": [ + { + "properties": { + "LocalStorage": { + "properties": { + "enabled": { + "const": true + } + } + } + } + }, + { + "properties": { + "S3Storage": { + "properties": { + "enabled": { + "const": true + } + } + } + } + } + ] + } + }, + "title": "Values", + "type": "object" +} diff --git a/charts/posit-chronicle/values.yaml b/charts/posit-chronicle/values.yaml index 39b496882..d3a1182c8 100644 --- a/charts/posit-chronicle/values.yaml +++ b/charts/posit-chronicle/values.yaml @@ -1,83 +1,157 @@ +# -- Override for the name of the release +nameOverride: "" +# -- Override for the full name of the release +fullnameOverride: "" +# -- Override for the namespace of the chart deployment +namespaceOverride: "" +# -- Common labels to add to all resources +commonLabels: {} +# -- Common annotations to add to all resources +commonAnnotations: {} + +# -- Additional manifests to deploy with the chart with template value rendering +extraObjects: [] + image: - repository: "ghcr.io/rstudio/chronicle" - tag: "2025.03.0" - imagePullPolicy: "IfNotPresent" + # -- The image registry + registry: "ghcr.io" + # -- The image repository + repository: "rstudio/chronicle" + # -- The image tag, defaults to the chart app version + tag: "" + # -- The image digest + sha: "" + # -- The image pull policy + pullPolicy: "IfNotPresent" + # -- The container-level security context + # ([reference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#securitycontext-v1-core)) + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true -serviceaccount: +serviceAccount: + # -- Creates a service account for Posit Chronicle if true create: false - # -- Additional annotations to add to the chronicle-server serviceaccount + # -- Override for the service account name, defaults to fullname + name: "" + # -- Annotations to add to the service account annotations: { # EKS role arn example # eks.amazonaws.com/role-arn: my-aws-iam-role-arn } - # -- Additional labels to add to the chronicle-server serviceaccount + # -- Labels to add to the service account labels: {} service: - # -- The port to use for the REST service + # -- The port to use for the REST API service port: 80 - # -- Additional annotations to add to the chronicle-server service + # -- Annotations to add to the service annotations: {} - # -- Additional labels to add to the chronicle-server service + # -- Labels to add to the service labels: {} - # -- Additional selector labels to add to the chronicle-server service - selectorLabels: {} -# -- The number of replica pods to maintain for this service +# -- The number of replica pods to maintain replicas: 1 -# -- A map used verbatim as the pod's "nodeSelector" definition -nodeSelector: {} - pod: - # -- The command and args to run in the chronicle-server container - command: "/chronicle" - args: ["start", "-c", "/etc/posit-chronicle/posit-chronicle.gcfg"] - # -- Optional environment variables + # -- The command to run in the Chronicle server container, defaults to the image `ENTRYPOINT` value + command: [] + # -- The arguments to pass to the command, defaults to the image `CMD` values + args: [] + # -- Additional environment variables to set on the Chronicle server container env: [] - # -- Additional annotations to add to the chronicle-server pods + # -- Additional annotations for pods annotations: {} - # -- Additional labels to add to the chronicle-server pods + # -- Additional labels for pods labels: {} - # -- Additional selector labels to add to the chronicle-server pods - selectorLabels: {} - # -- A map used verbatim as the pod's "affinity" definition + # -- A map used verbatim as the pod "affinity" definition affinity: {} - # -- An array used verbatim as the pod's "tolerations" definition + # -- A map used verbatim as the pod "nodeSelector" definition + nodeSelector: {} + # -- An array used verbatim as the pod "tolerations" definition tolerations: [] # -- The termination grace period seconds allowed for the pod before shutdown terminationGracePeriodSeconds: 30 + # -- The pod-level security context + # ([reference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#podsecuritycontext-v1-core)) + securityContext: + fsGroup: 1000 + fsGroupChangePolicy: "OnRootMismatch" + + +# Configuration for application Persistent Volume Claims +persistence: + # -- Enable persistence using Persistent Volume Claims + enabled: true + # -- Persistent Volume Storage Class, defaults to the default Storage Class for the cluster + storageClassName: "" + # -- Size of the data volume + size: 10Gi + # -- Persistent Volume Access Modes + accessModes: + - ReadWriteOnce + # -- Selector to match an existing Persistent Volume for the data PVC + selectorLabels: {} + # -- Additional annotations for the PVC + annotations: {} + # -- Additional labels for the PVC + labels: {} + # -- Finalizers for the PVC + finalizers: + - kubernetes.io/pvc-protection -# If config.LocalStorage.Enabled is set to true, -# the chart will provision a pvc of size storage.persistentVolumeSize for -# the chronicle server stateful-set -storage: - persistentVolumeSize: 1Gi +# -- Additional secrets to mount to the Chronicle server pod +extraSecretMounts: [] +# this option can be used to mount secrets such as an SSL certificate and key into the pod +# - name: "ssl" +# secretName: "chronicle-ssl" +# mountPath: "/etc/chronicle/ssl" +# optional: false +# items: +# - key: "tls.crt" +# - key: "tls.key" +# Configurations for the underlying Chronicle server instance ([reference](https://docs.posit.co/chronicle/appendix/library/advanced-server.html)) config: HTTPS: # If https.enabled=true, ignore any http # values and enable https in the config instead + # -- If set to true, Chronicle will use HTTPS instead of HTTP Enabled: false - Key: "" + # -- Path to a PEM encoded certificate file, required if `HTTPS.Enabled=true` Certificate: "" + # -- Path to a PEM encoded private key file corresponding to the specified certificate, required if `HTTPS.Enabled=true` + Key: "" Metrics: - Enabled: true + # -- Exposes a metrics endpoint for Prometheus if true + Enabled: false Profiling: + # -- Exposes a pprof profiling server if true Enabled: false + # -- The port to use for the profiling server + Port: 3030 Logging: + # -- Specifies the output for log messages, can be one of "STDOUT", "STDERR", or a file path ServiceLog: "STDOUT" + # -- The log level for the service, can be one of "TRACE", "DEBUG", "INFO", "WARN", or "ERROR" ServiceLogLevel: "INFO" + # -- The log format for the service, can be one of "TEXT" or "JSON" ServiceLogFormat: "TEXT" + # Configuration for local data storage with Chronicle, for configuring persistence of this data see the persistence section LocalStorage: - # By default LocalStorage.Enabled=true, so that installs work with the default values + # -- Use `config.LocalStorage.Path` for data storage if true, use in conjunction with `persistence.enabled=true` for persistent data storage Enabled: true - Location: "./chronicle-data" - RetentionPeriod: "30d" + # -- The path to use for local storage + Path: "/opt/chronicle-data" + # Configuration for S3 data storage with Chronicle S3Storage: + # -- Use S3 for data storage if true Enabled: false - Bucket: "posit-chronicle" + # -- The S3 bucket to use for storage, required if `S3Storage.Enabled=true` + Bucket: "" + # -- An optional prefix path to use when writing to the S3 bucket Prefix: "" + # -- An IAM Profile to use for accessing the S3 bucket, default is to read from the `AWS_PROFILE` env var Profile: "" - # An AWS region must be set if S3 Storage is enabled - Region: "us-east-2" + # -- Region of the S3 bucket, default is to read from the `AWS_REGION` env var + Region: ""