From 53d2b73f5d141e38682f965ab1cfafe63855edda Mon Sep 17 00:00:00 2001 From: Joshua Matsuoka Date: Tue, 23 Apr 2024 13:03:08 -0400 Subject: [PATCH] feat(deploy): deploy cryostat 3.0 (#727) * feat(discovery): options to configure discovery port names and numbers (backport #715) (#725) * feat(discovery): options to configure discovery port names and numbers (#715) Signed-off-by: Thuan Vo (cherry picked from commit a55202198f41debba71c521817c56162ebc1fce5) * resolve conflict --------- Co-authored-by: Thuan Vo Co-authored-by: Andrew Azores * Deploy cryostat 3.0 * Remove extraneous file * test adjustments * feat(discovery): options to configure discovery port names and numbers (#715) Signed-off-by: Thuan Vo * Fix typo in environment variable breaking reconciler test, fix missing SecurityContext * Fix conflict with cluster cryostat removal * ci(gh): add comment when /build_test is finished (#745) * add scorecard test/suite selection (#746) * test(scorecard): scorecard tests for recording management (#698) * test(scorecard): scorecard tests for recording management Signed-off-by: Thuan Vo * fixup(scorecard): fix cr cleanup func * test(scorecard): registry recording test to suite * chore(scorecard): reorganize client def * chore(scorecard): clean up common setup func * chore(bundle): regenerate bundle with scorecard tag * chore(bundle): correct image tag in bundle * fix(bundle): add missing scorecard test config patch * feat(scorecard): scaffold cryostat API client * chore(scorecard): clean up API client * test(scorecard): implement recording scorecard test * fixup(scorecard): correctly add scorecard test via hack templates * fix(client): ignore unverified tls certs and base64 oauth token * chore(bundle): split cryostat tests to separate stage * fix(scorecard): extend default transport instead of overwriting * chore(scorecard): refactor client to support multi-part * fixup(client): fix request verb * fix(client): fix recording create form format * fix(scorecard): create stored credentials for target JVM * fix(scorecard): fix 502 status error * chore(scorecard): simplify client def * chore(scorecard): fetch recordings to ensure action is correctly performed * test(scorecard): test generating report for a recording * chore(scorecard): clean up * test(scorecard): list archives in tests * ci(scorecard): reconfigure ingress for kind * ci(k8s): correct cluster name * test(scorecard): use role instead of clusterrole for oauth rules * test(scorecard): parse health response for additional checks * chore(scorecard): add missing newline in logs * chore(scorecard): check status code before parsing body in health check * test(scorecard): add custom target discovery to recording scorecard test * add EOF wait and resp headers * add resp headers * chore(client): configure all clients to send safe requests * fix(clients): add missing content-type header * fix(scorecard): add missing test name in help message * chore(client): create new http requests when retrying * chore(bundle): update scorecard image tags --------- Signed-off-by: Thuan Vo Co-authored-by: Ming Yu Wang <90855268+mwangggg@users.noreply.github.com> Co-authored-by: Ming Wang * test(scorecard): scorecard test for Cryostat CR configuration changes (#739) * CR config scorecard * reformat * reviews * add kubectl license * test(scorecard): scorecard test for report generator (#753) * deploy reports sidecar * report scorecard test * update * rebase fix * query health * fix(build-ci): fix scorecard image tag returned as null (#760) Signed-off-by: Thuan Vo Co-authored-by: Elliott Baron * test(scorecard): add container logs to scorecard results (#758) * test(scorecard): add container logs to scorecard results * build(bundle): regenerate bundle with new scorecard tags * chore(scorecard): refactor to remove duplicate codes * add permission to publish comment when ci fails (#769) Co-authored-by: Elliott Baron * Update NewCoreContainer and associated tests * build(go): update Golang to 1.21 (#777) * test(scorecard): logWorkloadEvent for cryostat-recording errors (#759) * logWorkLoadEvent for cryostat-recording errors * reviews * tr.LogChannel --------- Co-authored-by: Elliott Baron * test(scorecard): fix rebasing skipped commit (#780) * Merge pull request #8 from ebaron/scorecard-methods test(scorecard): use methods for more easily passing data * update bundle image * Review fixes * generate storage key, create expected Secret * fixup! generate storage key, create expected Secret * database secret handling corrections * combine database connection password and encryption key into one secret * correct storage secret key/access key * update datasource port number to not conflict with storage * precreate eventtemplates bucket * remove storage volume parameter overrides * use HTTP for Cryostat probe even when TLS is enabled - TLS will be done via auth proxy later * correct environment variable names for proxy awareness * Fix remaining merge conflict * Fix makefile * config cleanup and test fixup --------- Signed-off-by: Thuan Vo Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Thuan Vo Co-authored-by: Andrew Azores Co-authored-by: Ming Yu Wang <90855268+mwangggg@users.noreply.github.com> Co-authored-by: Ming Wang Co-authored-by: Elliott Baron --- Makefile | 18 +- api/v1beta1/cryostat_conversion_test.go | 4 - api/v1beta1/cryostat_types.go | 20 + api/v1beta1/zz_generated.deepcopy.go | 36 + api/v1beta2/cryostat_types.go | 8 + api/v1beta2/zz_generated.deepcopy.go | 10 + bundle.Dockerfile | 2 +- ...yostat-operator.clusterserviceversion.yaml | 18 +- .../operator.cryostat.io_cryostats.yaml | 828 ++++++++++++++++-- bundle/metadata/annotations.yaml | 2 +- .../bases/operator.cryostat.io_cryostats.yaml | 828 ++++++++++++++++-- config/default/image_tag_patch.yaml | 2 +- ...yostat-operator.clusterserviceversion.yaml | 12 + .../resource_definitions.go | 441 +++++++--- internal/controllers/const_generated.go | 8 +- internal/controllers/constants/constants.go | 4 +- internal/controllers/reconciler.go | 8 + internal/controllers/reconciler_test.go | 152 ++-- internal/controllers/secrets.go | 52 +- internal/test/reconciler.go | 8 + internal/test/resources.go | 309 +++---- internal/tools/const_generator.go | 12 + 22 files changed, 2252 insertions(+), 530 deletions(-) diff --git a/Makefile b/Makefile index fc33bbbd9..9d9a63ac9 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ export APP_NAME ?= Cryostat # Images used by the operator CORE_NAMESPACE ?= $(DEFAULT_NAMESPACE) CORE_NAME ?= cryostat -CORE_VERSION ?= latest +CORE_VERSION ?= 3.0.0-snapshot export CORE_IMG ?= $(CORE_NAMESPACE)/$(CORE_NAME):$(CORE_VERSION) DATASOURCE_NAMESPACE ?= $(DEFAULT_NAMESPACE) DATASOURCE_NAME ?= jfr-datasource @@ -76,6 +76,14 @@ REPORTS_NAMESPACE ?= $(DEFAULT_NAMESPACE) REPORTS_NAME ?= cryostat-reports REPORTS_VERSION ?= latest export REPORTS_IMG ?= $(REPORTS_NAMESPACE)/$(REPORTS_NAME):$(REPORTS_VERSION) +DATABASE_NAMESPACE ?= $(DEFAULT_NAMESPACE) +DATABASE_NAME ?= cryostat-db +DATABASE_VERSION ?= latest +export DATABASE_IMG ?= $(DATABASE_NAMESPACE)/$(DATABASE_NAME):$(DATABASE_VERSION) +STORAGE_NAMESPACE ?= $(DEFAULT_NAMESPACE) +STORAGE_NAME ?= cryostat-storage +STORAGE_VERSION ?= latest +export STORAGE_IMG ?= $(STORAGE_NAMESPACE)/$(STORAGE_NAME):$(STORAGE_VERSION) CERT_MANAGER_VERSION ?= 1.11.5 CERT_MANAGER_MANIFEST ?= \ @@ -449,8 +457,8 @@ endif ##@ Deployment .PHONY: install -install: manifests kustomize ## Install CRDs into the cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | $(CLUSTER_CLIENT) apply -f - +install: uninstall manifests kustomize ## Install CRDs into the cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | $(CLUSTER_CLIENT) create -f - .PHONY: uninstall uninstall: manifests kustomize ## Uninstall CRDs from the cluster specified in ~/.kube/config. @@ -466,8 +474,8 @@ print_deploy_config: predeploy ## Print deployment configurations for the contro $(KUSTOMIZE) build $(KUSTOMIZE_DIR) .PHONY: deploy -deploy: check_cert_manager manifests kustomize predeploy ## Deploy controller in the configured cluster in ~/.kube/config - $(KUSTOMIZE) build $(KUSTOMIZE_DIR) | $(CLUSTER_CLIENT) apply -f - +deploy: check_cert_manager manifests kustomize predeploy undeploy ## Deploy controller in the configured cluster in ~/.kube/config + $(KUSTOMIZE) build $(KUSTOMIZE_DIR) | $(CLUSTER_CLIENT) create -f - ifeq ($(DISABLE_SERVICE_TLS), true) @echo "Disabling TLS for in-cluster communication between Services" @$(CLUSTER_CLIENT) -n $(DEPLOY_NAMESPACE) set env deployment/cryostat-operator-controller-manager DISABLE_SERVICE_TLS=true diff --git a/api/v1beta1/cryostat_conversion_test.go b/api/v1beta1/cryostat_conversion_test.go index a7cbc1ec0..443b69cdb 100644 --- a/api/v1beta1/cryostat_conversion_test.go +++ b/api/v1beta1/cryostat_conversion_test.go @@ -139,8 +139,6 @@ func tableEntries() []TableEntry { (*test.TestResources).NewCryostatWithResources), Entry("low resource limit", (*test.TestResources).NewCryostatWithLowResourceLimitV1Beta1, (*test.TestResources).NewCryostatWithLowResourceLimit), - Entry("auth properties", (*test.TestResources).NewCryostatWithAuthPropertiesV1Beta1, - (*test.TestResources).NewCryostatWithAuthProperties), Entry("built-in discovery disabled", (*test.TestResources).NewCryostatWithBuiltInDiscoveryDisabledV1Beta1, (*test.TestResources).NewCryostatWithBuiltInDiscoveryDisabled), Entry("discovery port custom config", (*test.TestResources).NewCryostatWithDiscoveryPortConfigV1Beta1, @@ -149,8 +147,6 @@ func tableEntries() []TableEntry { (*test.TestResources).NewCryostatWithBuiltInPortConfigDisabled), Entry("JMX cache options", (*test.TestResources).NewCryostatWithJmxCacheOptionsSpecV1Beta1, (*test.TestResources).NewCryostatWithJmxCacheOptionsSpec), - Entry("subprocess heap", (*test.TestResources).NewCryostatWithReportSubprocessHeapSpecV1Beta1, - (*test.TestResources).NewCryostatWithReportSubprocessHeapSpec), Entry("security", (*test.TestResources).NewCryostatWithSecurityOptionsV1Beta1, (*test.TestResources).NewCryostatWithSecurityOptions), Entry("reports security", (*test.TestResources).NewCryostatWithReportSecurityOptionsV1Beta1, diff --git a/api/v1beta1/cryostat_types.go b/api/v1beta1/cryostat_types.go index 9d554e068..3b7b04000 100644 --- a/api/v1beta1/cryostat_types.go +++ b/api/v1beta1/cryostat_types.go @@ -141,6 +141,8 @@ type CryostatStatus struct { // +optional // +operator-sdk:csv:customresourcedefinitions:type=status,order=2,xDescriptors={"urn:alm:descriptor:io.kubernetes:Secret"} GrafanaSecret string `json:"grafanaSecret,omitempty"` + // Name of the Secret containing the cryostat storage connection key + StorageSecret string `json:"storageSecret,omitempty"` // Address of the deployed Cryostat web application. // +operator-sdk:csv:customresourcedefinitions:type=status,order=1,xDescriptors={"urn:alm:descriptor:org.w3:link"} ApplicationURL string `json:"applicationUrl"` @@ -287,6 +289,13 @@ type GrafanaServiceConfig struct { ServiceConfig `json:",inline"` } +type StorageServiceConfig struct { + // HTTP port number for the cryostat storage service. + // Defaults to 8333 + HTTPPort *int32 `json:"httpPort,omitempty"` + ServiceConfig `json:",inline"` +} + // ReportsServiceConfig provides customization for the service handling // traffic for the cryostat-reports sidecars. type ReportsServiceConfig struct { @@ -309,6 +318,9 @@ type ServiceConfigList struct { // Specification for the service responsible for the cryostat-reports sidecars. // +optional ReportsConfig *ReportsServiceConfig `json:"reportsConfig,omitempty"` + // Specification for the service responsible for the cryostat storage container. + // +optional + StorageConfig *StorageServiceConfig `json:"storageConfig,omitEmpty"` } // NetworkConfiguration provides customization for how to expose a Cryostat @@ -502,6 +514,14 @@ type SecurityOptions struct { // +optional // +operator-sdk:csv:customresourcedefinitions:type=spec GrafanaSecurityContext *corev1.SecurityContext `json:"grafanaSecurityContext,omitempty"` + // Security Context to apply to the storage container. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + StorageSecurityContext *corev1.SecurityContext `json:"storageSecurityContext,omitempty"` + // Security Context to apply to the storage container. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + DatabaseSecurityContext *corev1.SecurityContext `json:"databaseSecurityContext,omitempty"` } // ReportsSecurityOptions contains Security Context customizations for the diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index a4098fcbd..7863ae29e 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -660,6 +660,16 @@ func (in *SecurityOptions) DeepCopyInto(out *SecurityOptions) { *out = new(corev1.SecurityContext) (*in).DeepCopyInto(*out) } + if in.StorageSecurityContext != nil { + in, out := &in.StorageSecurityContext, &out.StorageSecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.DatabaseSecurityContext != nil { + in, out := &in.DatabaseSecurityContext, &out.DatabaseSecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityOptions. @@ -724,6 +734,11 @@ func (in *ServiceConfigList) DeepCopyInto(out *ServiceConfigList) { *out = new(ReportsServiceConfig) (*in).DeepCopyInto(*out) } + if in.StorageConfig != nil { + in, out := &in.StorageConfig, &out.StorageConfig + *out = new(StorageServiceConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceConfigList. @@ -761,6 +776,27 @@ func (in *StorageConfiguration) DeepCopy() *StorageConfiguration { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageServiceConfig) DeepCopyInto(out *StorageServiceConfig) { + *out = *in + if in.HTTPPort != nil { + in, out := &in.HTTPPort, &out.HTTPPort + *out = new(int32) + **out = **in + } + in.ServiceConfig.DeepCopyInto(&out.ServiceConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageServiceConfig. +func (in *StorageServiceConfig) DeepCopy() *StorageServiceConfig { + if in == nil { + return nil + } + out := new(StorageServiceConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetDiscoveryOptions) DeepCopyInto(out *TargetDiscoveryOptions) { *out = *in diff --git a/api/v1beta2/cryostat_types.go b/api/v1beta2/cryostat_types.go index db2257e90..118c57aef 100644 --- a/api/v1beta2/cryostat_types.go +++ b/api/v1beta2/cryostat_types.go @@ -512,6 +512,14 @@ type SecurityOptions struct { // +optional // +operator-sdk:csv:customresourcedefinitions:type=spec GrafanaSecurityContext *corev1.SecurityContext `json:"grafanaSecurityContext,omitempty"` + // Security Context to apply to the storage container. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + StorageSecurityContext *corev1.SecurityContext `json:"storageSecurityContext,omitempty"` + // Security Context to apply to the database container. + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + DatabaseSecurityContext *corev1.SecurityContext `json:"databaseSecurityContext,omitempty"` } // ReportsSecurityOptions contains Security Context customizations for the diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index 96818377e..94b9e5f5f 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -665,6 +665,16 @@ func (in *SecurityOptions) DeepCopyInto(out *SecurityOptions) { *out = new(corev1.SecurityContext) (*in).DeepCopyInto(*out) } + if in.StorageSecurityContext != nil { + in, out := &in.StorageSecurityContext, &out.StorageSecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.DatabaseSecurityContext != nil { + in, out := &in.DatabaseSecurityContext, &out.DatabaseSecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityOptions. diff --git a/bundle.Dockerfile b/bundle.Dockerfile index 8f2467e7d..b3a677365 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -6,7 +6,7 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=cryostat-operator LABEL operators.operatorframework.io.bundle.channels.v1=alpha -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.31.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.32.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v3 diff --git a/bundle/manifests/cryostat-operator.clusterserviceversion.yaml b/bundle/manifests/cryostat-operator.clusterserviceversion.yaml index 1761b6e18..e9ad2edfd 100644 --- a/bundle/manifests/cryostat-operator.clusterserviceversion.yaml +++ b/bundle/manifests/cryostat-operator.clusterserviceversion.yaml @@ -69,7 +69,7 @@ metadata: } } } - operators.operatorframework.io/builder: operator-sdk-v1.31.0 + operators.operatorframework.io/builder: operator-sdk-v1.32.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: github.com/cryostatio/cryostat-operator support: Cryostat Community @@ -379,12 +379,18 @@ spec: - description: Security Context to apply to the JFR Data Source container. displayName: Data Source Security Context path: securityOptions.dataSourceSecurityContext + - description: Security Context to apply to the storage container. + displayName: Database Security Context + path: securityOptions.databaseSecurityContext - description: Security Context to apply to the Grafana container. displayName: Grafana Security Context path: securityOptions.grafanaSecurityContext - description: Security Context to apply to the Cryostat pod. displayName: Pod Security Context path: securityOptions.podSecurityContext + - description: Security Context to apply to the storage container. + displayName: Storage Security Context + path: securityOptions.storageSecurityContext - description: Options to customize the services created for the Cryostat application and Grafana dashboard. displayName: Service Options @@ -754,12 +760,18 @@ spec: - description: Security Context to apply to the JFR Data Source container. displayName: Data Source Security Context path: securityOptions.dataSourceSecurityContext + - description: Security Context to apply to the storage container. + displayName: Database Security Context + path: securityOptions.databaseSecurityContext - description: Security Context to apply to the Grafana container. displayName: Grafana Security Context path: securityOptions.grafanaSecurityContext - description: Security Context to apply to the Cryostat pod. displayName: Pod Security Context path: securityOptions.podSecurityContext + - description: Security Context to apply to the storage container. + displayName: Storage Security Context + path: securityOptions.storageSecurityContext - description: Options to customize the services created for the Cryostat application and Grafana dashboard. displayName: Service Options @@ -1091,7 +1103,7 @@ spec: - /manager env: - name: RELATED_IMAGE_CORE - value: quay.io/cryostat/cryostat:latest + value: quay.io/cryostat/cryostat:3.0.0-snapshot - name: RELATED_IMAGE_DATASOURCE value: quay.io/cryostat/jfr-datasource:latest - name: RELATED_IMAGE_GRAFANA @@ -1226,7 +1238,7 @@ spec: provider: name: The Cryostat Community relatedImages: - - image: quay.io/cryostat/cryostat:latest + - image: quay.io/cryostat/cryostat:3.0.0-snapshot name: core - image: quay.io/cryostat/jfr-datasource:latest name: datasource diff --git a/bundle/manifests/operator.cryostat.io_cryostats.yaml b/bundle/manifests/operator.cryostat.io_cryostats.yaml index 6b5826616..ebdfb45cc 100644 --- a/bundle/manifests/operator.cryostat.io_cryostats.yaml +++ b/bundle/manifests/operator.cryostat.io_cryostats.yaml @@ -3915,6 +3915,174 @@ spec: type: string type: object type: object + databaseSecurityContext: + description: Security Context to apply to the storage container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object grafanaSecurityContext: description: Security Context to apply to the Grafana container. properties: @@ -4219,11 +4387,179 @@ spec: type: array windowsOptions: description: The Windows specific settings applied to all - containers. If unspecified, the options within a container's - SecurityContext will be used. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is linux. + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + storageSecurityContext: + description: Security Context to apply to the storage container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. properties: gmsaCredentialSpec: description: GMSACredentialSpec is where the GMSA admission @@ -4345,6 +4681,32 @@ spec: description: Type of service to create. Defaults to "ClusterIP". type: string type: object + storageConfig: + description: Specification for the service responsible for the + cryostat storage container. + properties: + annotations: + additionalProperties: + type: string + description: Annotations to add to the service during its + creation. + type: object + httpPort: + description: HTTP port number for the cryostat storage service. + Defaults to 8333 + format: int32 + type: integer + labels: + additionalProperties: + type: string + description: Labels to add to the service during its creation. + The labels with keys "app" and "component" are reserved + for use by the operator. + type: object + serviceType: + description: Type of service to create. Defaults to "ClusterIP". + type: string + type: object type: object storageOptions: description: Options to customize the storage for Flight Recordings @@ -4733,6 +5095,10 @@ spec: grafanaSecret: description: Name of the Secret containing the generated Grafana credentials. type: string + storageSecret: + description: Name of the Secret containing the cryostat storage connection + key + type: string required: - applicationUrl type: object @@ -7916,61 +8282,230 @@ spec: type: array type: object type: object - nodeSelector: - additionalProperties: - type: string - description: 'Label selector used to schedule a Cryostat pod to - a node. See: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' - type: object - tolerations: - description: 'Tolerations to allow scheduling of Cryostat pods - to tainted nodes. See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . - properties: - effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, allowed - values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match - all values and all keys. - type: string - operator: - description: Operator represents a key's relationship to - the value. Valid operators are Exists and Equal. Defaults - to Equal. Exists is equivalent to wildcard for value, - so that a pod can tolerate all taints of a particular - category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the taint - forever (do not evict). Zero and negative values will - be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array - type: object - securityOptions: - description: Options to configure the Security Contexts for the Cryostat - application. - properties: - coreSecurityContext: - description: Security Context to apply to the Cryostat application + nodeSelector: + additionalProperties: + type: string + description: 'Label selector used to schedule a Cryostat pod to + a node. See: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + tolerations: + description: 'Tolerations to allow scheduling of Cryostat pods + to tainted nodes. See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + securityOptions: + description: Options to configure the Security Contexts for the Cryostat + application. + properties: + coreSecurityContext: + description: Security Context to apply to the Cryostat application + container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + dataSourceSecurityContext: + description: Security Context to apply to the JFR Data Source container. properties: allowPrivilegeEscalation: @@ -8138,9 +8673,8 @@ spec: type: string type: object type: object - dataSourceSecurityContext: - description: Security Context to apply to the JFR Data Source - container. + databaseSecurityContext: + description: Security Context to apply to the storage container. properties: allowPrivilegeEscalation: description: 'AllowPrivilegeEscalation controls whether a @@ -8649,6 +9183,174 @@ spec: type: string type: object type: object + storageSecurityContext: + description: Security Context to apply to the storage container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object type: object serviceOptions: description: Options to customize the services created for the Cryostat diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml index fc9f8edfc..70e2c7764 100644 --- a/bundle/metadata/annotations.yaml +++ b/bundle/metadata/annotations.yaml @@ -5,7 +5,7 @@ annotations: operators.operatorframework.io.bundle.metadata.v1: metadata/ operators.operatorframework.io.bundle.package.v1: cryostat-operator operators.operatorframework.io.bundle.channels.v1: alpha - operators.operatorframework.io.metrics.builder: operator-sdk-v1.31.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.32.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v3 diff --git a/config/crd/bases/operator.cryostat.io_cryostats.yaml b/config/crd/bases/operator.cryostat.io_cryostats.yaml index 0739136ab..7b688916f 100644 --- a/config/crd/bases/operator.cryostat.io_cryostats.yaml +++ b/config/crd/bases/operator.cryostat.io_cryostats.yaml @@ -3905,6 +3905,174 @@ spec: type: string type: object type: object + databaseSecurityContext: + description: Security Context to apply to the storage container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object grafanaSecurityContext: description: Security Context to apply to the Grafana container. properties: @@ -4209,11 +4377,179 @@ spec: type: array windowsOptions: description: The Windows specific settings applied to all - containers. If unspecified, the options within a container's - SecurityContext will be used. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is linux. + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + storageSecurityContext: + description: Security Context to apply to the storage container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. properties: gmsaCredentialSpec: description: GMSACredentialSpec is where the GMSA admission @@ -4335,6 +4671,32 @@ spec: description: Type of service to create. Defaults to "ClusterIP". type: string type: object + storageConfig: + description: Specification for the service responsible for the + cryostat storage container. + properties: + annotations: + additionalProperties: + type: string + description: Annotations to add to the service during its + creation. + type: object + httpPort: + description: HTTP port number for the cryostat storage service. + Defaults to 8333 + format: int32 + type: integer + labels: + additionalProperties: + type: string + description: Labels to add to the service during its creation. + The labels with keys "app" and "component" are reserved + for use by the operator. + type: object + serviceType: + description: Type of service to create. Defaults to "ClusterIP". + type: string + type: object type: object storageOptions: description: Options to customize the storage for Flight Recordings @@ -4723,6 +5085,10 @@ spec: grafanaSecret: description: Name of the Secret containing the generated Grafana credentials. type: string + storageSecret: + description: Name of the Secret containing the cryostat storage connection + key + type: string required: - applicationUrl type: object @@ -7906,61 +8272,230 @@ spec: type: array type: object type: object - nodeSelector: - additionalProperties: - type: string - description: 'Label selector used to schedule a Cryostat pod to - a node. See: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' - type: object - tolerations: - description: 'Tolerations to allow scheduling of Cryostat pods - to tainted nodes. See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . - properties: - effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, allowed - values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match - all values and all keys. - type: string - operator: - description: Operator represents a key's relationship to - the value. Valid operators are Exists and Equal. Defaults - to Equal. Exists is equivalent to wildcard for value, - so that a pod can tolerate all taints of a particular - category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the taint - forever (do not evict). Zero and negative values will - be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array - type: object - securityOptions: - description: Options to configure the Security Contexts for the Cryostat - application. - properties: - coreSecurityContext: - description: Security Context to apply to the Cryostat application + nodeSelector: + additionalProperties: + type: string + description: 'Label selector used to schedule a Cryostat pod to + a node. See: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + tolerations: + description: 'Tolerations to allow scheduling of Cryostat pods + to tainted nodes. See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + securityOptions: + description: Options to configure the Security Contexts for the Cryostat + application. + properties: + coreSecurityContext: + description: Security Context to apply to the Cryostat application + container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + dataSourceSecurityContext: + description: Security Context to apply to the JFR Data Source container. properties: allowPrivilegeEscalation: @@ -8128,9 +8663,8 @@ spec: type: string type: object type: object - dataSourceSecurityContext: - description: Security Context to apply to the JFR Data Source - container. + databaseSecurityContext: + description: Security Context to apply to the database container. properties: allowPrivilegeEscalation: description: 'AllowPrivilegeEscalation controls whether a @@ -8639,6 +9173,174 @@ spec: type: string type: object type: object + storageSecurityContext: + description: Security Context to apply to the storage container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object type: object serviceOptions: description: Options to customize the services created for the Cryostat diff --git a/config/default/image_tag_patch.yaml b/config/default/image_tag_patch.yaml index 32f0912c3..443332fd6 100644 --- a/config/default/image_tag_patch.yaml +++ b/config/default/image_tag_patch.yaml @@ -10,7 +10,7 @@ spec: - name: manager env: - name: RELATED_IMAGE_CORE - value: "quay.io/cryostat/cryostat:latest" + value: "quay.io/cryostat/cryostat:3.0.0-snapshot" - name: RELATED_IMAGE_DATASOURCE value: "quay.io/cryostat/jfr-datasource:latest" - name: RELATED_IMAGE_GRAFANA diff --git a/config/manifests/bases/cryostat-operator.clusterserviceversion.yaml b/config/manifests/bases/cryostat-operator.clusterserviceversion.yaml index f78ca7ac2..c5fd8c9d5 100644 --- a/config/manifests/bases/cryostat-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/cryostat-operator.clusterserviceversion.yaml @@ -305,12 +305,18 @@ spec: - description: Security Context to apply to the JFR Data Source container. displayName: Data Source Security Context path: securityOptions.dataSourceSecurityContext + - description: Security Context to apply to the storage container. + displayName: Database Security Context + path: securityOptions.databaseSecurityContext - description: Security Context to apply to the Grafana container. displayName: Grafana Security Context path: securityOptions.grafanaSecurityContext - description: Security Context to apply to the Cryostat pod. displayName: Pod Security Context path: securityOptions.podSecurityContext + - description: Security Context to apply to the storage container. + displayName: Storage Security Context + path: securityOptions.storageSecurityContext - description: Options to customize the services created for the Cryostat application and Grafana dashboard. displayName: Service Options @@ -709,12 +715,18 @@ spec: - description: Security Context to apply to the JFR Data Source container. displayName: Data Source Security Context path: securityOptions.dataSourceSecurityContext + - description: Security Context to apply to the storage container. + displayName: Database Security Context + path: securityOptions.databaseSecurityContext - description: Security Context to apply to the Grafana container. displayName: Grafana Security Context path: securityOptions.grafanaSecurityContext - description: Security Context to apply to the Cryostat pod. displayName: Pod Security Context path: securityOptions.podSecurityContext + - description: Security Context to apply to the storage container. + displayName: Storage Security Context + path: securityOptions.storageSecurityContext - description: Options to customize the services created for the Cryostat application and Grafana dashboard. displayName: Service Options diff --git a/internal/controllers/common/resource_definitions/resource_definitions.go b/internal/controllers/common/resource_definitions/resource_definitions.go index a3cf2bf43..470f1dd5e 100644 --- a/internal/controllers/common/resource_definitions/resource_definitions.go +++ b/internal/controllers/common/resource_definitions/resource_definitions.go @@ -38,6 +38,8 @@ type ImageTags struct { DatasourceImageTag string GrafanaImageTag string ReportsImageTag string + StorageImageTag string + DatabaseImageTag string } type ServiceSpecs struct { @@ -45,6 +47,8 @@ type ServiceSpecs struct { GrafanaURL *url.URL ReportsURL *url.URL InsightsURL *url.URL + StorageURL *url.URL + DatabaseURL *url.URL } // TLSConfig contains TLS-related information useful when creating other objects @@ -241,6 +245,8 @@ func NewPodForCR(cr *model.CryostatInstance, specs *ServiceSpecs, imageTags *Ima NewCoreContainer(cr, specs, imageTags.CoreImageTag, tls, openshift), NewGrafanaContainer(cr, imageTags.GrafanaImageTag, tls), NewJfrDatasourceContainer(cr, imageTags.DatasourceImageTag), + NewStorageContainer(cr, imageTags.StorageImageTag, tls), + newDatabaseContainer(cr, imageTags.DatabaseImageTag, tls), } volumes := newVolumeForCR(cr) @@ -349,28 +355,6 @@ func NewPodForCR(cr *model.CryostatInstance, specs *ServiceSpecs, imageTags *Ima volumes = append(volumes, eventTemplateVolume) } - // Add Auth properties as a volume if specified (on Openshift) - if openshift && cr.Spec.AuthProperties != nil { - authResourceVolume := corev1.Volume{ - Name: "auth-properties-" + cr.Spec.AuthProperties.ConfigMapName, - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cr.Spec.AuthProperties.ConfigMapName, - }, - Items: []corev1.KeyToPath{ - { - Key: cr.Spec.AuthProperties.Filename, - Path: "OpenShiftAuthManager.properties", - Mode: &readOnlyMode, - }, - }, - }, - }, - } - volumes = append(volumes, authResourceVolume) - } - var podSc *corev1.PodSecurityContext if cr.Spec.SecurityOptions != nil && cr.Spec.SecurityOptions.PodSecurityContext != nil { podSc = cr.Spec.SecurityOptions.PodSecurityContext @@ -604,12 +588,71 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag templatesPath := "/opt/cryostat.d/templates.d" clientlibPath := "/opt/cryostat.d/clientlib.d" probesPath := "/opt/cryostat.d/probes.d" - authPropertiesPath := "/app/resources/io/cryostat/net/openshift/OpenShiftAuthManager.properties" envs := []corev1.EnvVar{ { - Name: "CRYOSTAT_WEB_PORT", - Value: strconv.Itoa(int(constants.CryostatHTTPContainerPort)), + Name: "QUARKUS_HTTP_HOST", + Value: "localhost", + }, + { + Name: "QUARKUS_HTTP_PORT", + Value: "8181", + }, + { + Name: "QUARKUS_HTTP_PROXY_PROXY_ADDRESS_FORWARDING", + Value: "true", + }, + { + Name: "QUARKUS_HTTP_PROXY_ALLOW_X_FORWARDED", + Value: "true", + }, + { + Name: "QUARKUS_HTTP_PROXY_ENABLE_FORWARDED_HOST", + Value: "true", + }, + { + Name: "QUARKUS_HTTP_PROXY_ENABLE_FORWARDED_PREFIX", + Value: "true", + }, + { + Name: "QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION", + Value: "drop-and-create", + }, + { + Name: "QUARKUS_DATASOURCE_USERNAME", + Value: "cryostat3", + }, + { + Name: "QUARKUS_DATASOURCE_JDBC_URL", + Value: "jdbc:postgresql://localhost:5432/cryostat3", + }, + { + Name: "STORAGE_BUCKETS_ARCHIVE_NAME", + Value: "archivedrecordings", + }, + { + Name: "QUARKUS_S3_ENDPOINT_OVERRIDE", + Value: "http://localhost:8333", + }, + { + Name: "QUARKUS_S3_PATH_STYLE_ACCESS", + Value: "true", + }, + { + Name: "QUARKUS_S3_AWS_REGION", + Value: "us-east-1", + }, + { + Name: "QUARKUS_S3_AWS_CREDENTIALS_TYPE", + Value: "static", + }, + { + Name: "QUARKUS_S3_CREDENTIALS_STATIC_PROVIDER_ACCESS_KEY_ID", + Value: "cryostat", + }, + { + Name: "AWS_ACCESS_KEY_ID", + Value: "$(QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_ACCESS_KEY_ID)", }, { Name: "CRYOSTAT_CONFIG_PATH", @@ -680,6 +723,48 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag }, } + optional := false + secretName := cr.Name + "-db" + if cr.Spec.JmxCredentialsDatabaseOptions != nil && cr.Spec.JmxCredentialsDatabaseOptions.DatabaseSecretName != nil { + secretName = *cr.Spec.JmxCredentialsDatabaseOptions.DatabaseSecretName + } + envs = append(envs, corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "CONNECTION_KEY", + Optional: &optional, + }, + }, + }) + + envs = append(envs, corev1.EnvVar{ + Name: "QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_ACCESS_KEY_ID", + Value: "cryostat", + }) + + secretName = cr.Name + "-storage-secret-key" + envs = append(envs, corev1.EnvVar{ + Name: "QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_SECRET_ACCESS_KEY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "SECRET_KEY", + Optional: &optional, + }, + }, + }) + + envs = append(envs, corev1.EnvVar{ + Name: "AWS_SECRET_ACCESS_KEY", + Value: "$(QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_SECRET_ACCESS_KEY)", + }) + if specs.CoreURL != nil { coreEnvs := []corev1.EnvVar{ { @@ -696,23 +781,11 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag if specs.ReportsURL != nil { reportsEnvs := []corev1.EnvVar{ { - Name: "CRYOSTAT_REPORT_GENERATOR", + Name: "CRYOSTAT_SERVICES_REPORTS_URL", Value: specs.ReportsURL.String(), }, } envs = append(envs, reportsEnvs...) - } else { - subProcessMaxHeapSize := "200" - if cr.Spec.ReportOptions != nil && cr.Spec.ReportOptions.SubProcessMaxHeapSize != 0 { - subProcessMaxHeapSize = strconv.Itoa(int(cr.Spec.ReportOptions.SubProcessMaxHeapSize)) - } - subprocessReportHeapEnv := []corev1.EnvVar{ - { - Name: "CRYOSTAT_REPORT_GENERATION_MAX_HEAP", - Value: subProcessMaxHeapSize, - }, - } - envs = append(envs, subprocessReportHeapEnv...) } // Define INSIGHTS_PROXY URL if Insights integration is enabled @@ -740,11 +813,11 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag } jmxCacheEnvs := []corev1.EnvVar{ { - Name: "CRYOSTAT_TARGET_CACHE_SIZE", + Name: "CRYOSTAT_CONNECTIONS_MAX_OPEN", Value: targetCacheSize, }, { - Name: "CRYOSTAT_TARGET_CACHE_TTL", + Name: "CRYOSTAT_CONNECTIONS_TTL", Value: targetCacheTTL, }, } @@ -759,51 +832,7 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag }, } - if openshift { - // Force OpenShift platform strategy - openshiftEnvs := []corev1.EnvVar{ - { - Name: "CRYOSTAT_PLATFORM", - Value: "io.cryostat.platform.internal.OpenShiftPlatformStrategy", - }, - { - Name: "CRYOSTAT_AUTH_MANAGER", - Value: "io.cryostat.net.openshift.OpenShiftAuthManager", - }, - { - Name: "CRYOSTAT_OAUTH_CLIENT_ID", - Value: cr.Name, - }, - { - Name: "CRYOSTAT_BASE_OAUTH_ROLE", - Value: constants.OperatorNamePrefix + "oauth-client", - }, - } - envs = append(envs, openshiftEnvs...) - - if cr.Spec.AuthProperties != nil { - // Mount Auth properties if specified (on Openshift) - mounts = append(mounts, corev1.VolumeMount{ - Name: "auth-properties-" + cr.Spec.AuthProperties.ConfigMapName, - MountPath: authPropertiesPath, - SubPath: "OpenShiftAuthManager.properties", - ReadOnly: true, - }) - envs = append(envs, corev1.EnvVar{ - Name: "CRYOSTAT_CUSTOM_OAUTH_ROLE", - Value: cr.Spec.AuthProperties.ClusterRoleName, - }) - } - } - if cr.Spec.TargetDiscoveryOptions != nil { - if cr.Spec.TargetDiscoveryOptions.BuiltInDiscoveryDisabled { - envs = append(envs, corev1.EnvVar{ - Name: "CRYOSTAT_DISABLE_BUILTIN_DISCOVERY", - Value: "true", - }) - } - var portNames string if len(cr.Spec.TargetDiscoveryOptions.DiscoveryPortNames) > 0 { portNames = strings.Join(cr.Spec.TargetDiscoveryOptions.DiscoveryPortNames[:], ",") @@ -835,46 +864,6 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag } } - if !useEmptyDir(cr) { - envs = append(envs, corev1.EnvVar{ - Name: "CRYOSTAT_JDBC_URL", - Value: "jdbc:h2:file:/opt/cryostat.d/conf.d/h2;INIT=create domain if not exists jsonb as varchar", - }, corev1.EnvVar{ - Name: "CRYOSTAT_HBM2DDL", - Value: "update", - }, corev1.EnvVar{ - Name: "CRYOSTAT_JDBC_DRIVER", - Value: "org.h2.Driver", - }, corev1.EnvVar{ - Name: "CRYOSTAT_HIBERNATE_DIALECT", - Value: "org.hibernate.dialect.H2Dialect", - }, corev1.EnvVar{ - Name: "CRYOSTAT_JDBC_USERNAME", - Value: cr.Name, - }, corev1.EnvVar{ - Name: "CRYOSTAT_JDBC_PASSWORD", - Value: cr.Name, - }) - } - - secretOptional := false - secretName := cr.Name + "-jmx-credentials-db" - if cr.Spec.JmxCredentialsDatabaseOptions != nil && cr.Spec.JmxCredentialsDatabaseOptions.DatabaseSecretName != nil { - secretName = *cr.Spec.JmxCredentialsDatabaseOptions.DatabaseSecretName - } - envs = append(envs, corev1.EnvVar{ - Name: "CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: secretName, - }, - Key: "CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD", - Optional: &secretOptional, - }, - }, - }) - grafanaVars := []corev1.EnvVar{ { Name: "GRAFANA_DATASOURCE_URL", @@ -894,7 +883,6 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag } envs = append(envs, grafanaVars...) - livenessProbeScheme := corev1.URISchemeHTTP if tls == nil { // If TLS isn't set up, tell Cryostat to not use it envs = append(envs, corev1.EnvVar{ @@ -930,9 +918,6 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag } mounts = append(mounts, keystoreMount) - - // Use HTTPS for liveness probe - livenessProbeScheme = corev1.URISchemeHTTPS } // Mount the templates specified in Cryostat CR under /opt/cryostat.d/templates.d @@ -950,7 +935,7 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag HTTPGet: &corev1.HTTPGetAction{ Port: intstr.IntOrString{IntVal: constants.CryostatHTTPContainerPort}, Path: "/health/liveness", - Scheme: livenessProbeScheme, + Scheme: corev1.URISchemeHTTP, }, } @@ -1091,6 +1076,198 @@ func NewGrafanaContainer(cr *model.CryostatInstance, imageTag string, tls *TLSCo } } +/* + * storage.image.repository - Repository for the database container + * storage.image.pullPolicy - Pull Policy for the database container + * storage.image.tag - Image tag for the database container + * storage.resources - Resource requests/limits for the database container + * storage.securityContext - Security Context for the database container + */ +func NewStorageContainer(cr *model.CryostatInstance, imageTag string, tls *TLSConfig) corev1.Container { + var containerSc *corev1.SecurityContext + envs := []corev1.EnvVar{ + { + Name: "CRYOSTAT_BUCKETS", + Value: "archivedrecordings,archivedreports,eventtemplates", + }, + { + Name: "CRYOSTAT_ACCESS_KEY", + Value: "cryostat", + }, + { + Name: "DATA_DIR", + Value: "/data", + }, + { + Name: "IP_BIND", + Value: "0.0.0.0", + }, + } + + mounts := []corev1.VolumeMount{ + { + Name: cr.Name, + MountPath: "/data", + SubPath: "seaweed", + }, + } + + secretName := cr.Name + "-storage-secret-key" + optional := false + envs = append(envs, corev1.EnvVar{ + Name: "CRYOSTAT_SECRET_KEY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "SECRET_KEY", + Optional: &optional, + }, + }, + }) + + if cr.Spec.SecurityOptions != nil && cr.Spec.SecurityOptions.StorageSecurityContext != nil { + containerSc = cr.Spec.SecurityOptions.StorageSecurityContext + } else { + privEscalation := false + containerSc = &corev1.SecurityContext{ + AllowPrivilegeEscalation: &privEscalation, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{constants.CapabilityAll}, + }, + } + } + + livenessProbeScheme := corev1.URISchemeHTTP + probeHandler := corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.IntOrString{IntVal: 8333}, + Path: "/status", + Scheme: livenessProbeScheme, + }, + } + + return corev1.Container{ + Name: cr.Name + "-storage", + Image: imageTag, + ImagePullPolicy: getPullPolicy(imageTag), + VolumeMounts: mounts, + SecurityContext: containerSc, + Env: envs, + Ports: []corev1.ContainerPort{ + { + ContainerPort: constants.StoragePort, + }, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: probeHandler, + FailureThreshold: 2, + }, + StartupProbe: &corev1.Probe{ + ProbeHandler: probeHandler, + FailureThreshold: 13, + }, + } +} + +/* +* { + Name: cr.Name, + MountPath: "/var/lib/pgsql/data", + SubPath: "postgres", + }, +*/ +/* + * db.image.repository - Repository for the database container + * db.image.pullPolicy - Pull Policy for the database container + * db.image.tag - Image tag for the database container + * db.resources - Resource requests/limits for the database container + * db.securityContext - Security Context for the database container + */ +func newDatabaseContainer(cr *model.CryostatInstance, imageTag string, tls *TLSConfig) corev1.Container { + var containerSc *corev1.SecurityContext + if cr.Spec.SecurityOptions != nil && cr.Spec.SecurityOptions.DatabaseSecurityContext != nil { + containerSc = cr.Spec.SecurityOptions.DatabaseSecurityContext + } else { + privEscalation := false + containerSc = &corev1.SecurityContext{ + AllowPrivilegeEscalation: &privEscalation, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{constants.CapabilityAll}, + }, + } + } + + envs := []corev1.EnvVar{ + { + Name: "POSTGRESQL_USER", + Value: "cryostat3", + }, + { + Name: "POSTGRESQL_DATABASE", + Value: "cryostat3", + }, + } + + optional := false + secretName := cr.Name + "-db" + envs = append(envs, corev1.EnvVar{ + Name: "POSTGRESQL_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "CONNECTION_KEY", + Optional: &optional, + }, + }, + }) + + envs = append(envs, corev1.EnvVar{ + Name: "PG_ENCRYPT_KEY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "ENCRYPTION_KEY", + Optional: &optional, + }, + }, + }) + + mounts := []corev1.VolumeMount{ + { + Name: cr.Name, + MountPath: "/data", + SubPath: "seaweed", + }, + } + + return corev1.Container{ + Name: cr.Name + "-db", + Image: imageTag, + ImagePullPolicy: getPullPolicy(imageTag), + VolumeMounts: mounts, + SecurityContext: containerSc, + Env: envs, + Ports: []corev1.ContainerPort{ + { + ContainerPort: constants.DatabasePort, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"pg_isready", "-U", "cryostat3", "-d", "cryostat3"}, + }, + }, + }, + } +} + // datasourceURL contains the fixed URL to jfr-datasource's web server var datasourceURL = "http://" + constants.LoopbackAddress + ":" + strconv.Itoa(int(constants.DatasourceContainerPort)) @@ -1128,9 +1305,13 @@ func NewJfrDatasourceContainer(cr *model.CryostatInstance, imageTag string) core }, Env: []corev1.EnvVar{ { - Name: "LISTEN_HOST", + Name: "QUARKUS_HTTP_HOST", Value: constants.LoopbackAddress, }, + { + Name: "QUARKUS_HTTP_PORT", + Value: strconv.Itoa(int(constants.DatasourceContainerPort)), + }, }, // Can't use HTTP probe since the port is not exposed over the network LivenessProbe: &corev1.Probe{ diff --git a/internal/controllers/const_generated.go b/internal/controllers/const_generated.go index 94f769ea5..d96a17a87 100644 --- a/internal/controllers/const_generated.go +++ b/internal/controllers/const_generated.go @@ -8,7 +8,7 @@ const AppName = "Cryostat" const OperatorVersion = "3.0.0-dev" // Default image tag for the core application image -const DefaultCoreImageTag = "quay.io/cryostat/cryostat:latest" +const DefaultCoreImageTag = "quay.io/cryostat/cryostat:3.0.0-snapshot" // Default image tag for the JFR datasource image const DefaultDatasourceImageTag = "quay.io/cryostat/jfr-datasource:latest" @@ -18,3 +18,9 @@ const DefaultGrafanaImageTag = "quay.io/cryostat/cryostat-grafana-dashboard:late // Default image tag for the Grafana dashboard image const DefaultReportsImageTag = "quay.io/cryostat/cryostat-reports:latest" + +// Default image tag for the Storage image +const DefaultStorageImageTag = "quay.io/cryostat/cryostat-storage:latest" + +// Default image tag for the Database image +const DefaultDatabaseImageTag = "quay.io/cryostat/cryostat-db:latest" diff --git a/internal/controllers/constants/constants.go b/internal/controllers/constants/constants.go index cc9541bf3..0e89f2f6a 100644 --- a/internal/controllers/constants/constants.go +++ b/internal/controllers/constants/constants.go @@ -23,8 +23,10 @@ const ( CryostatHTTPContainerPort int32 = 8181 CryostatJMXContainerPort int32 = 9091 GrafanaContainerPort int32 = 3000 - DatasourceContainerPort int32 = 8080 + DatasourceContainerPort int32 = 8989 ReportsContainerPort int32 = 10000 + StoragePort int32 = 8333 + DatabasePort int32 = 5432 LoopbackAddress string = "127.0.0.1" OperatorNamePrefix string = "cryostat-operator-" OperatorDeploymentName string = "cryostat-operator-controller-manager" diff --git a/internal/controllers/reconciler.go b/internal/controllers/reconciler.go index 2973af1ef..ce23a86ee 100644 --- a/internal/controllers/reconciler.go +++ b/internal/controllers/reconciler.go @@ -93,6 +93,12 @@ const grafanaImageTagEnv = "RELATED_IMAGE_GRAFANA" // Environment variable to override the cryostat-reports image const reportsImageTagEnv = "RELATED_IMAGE_REPORTS" +// Environment variable to override the cryostat-storage image +const storageImageTagEnv = "RELATED_IMAGE_STORAGE" + +// Environment variable to override the cryostat-database image +const databaseImageTagEnv = "RELATED_IMAGE_DATABASE" + // Regular expression for the start of a GID range in the OpenShift // supplemental groups SCC annotation var supGroupRegexp = regexp.MustCompile(`^\d+`) @@ -362,6 +368,8 @@ func (r *Reconciler) getImageTags() *resources.ImageTags { DatasourceImageTag: r.getEnvOrDefault(datasourceImageTagEnv, DefaultDatasourceImageTag), GrafanaImageTag: r.getEnvOrDefault(grafanaImageTagEnv, DefaultGrafanaImageTag), ReportsImageTag: r.getEnvOrDefault(reportsImageTagEnv, DefaultReportsImageTag), + StorageImageTag: r.getEnvOrDefault(storageImageTagEnv, DefaultStorageImageTag), + DatabaseImageTag: r.getEnvOrDefault(databaseImageTagEnv, DefaultDatabaseImageTag), } } diff --git a/internal/controllers/reconciler_test.go b/internal/controllers/reconciler_test.go index 57c7402ef..e8fcde9ab 100644 --- a/internal/controllers/reconciler_test.go +++ b/internal/controllers/reconciler_test.go @@ -66,7 +66,7 @@ type cryostatTestInput struct { func (c *controllerTest) commonBeforeEach() *cryostatTestInput { t := &cryostatTestInput{ TestReconcilerConfig: test.TestReconcilerConfig{ - GeneratedPasswords: []string{"grafana", "credentials_database", "jmx", "keystore"}, + GeneratedPasswords: []string{"grafana", "credentials_database", "encryption_key", "object_storage", "jmx", "keystore"}, }, TestResources: &test.TestResources{ Name: "cryostat", @@ -146,6 +146,7 @@ func resourceChecks() []resourceCheck { }, "persistent volume claim"}, {(*cryostatTestInput).expectGrafanaSecret, "Grafana secret"}, {(*cryostatTestInput).expectCredentialsDatabaseSecret, "credentials database secret"}, + {(*cryostatTestInput).expectStorageSecret, "object storage secret"}, {(*cryostatTestInput).expectJMXSecret, "JMX secret"}, {(*cryostatTestInput).expectGrafanaService, "Grafana service"}, {(*cryostatTestInput).expectCoreService, "core service"}, @@ -528,7 +529,7 @@ func (c *controllerTest) commonTests() { var oldSecret *corev1.Secret BeforeEach(func() { cr = t.NewCryostat() - oldSecret = t.OtherCredentialsDatabaseSecret() + oldSecret = t.OtherDatabaseSecret() t.objs = append(t.objs, cr.Object, oldSecret) }) JustBeforeEach(func() { @@ -540,7 +541,7 @@ func (c *controllerTest) commonTests() { Expect(err).ToNot(HaveOccurred()) Expect(metav1.IsControlledBy(secret, cr.Object)).To(BeTrue()) - Expect(secret.StringData["CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD"]).To(Equal(oldSecret.StringData["CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD"])) + Expect(secret.StringData["CONNECTION_KEY"]).To(Equal(oldSecret.StringData["CONNECTION_KEY"])) }) }) Context("with existing Routes", func() { @@ -949,9 +950,6 @@ func (c *controllerTest) commonTests() { It("should create the EmptyDir with default specs", func() { t.expectEmptyDir(t.NewDefaultEmptyDir()) }) - It("should set Cryostat database to h2:mem", func() { - t.expectInMemoryDatabase() - }) }) Context("with custom EmptyDir config with requested spec", func() { BeforeEach(func() { @@ -963,9 +961,6 @@ func (c *controllerTest) commonTests() { It("should create the EmptyDir with requested specs", func() { t.expectEmptyDir(t.NewEmptyDirWithSpec()) }) - It("should set Cryostat database to h2:file", func() { - t.expectInMemoryDatabase() - }) }) Context("with overriden image tags", func() { var mainDeploy, reportsDeploy *appsv1.Deployment @@ -988,10 +983,14 @@ func (c *controllerTest) commonTests() { datasourceImg := "my/datasource-image:1.0.0-BETA25" grafanaImg := "my/grafana-image:1.0.0-dev" reportsImg := "my/reports-image:1.0.0-SNAPSHOT" + storageImg := "my/storage-image:1.0.0-dev" + databaseImg := "my/database-image:1.0.0-dev" t.EnvCoreImageTag = &coreImg t.EnvDatasourceImageTag = &datasourceImg t.EnvGrafanaImageTag = &grafanaImg t.EnvReportsImageTag = &reportsImg + t.EnvDatabaseImageTag = &databaseImg + t.EnvStorageImageTag = &storageImg }) It("should create deployment with the expected tags", func() { t.expectMainDeployment() @@ -999,7 +998,7 @@ func (c *controllerTest) commonTests() { }) It("should set ImagePullPolicy to Always", func() { containers := mainDeploy.Spec.Template.Spec.Containers - Expect(containers).To(HaveLen(3)) + Expect(containers).To(HaveLen(5)) for _, container := range containers { Expect(container.ImagePullPolicy).To(Equal(corev1.PullAlways)) } @@ -1014,10 +1013,14 @@ func (c *controllerTest) commonTests() { datasourceImg := "my/datasource-image:1.0.0" grafanaImg := "my/grafana-image:1.0.0" reportsImg := "my/reports-image:1.0.0" + storageImg := "my/storage-image:1.0.0" + databaseImg := "my/database-image:1.0.0" t.EnvCoreImageTag = &coreImg t.EnvDatasourceImageTag = &datasourceImg t.EnvGrafanaImageTag = &grafanaImg t.EnvReportsImageTag = &reportsImg + t.EnvDatabaseImageTag = &databaseImg + t.EnvStorageImageTag = &storageImg }) JustBeforeEach(func() { t.reconcileCryostatFully() @@ -1028,7 +1031,7 @@ func (c *controllerTest) commonTests() { }) It("should set ImagePullPolicy to IfNotPresent", func() { containers := mainDeploy.Spec.Template.Spec.Containers - Expect(containers).To(HaveLen(3)) + Expect(containers).To(HaveLen(5)) for _, container := range containers { Expect(container.ImagePullPolicy).To(Equal(corev1.PullIfNotPresent)) } @@ -1043,10 +1046,14 @@ func (c *controllerTest) commonTests() { datasourceImg := "my/datasource-image@sha256:59ded87392077c2371b26e021aade0409855b597383fa78e549eefafab8fc90c" grafanaImg := "my/grafana-image@sha256:e5bc16c2c5b69cd6fd8fdf1381d0a8b6cc9e01d92b9e1bb0a61ed89196563c72" reportsImg := "my/reports-image@sha256:8a23ca5e8c8a343789b8c14558a44a49d35ecd130c18e62edf0d1ad9ce88d37d" + storageImg := "my/storage-image@sha256:8b23ca5e8c8a343789b8c14558a44a49d35ecd130c18e62edf0d1ad9ce88d37d" + databaseImg := "my/database-image@sha256:8c23ca5e8c8a343789b8c14558a44a49d35ecd130c18e62edf0d1ad9ce88d37d" t.EnvCoreImageTag = &coreImg t.EnvDatasourceImageTag = &datasourceImg t.EnvGrafanaImageTag = &grafanaImg t.EnvReportsImageTag = &reportsImg + t.EnvDatabaseImageTag = &databaseImg + t.EnvStorageImageTag = &storageImg }) It("should create deployment with the expected tags", func() { t.expectMainDeployment() @@ -1054,7 +1061,7 @@ func (c *controllerTest) commonTests() { }) It("should set ImagePullPolicy to IfNotPresent", func() { containers := mainDeploy.Spec.Template.Spec.Containers - Expect(containers).To(HaveLen(3)) + Expect(containers).To(HaveLen(5)) for _, container := range containers { Expect(container.ImagePullPolicy).To(Equal(corev1.PullIfNotPresent)) } @@ -1080,7 +1087,7 @@ func (c *controllerTest) commonTests() { }) It("should set ImagePullPolicy to Always", func() { containers := mainDeploy.Spec.Template.Spec.Containers - Expect(containers).To(HaveLen(3)) + Expect(containers).To(HaveLen(5)) for _, container := range containers { Expect(container.ImagePullPolicy).To(Equal(corev1.PullAlways)) } @@ -1499,14 +1506,6 @@ func (c *controllerTest) commonTests() { JustBeforeEach(func() { t.reconcileCryostatFully() }) - Context("containing SubProcessMaxHeapSize", func() { - BeforeEach(func() { - t.objs = append(t.objs, t.NewCryostatWithReportSubprocessHeapSpec().Object) - }) - It("should set report subprocess max heap size", func() { - t.checkCoreHasEnvironmentVariables(t.NewReportSubprocessHeapEnv()) - }) - }) Context("containing JmxCacheOptions", func() { BeforeEach(func() { t.objs = append(t.objs, t.NewCryostatWithJmxCacheOptionsSpec().Object) @@ -1558,17 +1557,6 @@ func (c *controllerTest) commonTests() { }) }) }) - Context("Cryostat CR has authorization properties", func() { - BeforeEach(func() { - t.objs = append(t.objs, t.NewCryostatWithAuthProperties().Object, t.NewAuthPropertiesConfigMap(), t.NewAuthClusterRole()) - }) - JustBeforeEach(func() { - t.reconcileCryostatFully() - }) - It("Should add volumes and volumeMounts to deployment", func() { - t.checkDeploymentHasAuthProperties() - }) - }) Context("with security options", func() { JustBeforeEach(func() { t.reconcileCryostatFully() @@ -1658,16 +1646,16 @@ func (c *controllerTest) commonTests() { }) It("should not generate default secret", func() { secret := &corev1.Secret{} - err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-jmx-credentials-db", Namespace: t.Namespace}, secret) + err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-db", Namespace: t.Namespace}, secret) Expect(kerrors.IsNotFound(err)).To(BeTrue()) }) Context("with an existing Credentials Database Secret", func() { BeforeEach(func() { - t.objs = append(t.objs, t.NewCredentialsDatabaseSecret()) + t.objs = append(t.objs, t.NewDatabaseSecret()) }) It("should not delete the existing Credentials Database Secret", func() { secret := &corev1.Secret{} - err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-jmx-credentials-db", Namespace: t.Namespace}, secret) + err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-db", Namespace: t.Namespace}, secret) Expect(err).ToNot(HaveOccurred()) }) }) @@ -2008,17 +1996,6 @@ func (c *controllerTest) commonTests() { Expect(kerrors.IsNotFound(err)).To(BeTrue()) }) }) - Context("Cryostat CR has authorization properties", func() { - BeforeEach(func() { - t.objs = append(t.objs, t.NewCryostatWithAuthProperties().Object, t.NewAuthPropertiesConfigMap(), t.NewAuthClusterRole()) - }) - JustBeforeEach(func() { - t.reconcileCryostatFully() - }) - It("Should not add volumes and volumeMounts to deployment", func() { - t.checkDeploymentHasNoAuthProperties() - }) - }) Context("with report generator service", func() { BeforeEach(func() { t.ReportReplicas = 1 @@ -2462,16 +2439,6 @@ func (t *cryostatTestInput) expectEmptyDir(expectedEmptyDir *corev1.EmptyDirVolu Expect(emptyDir.SizeLimit).To(Equal(expectedEmptyDir.SizeLimit)) } -func (t *cryostatTestInput) expectInMemoryDatabase() { - deployment := &appsv1.Deployment{} - err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name, Namespace: t.Namespace}, deployment) - Expect(err).ToNot(HaveOccurred()) - - containers := deployment.Spec.Template.Spec.Containers - coreContainer := containers[0] - Expect(coreContainer.Env).ToNot(ContainElements(t.DatabaseConfigEnvironmentVariables())) -} - func (t *cryostatTestInput) expectGrafanaSecret() { secret := &corev1.Secret{} err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-grafana-basic", Namespace: t.Namespace}, secret) @@ -2485,11 +2452,22 @@ func (t *cryostatTestInput) expectGrafanaSecret() { func (t *cryostatTestInput) expectCredentialsDatabaseSecret() { secret := &corev1.Secret{} - err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-jmx-credentials-db", Namespace: t.Namespace}, secret) + err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-db", Namespace: t.Namespace}, secret) + Expect(err).ToNot(HaveOccurred()) + + // Compare to desired spec + expectedSecret := t.NewDatabaseSecret() + t.checkMetadata(secret, expectedSecret) + Expect(secret.StringData).To(Equal(expectedSecret.StringData)) +} + +func (t *cryostatTestInput) expectStorageSecret() { + secret := &corev1.Secret{} + err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-storage-secret-key", Namespace: t.Namespace}, secret) Expect(err).ToNot(HaveOccurred()) // Compare to desired spec - expectedSecret := t.NewCredentialsDatabaseSecret() + expectedSecret := t.NewStorageSecret() t.checkMetadata(secret, expectedSecret) Expect(secret.StringData).To(Equal(expectedSecret.StringData)) } @@ -2499,6 +2477,7 @@ func (t *cryostatTestInput) expectJMXSecret() { err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-jmx-auth", Namespace: t.Namespace}, secret) Expect(err).ToNot(HaveOccurred()) + // Compare to desired spec expectedSecret := t.NewJMXSecret() t.checkMetadata(secret, expectedSecret) Expect(secret.StringData).To(Equal(expectedSecret.StringData)) @@ -2715,7 +2694,6 @@ func (t *cryostatTestInput) checkMainPodTemplate(deployment *appsv1.Deployment, ingress := !t.OpenShift && cr.Spec.NetworkOptions != nil && cr.Spec.NetworkOptions.CoreConfig != nil && cr.Spec.NetworkOptions.CoreConfig.IngressSpec != nil emptyDir := cr.Spec.StorageOptions != nil && cr.Spec.StorageOptions.EmptyDir != nil && cr.Spec.StorageOptions.EmptyDir.Enabled - builtInDiscoveryDisabled := cr.Spec.TargetDiscoveryOptions != nil && cr.Spec.TargetDiscoveryOptions.BuiltInDiscoveryDisabled hasPortConfig := cr.Spec.TargetDiscoveryOptions != nil && len(cr.Spec.TargetDiscoveryOptions.DiscoveryPortNames) > 0 && len(cr.Spec.TargetDiscoveryOptions.DiscoveryPortNumbers) > 0 @@ -2727,7 +2705,6 @@ func (t *cryostatTestInput) checkMainPodTemplate(deployment *appsv1.Deployment, t.checkCoreContainer(&coreContainer, ingress, reportsUrl, cr.Spec.AuthProperties != nil, emptyDir, - builtInDiscoveryDisabled, hasPortConfig, builtInPortConfigDisabled, dbSecretProvided, @@ -2870,45 +2847,10 @@ func (t *cryostatTestInput) checkDeploymentHasTemplates() { Expect(volumeMounts).To(ConsistOf(expectedVolumeMounts)) } -func (t *cryostatTestInput) checkDeploymentHasAuthProperties() { - deployment := &appsv1.Deployment{} - err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name, Namespace: t.Namespace}, deployment) - Expect(err).ToNot(HaveOccurred()) - - volumes := deployment.Spec.Template.Spec.Volumes - expectedVolumes := t.NewVolumeWithAuthProperties() - Expect(volumes).To(ConsistOf(expectedVolumes)) - - coreContainer := deployment.Spec.Template.Spec.Containers[0] - - volumeMounts := coreContainer.VolumeMounts - expectedVolumeMounts := t.NewVolumeMountsWithAuthProperties() - Expect(volumeMounts).To(ConsistOf(expectedVolumeMounts)) - Expect(coreContainer.Env).To(ConsistOf(t.NewCoreEnvironmentVariables("", true, false, false, false, false, false, false))) -} - -func (t *cryostatTestInput) checkDeploymentHasNoAuthProperties() { - deployment := &appsv1.Deployment{} - err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name, Namespace: t.Namespace}, deployment) - Expect(err).ToNot(HaveOccurred()) - - volumes := deployment.Spec.Template.Spec.Volumes - expectedVolumes := t.NewVolumes() - Expect(volumes).ToNot(ContainElements(t.NewAuthPropertiesVolume())) - Expect(volumes).To(ConsistOf(expectedVolumes)) - - coreContainer := deployment.Spec.Template.Spec.Containers[0] - - volumeMounts := coreContainer.VolumeMounts - expectedVolumeMounts := t.NewCoreVolumeMounts() - Expect(volumeMounts).ToNot(ContainElement(t.NewAuthPropertiesVolumeMount())) - Expect(volumeMounts).To(ConsistOf(expectedVolumeMounts)) -} - func (t *cryostatTestInput) checkCoreContainer(container *corev1.Container, ingress bool, reportsUrl string, authProps bool, emptyDir bool, - builtInDiscoveryDisabled bool, hasPortConfig bool, builtInPortConfigDisabled bool, + hasPortConfig bool, builtInPortConfigDisabled bool, dbSecretProvided bool, resources *corev1.ResourceRequirements, securityContext *corev1.SecurityContext) { @@ -2919,7 +2861,7 @@ func (t *cryostatTestInput) checkCoreContainer(container *corev1.Container, ingr Expect(container.Image).To(Equal(*t.EnvCoreImageTag)) } Expect(container.Ports).To(ConsistOf(t.NewCorePorts())) - Expect(container.Env).To(ConsistOf(t.NewCoreEnvironmentVariables(reportsUrl, authProps, ingress, emptyDir, builtInDiscoveryDisabled, hasPortConfig, builtInPortConfigDisabled, dbSecretProvided))) + Expect(container.Env).To(ConsistOf(t.NewCoreEnvironmentVariables(reportsUrl, authProps, ingress, emptyDir, hasPortConfig, builtInPortConfigDisabled, dbSecretProvided))) Expect(container.EnvFrom).To(ConsistOf(t.NewCoreEnvFromSource())) Expect(container.VolumeMounts).To(ConsistOf(t.NewCoreVolumeMounts())) Expect(container.LivenessProbe).To(Equal(t.NewCoreLivenessProbe())) @@ -2979,6 +2921,22 @@ func (t *cryostatTestInput) checkReportsContainer(container *corev1.Container, r test.ExpectResourceRequirements(&container.Resources, resources) } +// func (t *cryostatTestInput) checkStorageContainer(container *corev1.Container, resources *corev1.ResourceRequirements, securityContext *corev1.SecurityContext) { +// Expect(container.Name).To(Equal(t.Name + "-storage")) +// if t.EnvReportsImageTag == nil { +// Expect(container.Image).To(HavePrefix("quay.io/cryostat/cryostat-storage:")) +// } else { +// Expect(container.Image).To(Equal(*t.EnvReportsImageTag)) +// } +// Expect(container.Ports).To(ConsistOf(t.NewStoragePorts())) +// Expect(container.Env).To(ConsistOf(t.NewStorageEnvironmentVariables(resources))) +// Expect(container.VolumeMounts).To(ConsistOf(t.NewReportsVolumeMounts())) +// Expect(container.LivenessProbe).To(Equal(t.NewReportsLivenessProbe())) +// Expect(container.SecurityContext).To(Equal(securityContext)) + +// test.ExpectResourceRequirements(&container.Resources, resources) +// } + func (t *cryostatTestInput) checkCoreHasEnvironmentVariables(expectedEnvVars []corev1.EnvVar) { deployment := &appsv1.Deployment{} err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name, Namespace: t.Namespace}, deployment) diff --git a/internal/controllers/secrets.go b/internal/controllers/secrets.go index 278ff5813..b2dc3209c 100644 --- a/internal/controllers/secrets.go +++ b/internal/controllers/secrets.go @@ -29,7 +29,10 @@ func (r *Reconciler) reconcileSecrets(ctx context.Context, cr *model.CryostatIns if err := r.reconcileGrafanaSecret(ctx, cr); err != nil { return err } - if err := r.reconcileDatabaseSecret(ctx, cr); err != nil { + if err := r.reconcileDatabaseConnectionSecret(ctx, cr); err != nil { + return err + } + if err := r.reconcileStorageSecret(ctx, cr); err != nil { return err } return r.reconcileJMXSecret(ctx, cr) @@ -98,12 +101,15 @@ func (r *Reconciler) reconcileJMXSecret(ctx context.Context, cr *model.CryostatI // databaseSecretNameSuffix is the suffix to be appended to the name of a // Cryostat CR to name its credentials database secret -const databaseSecretNameSuffix = "-jmx-credentials-db" +const databaseSecretNameSuffix = "-db" + +// databaseSecretConnectionPassKey indexes the database connection password within the Cryostat database Secret +const databaseSecretConnectionPassKey = "CONNECTION_KEY" -// dbSecretUserKey indexes the password within the Cryostat credentials database Secret -const databaseSecretPassKey = "CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD" +// databaseSecretEncryptionKey indexes the database encryption key within the Cryostat database Secret +const databaseSecretEncryptionKey = "ENCRYPTION_KEY" -func (r *Reconciler) reconcileDatabaseSecret(ctx context.Context, cr *model.CryostatInstance) error { +func (r *Reconciler) reconcileDatabaseConnectionSecret(ctx context.Context, cr *model.CryostatInstance) error { secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: cr.Name + databaseSecretNameSuffix, @@ -123,7 +129,41 @@ func (r *Reconciler) reconcileDatabaseSecret(ctx context.Context, cr *model.Cryo // Password is generated, so don't regenerate it when updating if secret.CreationTimestamp.IsZero() { - secret.StringData[databaseSecretPassKey] = r.GenPasswd(32) + secret.StringData[databaseSecretConnectionPassKey] = r.GenPasswd(32) + secret.StringData[databaseSecretEncryptionKey] = r.GenPasswd(32) + } + return nil + }) +} + +// storageSecretNameSuffix is the suffix to be appended to the name of a +// Cryostat CR to name its object storage secret +const storageSecretNameSuffix = "-storage-secret-key" + +// storageSecretUserKey indexes the password within the Cryostat storage Secret +const storageSecretPassKey = "SECRET_KEY" + +func (r *Reconciler) reconcileStorageSecret(ctx context.Context, cr *model.CryostatInstance) error { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.Name + storageSecretNameSuffix, + Namespace: cr.InstallNamespace, + }, + } + + // secretProvided := cr.Spec.JmxCredentialsDatabaseOptions != nil && cr.Spec.JmxCredentialsDatabaseOptions.DatabaseSecretName != nil + // if secretProvided { + // return nil // Do not delete default secret to allow reverting to use default if needed + // } + + return r.createOrUpdateSecret(ctx, secret, cr.Object, func() error { + if secret.StringData == nil { + secret.StringData = map[string]string{} + } + + // Password is generated, so don't regenerate it when updating + if secret.CreationTimestamp.IsZero() { + secret.StringData[storageSecretPassKey] = r.GenPasswd(32) } return nil }) diff --git a/internal/test/reconciler.go b/internal/test/reconciler.go index 44428d576..714610ab8 100644 --- a/internal/test/reconciler.go +++ b/internal/test/reconciler.go @@ -29,6 +29,8 @@ type TestReconcilerConfig struct { EnvDisableTLS *bool EnvCoreImageTag *string EnvDatasourceImageTag *string + EnvStorageImageTag *string + EnvDatabaseImageTag *string EnvGrafanaImageTag *string EnvReportsImageTag *string GeneratedPasswords []string @@ -61,6 +63,12 @@ func newTestOSUtils(config *TestReconcilerConfig) *testOSUtils { if config.EnvGrafanaImageTag != nil { envs["RELATED_IMAGE_GRAFANA"] = *config.EnvGrafanaImageTag } + if config.EnvStorageImageTag != nil { + envs["RELATED_IMAGE_STORAGE"] = *config.EnvStorageImageTag + } + if config.EnvDatabaseImageTag != nil { + envs["RELATED_IMAGE_DATABASE"] = *config.EnvDatabaseImageTag + } if config.EnvReportsImageTag != nil { envs["RELATED_IMAGE_REPORTS"] = *config.EnvReportsImageTag } diff --git a/internal/test/resources.go b/internal/test/resources.go index 3454d6da8..a7584499c 100644 --- a/internal/test/resources.go +++ b/internal/test/resources.go @@ -500,16 +500,6 @@ func (r *TestResources) NewCryostatWithLowResourceLimit() *model.CryostatInstanc return cr } -func (r *TestResources) NewCryostatWithAuthProperties() *model.CryostatInstance { - cr := r.NewCryostat() - cr.Spec.AuthProperties = &operatorv1beta2.AuthorizationProperties{ - ConfigMapName: "authConfigMapName", - Filename: "auth.properties", - ClusterRoleName: "custom-auth-cluster-role", - } - return cr -} - func (r *TestResources) NewCryostatWithBuiltInDiscoveryDisabled() *model.CryostatInstance { cr := r.NewCryostat() cr.Spec.TargetDiscoveryOptions = &operatorv1beta2.TargetDiscoveryOptions{ @@ -936,26 +926,40 @@ func (r *TestResources) OtherGrafanaSecret() *corev1.Secret { } } -func (r *TestResources) NewCredentialsDatabaseSecret() *corev1.Secret { +func (r *TestResources) NewDatabaseSecret() *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: r.Name + "-jmx-credentials-db", + Name: r.Name + "-db", Namespace: r.Namespace, }, StringData: map[string]string{ - "CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD": "credentials_database", + "CONNECTION_KEY": "credentials_database", + "ENCRYPTION_KEY": "encryption_key", }, } } -func (r *TestResources) OtherCredentialsDatabaseSecret() *corev1.Secret { +func (r *TestResources) NewStorageSecret() *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: r.Name + "-jmx-credentials-db", + Name: r.Name + "-storage-secret-key", Namespace: r.Namespace, }, StringData: map[string]string{ - "CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD": "other-pass", + "SECRET_KEY": "object_storage", + }, + } +} + +func (r *TestResources) OtherDatabaseSecret() *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: r.Name + "-db", + Namespace: r.Namespace, + }, + StringData: map[string]string{ + "CONNECTION_KEY": "other-pass", + "ENCRYPTION_KEY": "other-key", }, } } @@ -1258,7 +1262,7 @@ func (r *TestResources) NewGrafanaPorts() []corev1.ContainerPort { func (r *TestResources) NewDatasourcePorts() []corev1.ContainerPort { return []corev1.ContainerPort{ { - ContainerPort: 8080, + ContainerPort: 8989, }, } } @@ -1271,13 +1275,87 @@ func (r *TestResources) NewReportsPorts() []corev1.ContainerPort { } } +func (r *TestResources) NewStoragePorts() []corev1.ContainerPort { + return []corev1.ContainerPort{ + { + ContainerPort: 8080, + }, + { + ContainerPort: 8333, + }, + { + ContainerPort: 8888, + }, + } +} + func (r *TestResources) NewCoreEnvironmentVariables(reportsUrl string, authProps bool, ingress bool, - emptyDir bool, builtInDiscoveryDisabled bool, hasPortConfig bool, builtInPortConfigDisabled bool, dbSecretProvided bool) []corev1.EnvVar { + emptyDir bool, hasPortConfig bool, builtInPortConfigDisabled bool, dbSecretProvided bool) []corev1.EnvVar { envs := []corev1.EnvVar{ { - Name: "CRYOSTAT_WEB_PORT", + Name: "QUARKUS_HTTP_HOST", + Value: "localhost", + }, + { + Name: "QUARKUS_HTTP_PORT", Value: "8181", }, + { + Name: "QUARKUS_HTTP_PROXY_PROXY_ADDRESS_FORWARDING", + Value: "true", + }, + { + Name: "QUARKUS_HTTP_PROXY_ALLOW_X_FORWARDED", + Value: "true", + }, + { + Name: "QUARKUS_HTTP_PROXY_ENABLE_FORWARDED_HOST", + Value: "true", + }, + { + Name: "QUARKUS_HTTP_PROXY_ENABLE_FORWARDED_PREFIX", + Value: "true", + }, + { + Name: "QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION", + Value: "drop-and-create", + }, + { + Name: "QUARKUS_DATASOURCE_USERNAME", + Value: "cryostat3", + }, + { + Name: "QUARKUS_DATASOURCE_JDBC_URL", + Value: "jdbc:postgresql://localhost:5432/cryostat3", + }, + { + Name: "STORAGE_BUCKETS_ARCHIVE_NAME", + Value: "archivedrecordings", + }, + { + Name: "QUARKUS_S3_ENDPOINT_OVERRIDE", + Value: "http://localhost:8333", + }, + { + Name: "QUARKUS_S3_PATH_STYLE_ACCESS", + Value: "true", + }, + { + Name: "QUARKUS_S3_AWS_REGION", + Value: "us-east-1", + }, + { + Name: "QUARKUS_S3_AWS_CREDENTIALS_TYPE", + Value: "static", + }, + { + Name: "QUARKUS_S3_CREDENTIALS_STATIC_PROVIDER_ACCESS_KEY_ID", + Value: "cryostat", + }, + { + Name: "AWS_ACCESS_KEY_ID", + Value: "$(QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_ACCESS_KEY_ID)", + }, { Name: "CRYOSTAT_CONFIG_PATH", Value: "/opt/cryostat.d/conf.d", @@ -1303,46 +1381,65 @@ func (r *TestResources) NewCoreEnvironmentVariables(reportsUrl string, authProps Value: "false", }, { - Name: "CRYOSTAT_TARGET_CACHE_SIZE", + Name: "CRYOSTAT_CONNECTIONS_MAX_OPEN", Value: "-1", }, { - Name: "CRYOSTAT_TARGET_CACHE_TTL", + Name: "CRYOSTAT_CONNECTIONS_TTL", Value: "10", }, { Name: "CRYOSTAT_K8S_NAMESPACES", Value: strings.Join(r.TargetNamespaces, ","), }, + { + Name: "GRAFANA_DATASOURCE_URL", + Value: "http://127.0.0.1:8989", + }, + { + Name: "QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_ACCESS_KEY_ID", + Value: "cryostat", + }, + { + Name: "AWS_SECRET_ACCESS_KEY", + Value: "$(QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_SECRET_ACCESS_KEY)", + }, } - envs = append(envs, r.NewTargetDiscoveryEnvVar(builtInDiscoveryDisabled, hasPortConfig, builtInPortConfigDisabled)...) - - if !emptyDir { - envs = append(envs, r.DatabaseConfigEnvironmentVariables()...) - } + envs = append(envs, r.NewTargetDiscoveryEnvVar(hasPortConfig, builtInPortConfigDisabled)...) optional := false - secretName := r.NewCredentialsDatabaseSecret().Name + secretName := r.NewDatabaseSecret().Name if dbSecretProvided { secretName = providedDatabaseSecretName } envs = append(envs, corev1.EnvVar{ - Name: "CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD", + Name: "QUARKUS_DATASOURCE_PASSWORD", ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ Name: secretName, }, - Key: "CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD", + Key: "CONNECTION_KEY", Optional: &optional, }, }, }, - corev1.EnvVar{ - Name: "GRAFANA_DATASOURCE_URL", - Value: "http://127.0.0.1:8080", + ) + + secretName = r.NewStorageSecret().Name + envs = append(envs, corev1.EnvVar{ + Name: "QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_SECRET_ACCESS_KEY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "SECRET_KEY", + Optional: &optional, + }, }, + }, ) if !r.TLS { @@ -1365,48 +1462,16 @@ func (r *TestResources) NewCoreEnvironmentVariables(reportsUrl string, authProps }) } - if r.OpenShift { - envs = append(envs, - corev1.EnvVar{ - Name: "CRYOSTAT_PLATFORM", - Value: "io.cryostat.platform.internal.OpenShiftPlatformStrategy", - }, - corev1.EnvVar{ - Name: "CRYOSTAT_AUTH_MANAGER", - Value: "io.cryostat.net.openshift.OpenShiftAuthManager", - }, - corev1.EnvVar{ - Name: "CRYOSTAT_OAUTH_CLIENT_ID", - Value: r.Name, - }, - corev1.EnvVar{ - Name: "CRYOSTAT_BASE_OAUTH_ROLE", - Value: "cryostat-operator-oauth-client", - }) - - if authProps { - envs = append(envs, corev1.EnvVar{ - Name: "CRYOSTAT_CUSTOM_OAUTH_ROLE", - Value: "custom-auth-cluster-role", - }) - } - envs = append(envs, r.newNetworkEnvironmentVariables()...) - } else if ingress { // On Kubernetes + if r.OpenShift || ingress { envs = append(envs, r.newNetworkEnvironmentVariables()...) } if reportsUrl != "" { envs = append(envs, corev1.EnvVar{ - Name: "CRYOSTAT_REPORT_GENERATOR", + Name: "CRYOSTAT_SERVICES_REPORTS_URL", Value: reportsUrl, }) - } else { - envs = append(envs, - corev1.EnvVar{ - Name: "CRYOSTAT_REPORT_GENERATION_MAX_HEAP", - Value: "200", - }) } if len(r.InsightsURL) > 0 { @@ -1419,35 +1484,6 @@ func (r *TestResources) NewCoreEnvironmentVariables(reportsUrl string, authProps return envs } -func (r *TestResources) DatabaseConfigEnvironmentVariables() []corev1.EnvVar { - return []corev1.EnvVar{ - { - Name: "CRYOSTAT_JDBC_URL", - Value: "jdbc:h2:file:/opt/cryostat.d/conf.d/h2;INIT=create domain if not exists jsonb as varchar", - }, - { - Name: "CRYOSTAT_HBM2DDL", - Value: "update", - }, - { - Name: "CRYOSTAT_JDBC_DRIVER", - Value: "org.h2.Driver", - }, - { - Name: "CRYOSTAT_HIBERNATE_DIALECT", - Value: "org.hibernate.dialect.H2Dialect", - }, - { - Name: "CRYOSTAT_JDBC_USERNAME", - Value: r.Name, - }, - { - Name: "CRYOSTAT_JDBC_PASSWORD", - Value: r.Name, - }, - } -} - func (r *TestResources) newNetworkEnvironmentVariables() []corev1.EnvVar { envs := []corev1.EnvVar{ { @@ -1501,7 +1537,7 @@ func (r *TestResources) NewGrafanaEnvironmentVariables() []corev1.EnvVar { envs := []corev1.EnvVar{ { Name: "JFR_DATASOURCE_URL", - Value: "http://127.0.0.1:8080", + Value: "http://127.0.0.1:8989", }, } if r.TLS { @@ -1522,9 +1558,13 @@ func (r *TestResources) NewGrafanaEnvironmentVariables() []corev1.EnvVar { func (r *TestResources) NewDatasourceEnvironmentVariables() []corev1.EnvVar { return []corev1.EnvVar{ { - Name: "LISTEN_HOST", + Name: "QUARKUS_HTTP_HOST", Value: "127.0.0.1", }, + { + Name: "QUARKUS_HTTP_PORT", + Value: "8989", + }, } } @@ -1569,6 +1609,19 @@ func (r *TestResources) NewReportsEnvironmentVariables(resources *corev1.Resourc return envs } +func (r *TestResources) NewStorageEnvironmentVariables() []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: "QUARKUS_HTTP_HOST", + Value: "127.0.0.1", + }, + { + Name: "QUARKUS_HTTP_PORT", + Value: "8989", + }, + } +} + func (r *TestResources) NewCoreEnvFromSource() []corev1.EnvFromSource { envsFrom := []corev1.EnvFromSource{ { @@ -1603,38 +1656,22 @@ func (r *TestResources) NewGrafanaEnvFromSource() []corev1.EnvFromSource { } } -func (r *TestResources) NewReportSubprocessHeapEnv() []corev1.EnvVar { - return []corev1.EnvVar{ - { - Name: "CRYOSTAT_REPORT_GENERATION_MAX_HEAP", - Value: "500", - }, - } -} - func (r *TestResources) NewJmxCacheOptionsEnv() []corev1.EnvVar { return []corev1.EnvVar{ { - Name: "CRYOSTAT_TARGET_CACHE_SIZE", + Name: "CRYOSTAT_CONNECTIONS_MAX_OPEN", Value: "10", }, { - Name: "CRYOSTAT_TARGET_CACHE_TTL", + Name: "CRYOSTAT_CONNECTIONS_TTL", Value: "20", }, } } -func (r *TestResources) NewTargetDiscoveryEnvVar(builtInDiscoveryDisabled bool, hasPortConfig bool, builtInPortConfigDisabled bool) []corev1.EnvVar { +func (r *TestResources) NewTargetDiscoveryEnvVar(hasPortConfig bool, builtInPortConfigDisabled bool) []corev1.EnvVar { envs := make([]corev1.EnvVar, 0) - if builtInDiscoveryDisabled { - envs = append(envs, corev1.EnvVar{ - Name: "CRYOSTAT_DISABLE_BUILTIN_DISCOVERY", - Value: "true", - }) - } - if hasPortConfig { envs = append(envs, corev1.EnvVar{ @@ -1785,15 +1822,11 @@ func (r *TestResources) NewCoreStartupProbe() *corev1.Probe { } func (r *TestResources) newCoreProbeHandler() corev1.ProbeHandler { - protocol := corev1.URISchemeHTTPS - if !r.TLS { - protocol = corev1.URISchemeHTTP - } return corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.IntOrString{IntVal: 8181}, Path: "/health/liveness", - Scheme: protocol, + Scheme: corev1.URISchemeHTTP, }, } } @@ -1818,7 +1851,7 @@ func (r *TestResources) NewDatasourceLivenessProbe() *corev1.Probe { return &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: []string{"curl", "--fail", "http://127.0.0.1:8080"}, + Command: []string{"curl", "--fail", "http://127.0.0.1:8989"}, }, }, } @@ -2512,26 +2545,6 @@ func (r *TestResources) OtherRole() *rbacv1.Role { } } -func (r *TestResources) NewAuthClusterRole() *rbacv1.ClusterRole { - return &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: "custom-auth-cluster-role", - }, - Rules: []rbacv1.PolicyRule{ - { - Verbs: []string{"get", "update", "patch", "delete"}, - APIGroups: []string{"group"}, - Resources: []string{"resources"}, - }, - { - Verbs: []string{"get", "update", "patch", "delete"}, - APIGroups: []string{"another_group"}, - Resources: []string{"another_resources"}, - }, - }, - } -} - func (r *TestResources) NewRoleBinding(ns string) *rbacv1.RoleBinding { return &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ @@ -2657,18 +2670,6 @@ func (r *TestResources) NewOtherTemplateConfigMap() *corev1.ConfigMap { } } -func (r *TestResources) NewAuthPropertiesConfigMap() *corev1.ConfigMap { - return &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "authConfigMapName", - Namespace: r.Namespace, - }, - Data: map[string]string{ - "auth.properties": "CRYOSTAT_RESOURCE=resources.group\nANOTHER_CRYOSTAT_RESOURCE=another_resources.another_group", - }, - } -} - func (r *TestResources) NewNamespace() *corev1.Namespace { return &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ diff --git a/internal/tools/const_generator.go b/internal/tools/const_generator.go index eadea2eeb..116c4e5ee 100644 --- a/internal/tools/const_generator.go +++ b/internal/tools/const_generator.go @@ -28,6 +28,8 @@ const coreImageEnv = "CORE_IMG" const datasourceImageEnv = "DATASOURCE_IMG" const grafanaImageEnv = "GRAFANA_IMG" const reportsImageEnv = "REPORTS_IMG" +const storageImageEnv = "STORAGE_IMG" +const databaseImageEnv = "DATABASE_IMG" // This program generates a const_generated.go file containing image tag // constants for each container image deployed by the operator, along with @@ -41,6 +43,8 @@ func main() { DatasourceImageTag string GrafanaImageTag string ReportsImageTag string + StorageImageTag string + DatabaseImageTag string }{ AppName: getEnvVar(appNameEnv), OperatorVersion: getEnvVar(operatorVersionEnv), @@ -48,6 +52,8 @@ func main() { DatasourceImageTag: getEnvVar(datasourceImageEnv), GrafanaImageTag: getEnvVar(grafanaImageEnv), ReportsImageTag: getEnvVar(reportsImageEnv), + StorageImageTag: getEnvVar(storageImageEnv), + DatabaseImageTag: getEnvVar(databaseImageEnv), } // Create the source file to generate @@ -91,4 +97,10 @@ const DefaultGrafanaImageTag = "{{ .GrafanaImageTag }}" // Default image tag for the Grafana dashboard image const DefaultReportsImageTag = "{{ .ReportsImageTag }}" + +// Default image tag for the Storage image +const DefaultStorageImageTag = "{{ .StorageImageTag }}" + +// Default image tag for the Database image +const DefaultDatabaseImageTag = "{{ .DatabaseImageTag }}" `))