From 78d5118a7c32809c59bc0bd399dcf97e9df2398f Mon Sep 17 00:00:00 2001 From: Saswata Mukherjee Date: Mon, 17 Jun 2024 16:27:01 +0100 Subject: [PATCH] [ACM-8879]: Add initial implementation for T-shirt sizing ACM o11y resources (#1295) * Refactor the existing resource configuration into separate file and make code clearer Signed-off-by: Saswata Mukherjee * Add initial implementation for T-Shirt Sizing ACM o11y Signed-off-by: Saswata Mukherjee * Make sure replicas are also t-shirt sized Signed-off-by: Saswata Mukherjee * make lint Signed-off-by: Saswata Mukherjee * Fix test Signed-off-by: Saswata Mukherjee * Implement suggestions Signed-off-by: Saswata Mukherjee * Generate bundle Signed-off-by: Saswata Mukherjee * Unify and rename to InstanceSize; Add default & minimal options Signed-off-by: Saswata Mukherjee * Add in Thanos t-shirt size values Signed-off-by: Saswata Mukherjee * Add in API/RBAC Query Proxy and AM t-shirt size value Signed-off-by: Saswata Mukherjee * Add cache values Signed-off-by: Saswata Mukherjee * Remove 8xlarge size Signed-off-by: Saswata Mukherjee * Add spec and spec update tests Signed-off-by: Saswata Mukherjee * Keep memcached exporter at 5m Signed-off-by: Saswata Mukherjee --------- Signed-off-by: Saswata Mukherjee --- .../multiclusterobservability_types.go | 7 + ...gement.io_multiclusterobservabilities.yaml | 12 + ...gement.io_multiclusterobservabilities.yaml | 12 + .../observatorium.go | 52 +- .../observatorium_test.go | 162 ++++++ .../controllers/placementrule/manifestwork.go | 2 +- .../pkg/config/config.go | 447 +--------------- .../pkg/config/config_test.go | 374 ------------- .../pkg/config/resources.go | 374 +++++++++++++ .../pkg/config/resources_map.go | 496 ++++++++++++++++++ .../pkg/config/resources_test.go | 388 ++++++++++++++ .../pkg/rendering/renderer.go | 7 +- .../pkg/rendering/renderer_alertmanager.go | 4 +- .../pkg/rendering/renderer_grafana.go | 4 +- .../pkg/rendering/renderer_proxy.go | 13 +- .../pkg/rendering/renderer_thanos.go | 2 +- 16 files changed, 1501 insertions(+), 855 deletions(-) create mode 100644 operators/multiclusterobservability/pkg/config/resources.go create mode 100644 operators/multiclusterobservability/pkg/config/resources_map.go create mode 100644 operators/multiclusterobservability/pkg/config/resources_test.go diff --git a/operators/multiclusterobservability/api/v1beta2/multiclusterobservability_types.go b/operators/multiclusterobservability/api/v1beta2/multiclusterobservability_types.go index de03d3b35..10481ded9 100644 --- a/operators/multiclusterobservability/api/v1beta2/multiclusterobservability_types.go +++ b/operators/multiclusterobservability/api/v1beta2/multiclusterobservability_types.go @@ -16,6 +16,9 @@ type MultiClusterObservabilitySpec struct { // Advanced configurations for observability // +optional AdvancedConfig *AdvancedConfig `json:"advanced,omitempty"` + // Size read and write paths of your Observability instance + // +optional + InstanceSize TShirtSize `json:"instanceSize,omitempty"` // Enable or disable the downsample. // +optional // +kubebuilder:default:=true @@ -41,6 +44,10 @@ type MultiClusterObservabilitySpec struct { ObservabilityAddonSpec *observabilityshared.ObservabilityAddonSpec `json:"observabilityAddonSpec"` } +// T Shirt size class for a particular o11y resource. +// +kubebuilder:validation:Enum:={"default","minimal","small","medium","large","xlarge","2xlarge","4xlarge"} +type TShirtSize string + type AdvancedConfig struct { // CustomObservabilityHubURL overrides the endpoint used by the metrics-collector to send // metrics to the hub server. diff --git a/operators/multiclusterobservability/bundle/manifests/observability.open-cluster-management.io_multiclusterobservabilities.yaml b/operators/multiclusterobservability/bundle/manifests/observability.open-cluster-management.io_multiclusterobservabilities.yaml index baa1f724d..aa6fe6a56 100644 --- a/operators/multiclusterobservability/bundle/manifests/observability.open-cluster-management.io_multiclusterobservabilities.yaml +++ b/operators/multiclusterobservability/bundle/manifests/observability.open-cluster-management.io_multiclusterobservabilities.yaml @@ -5904,6 +5904,18 @@ spec: imagePullSecret: description: Pull secret of the MultiClusterObservability images type: string + instanceSize: + description: Size read and write paths of your Observability instance + enum: + - default + - minimal + - small + - medium + - large + - xlarge + - 2xlarge + - 4xlarge + type: string nodeSelector: additionalProperties: type: string diff --git a/operators/multiclusterobservability/config/crd/bases/observability.open-cluster-management.io_multiclusterobservabilities.yaml b/operators/multiclusterobservability/config/crd/bases/observability.open-cluster-management.io_multiclusterobservabilities.yaml index c6b348139..82b7fa96e 100644 --- a/operators/multiclusterobservability/config/crd/bases/observability.open-cluster-management.io_multiclusterobservabilities.yaml +++ b/operators/multiclusterobservability/config/crd/bases/observability.open-cluster-management.io_multiclusterobservabilities.yaml @@ -9497,6 +9497,18 @@ spec: imagePullSecret: description: Pull secret of the MultiClusterObservability images type: string + instanceSize: + description: Size read and write paths of your Observability instance + enum: + - default + - minimal + - small + - medium + - large + - xlarge + - 2xlarge + - 4xlarge + type: string nodeSelector: additionalProperties: type: string diff --git a/operators/multiclusterobservability/controllers/multiclusterobservability/observatorium.go b/operators/multiclusterobservability/controllers/multiclusterobservability/observatorium.go index 0cc5d1a8e..1c414cc6b 100644 --- a/operators/multiclusterobservability/controllers/multiclusterobservability/observatorium.go +++ b/operators/multiclusterobservability/controllers/multiclusterobservability/observatorium.go @@ -521,9 +521,9 @@ func newAPISpec(c client.Client, mco *mcov1beta2.MultiClusterObservability) (obs apiSpec.RBAC = newAPIRBAC() apiSpec.Tenants = newAPITenants() apiSpec.TLS = newAPITLS() - apiSpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ObservatoriumAPI, mco.Spec.AdvancedConfig) + apiSpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ObservatoriumAPI, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { - apiSpec.Resources = mcoconfig.GetResources(mcoconfig.ObservatoriumAPI, mco.Spec.AdvancedConfig) + apiSpec.Resources = mcoconfig.GetResources(mcoconfig.ObservatoriumAPI, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) } // set the default observatorium components' image apiSpec.Image = mcoconfig.DefaultImgRepository + "/" + mcoconfig.ObservatoriumAPIImgName + @@ -619,16 +619,17 @@ func newReceiversSpec( receSpec.Retention = mcoconfig.RetentionInLocal } - receSpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ThanosReceive, mco.Spec.AdvancedConfig) + receSpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ThanosReceive, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) if *receSpec.Replicas < 3 { receSpec.ReplicationFactor = receSpec.Replicas } else { - receSpec.ReplicationFactor = &mcoconfig.Replicas3 + var replicas3 int32 = 3 + receSpec.ReplicationFactor = &replicas3 } receSpec.ServiceMonitor = true if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { - receSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosReceive, mco.Spec.AdvancedConfig) + receSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosReceive, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) } receSpec.VolumeClaimTemplate = newVolumeClaimTemplate( mco.Spec.StorageConfig.ReceiveStorageSize, @@ -668,15 +669,18 @@ func newRuleSpec(mco *mcov1beta2.MultiClusterObservability, scSelected string) o } else { ruleSpec.EvalInterval = fmt.Sprintf("%ds", mco.Spec.ObservabilityAddonSpec.Interval) } - ruleSpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ThanosRule, mco.Spec.AdvancedConfig) + ruleSpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ThanosRule, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) ruleSpec.ServiceMonitor = true if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { - ruleSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosRule, mco.Spec.AdvancedConfig) + ruleSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosRule, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) + if mco.Spec.InstanceSize == "" { + mco.Spec.InstanceSize = mcoconfig.Default + } ruleSpec.ReloaderResources = v1.ResourceRequirements{ Requests: v1.ResourceList{ - v1.ResourceName(v1.ResourceCPU): resource.MustParse(mcoconfig.ThanosRuleReloaderCPURequets), - v1.ResourceName(v1.ResourceMemory): resource.MustParse(mcoconfig.ThanosRuleReloaderMemoryRequets), + v1.ResourceName(v1.ResourceCPU): resource.MustParse(mcoconfig.ThanosRuleReloaderCPURequest[mco.Spec.InstanceSize]), + v1.ResourceName(v1.ResourceMemory): resource.MustParse(mcoconfig.ThanosRuleReloaderMemoryRequest[mco.Spec.InstanceSize]), }, } } @@ -750,14 +754,14 @@ func newRuleSpec(mco *mcov1beta2.MultiClusterObservability, scSelected string) o func newStoreSpec(mco *mcov1beta2.MultiClusterObservability, scSelected string) obsv1alpha1.StoreSpec { storeSpec := obsv1alpha1.StoreSpec{} if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { - storeSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosStoreShard, mco.Spec.AdvancedConfig) + storeSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosStoreShard, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) } storeSpec.VolumeClaimTemplate = newVolumeClaimTemplate( mco.Spec.StorageConfig.StoreStorageSize, scSelected) - storeSpec.Shards = mcoconfig.GetReplicas(mcoconfig.ThanosStoreShard, mco.Spec.AdvancedConfig) + storeSpec.Shards = mcoconfig.GetReplicas(mcoconfig.ThanosStoreShard, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) storeSpec.ServiceMonitor = true storeSpec.Cache = newMemCacheSpec(mcoconfig.ThanosStoreMemcached, mco) @@ -787,15 +791,15 @@ func newMemCacheSpec(component string, mco *mcov1beta2.MultiClusterObservability memCacheSpec.Image = mcoconfig.MemcachedImgRepo + "/" + mcoconfig.MemcachedImgName + ":" + mcoconfig.MemcachedImgTag memCacheSpec.Version = mcoconfig.MemcachedImgTag - memCacheSpec.Replicas = mcoconfig.GetReplicas(component, mco.Spec.AdvancedConfig) + memCacheSpec.Replicas = mcoconfig.GetReplicas(component, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) memCacheSpec.ServiceMonitor = true memCacheSpec.ExporterImage = mcoconfig.MemcachedExporterImgRepo + "/" + mcoconfig.MemcachedExporterImgName + ":" + mcoconfig.MemcachedExporterImgTag memCacheSpec.ExporterVersion = mcoconfig.MemcachedExporterImgTag if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { - memCacheSpec.Resources = mcoconfig.GetResources(component, mco.Spec.AdvancedConfig) - memCacheSpec.ExporterResources = mcoconfig.GetResources(mcoconfig.MemcachedExporter, mco.Spec.AdvancedConfig) + memCacheSpec.Resources = mcoconfig.GetResources(component, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) + memCacheSpec.ExporterResources = mcoconfig.GetResources(mcoconfig.MemcachedExporter, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) } found, image := mcoconfig.ReplaceImage(mco.Annotations, memCacheSpec.Image, mcoconfig.MemcachedImgName) @@ -851,10 +855,10 @@ func newThanosSpec(mco *mcov1beta2.MultiClusterObservability, scSelected string) func newQueryFrontendSpec(mco *mcov1beta2.MultiClusterObservability) obsv1alpha1.QueryFrontendSpec { queryFrontendSpec := obsv1alpha1.QueryFrontendSpec{} - queryFrontendSpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ThanosQueryFrontend, mco.Spec.AdvancedConfig) + queryFrontendSpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ThanosQueryFrontend, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) queryFrontendSpec.ServiceMonitor = true if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { - queryFrontendSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosQueryFrontend, mco.Spec.AdvancedConfig) + queryFrontendSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosQueryFrontend, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) } queryFrontendSpec.Cache = newMemCacheSpec(mcoconfig.ThanosQueryFrontendMemcached, mco) @@ -868,7 +872,7 @@ func newQueryFrontendSpec(mco *mcov1beta2.MultiClusterObservability) obsv1alpha1 func newQuerySpec(mco *mcov1beta2.MultiClusterObservability) obsv1alpha1.QuerySpec { querySpec := obsv1alpha1.QuerySpec{} - querySpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ThanosQuery, mco.Spec.AdvancedConfig) + querySpec.Replicas = mcoconfig.GetReplicas(mcoconfig.ThanosQuery, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) querySpec.ServiceMonitor = true // only set lookback-delta when the scrape interval * 2 is larger than 5 minute, // otherwise default value(5m) will be used. @@ -876,7 +880,7 @@ func newQuerySpec(mco *mcov1beta2.MultiClusterObservability) obsv1alpha1.QuerySp querySpec.LookbackDelta = fmt.Sprintf("%ds", mco.Spec.ObservabilityAddonSpec.Interval*2) } if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { - querySpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosQuery, mco.Spec.AdvancedConfig) + querySpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosQuery, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) } if mco.Spec.AdvancedConfig != nil && mco.Spec.AdvancedConfig.Query != nil && mco.Spec.AdvancedConfig.Query.ServiceAccountAnnotations != nil { @@ -902,13 +906,16 @@ func newReceiverControllerSpec(mco *mcov1beta2.MultiClusterObservability) obsv1a receiveControllerSpec.ServiceMonitor = true receiveControllerSpec.Version = mcoconfig.ThanosReceiveControllerImgTag if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { + if mco.Spec.InstanceSize == "" { + mco.Spec.InstanceSize = mcoconfig.Default + } receiveControllerSpec.Resources = v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceName(v1.ResourceCPU): resource.MustParse( - mcoconfig.ObservatoriumReceiveControllerCPURequets, + mcoconfig.ObservatoriumReceiveControllerCPURequest[mco.Spec.InstanceSize], ), v1.ResourceName(v1.ResourceMemory): resource.MustParse( - mcoconfig.ObservatoriumReceiveControllerMemoryRequets, + mcoconfig.ObservatoriumReceiveControllerMemoryRequest[mco.Spec.InstanceSize], ), }, } @@ -926,9 +933,10 @@ func newCompactSpec(mco *mcov1beta2.MultiClusterObservability, scSelected string compactSpec := obsv1alpha1.CompactSpec{} // Compactor, generally, does not need to be highly available. // Compactions are needed from time to time, only when new blocks appear. - compactSpec.Replicas = &mcoconfig.Replicas1 + var replicas1 int32 = 1 + compactSpec.Replicas = &replicas1 if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) { - compactSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosCompact, mco.Spec.AdvancedConfig) + compactSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosCompact, mco.Spec.InstanceSize, mco.Spec.AdvancedConfig) } compactSpec.ServiceMonitor = true compactSpec.EnableDownsampling = mco.Spec.EnableDownsampling diff --git a/operators/multiclusterobservability/controllers/multiclusterobservability/observatorium_test.go b/operators/multiclusterobservability/controllers/multiclusterobservability/observatorium_test.go index 7d07fa0c4..a38d06d71 100644 --- a/operators/multiclusterobservability/controllers/multiclusterobservability/observatorium_test.go +++ b/operators/multiclusterobservability/controllers/multiclusterobservability/observatorium_test.go @@ -93,6 +93,9 @@ func TestNewDefaultObservatoriumSpec(t *testing.T) { "write_key": []byte(`url: http://remotewrite/endpoint`), }, } + s := scheme.Scheme + mcov1beta2.SchemeBuilder.AddToScheme(s) + observatoriumv1alpha1.AddToScheme(s) objs := []runtime.Object{mco, writeStorageS} // Create a fake client to mock API calls. @@ -139,6 +142,89 @@ func TestNewDefaultObservatoriumSpec(t *testing.T) { } } +func TestNewDefaultObservatoriumSpecWithTShirtSize(t *testing.T) { + mco := &mcov1beta2.MultiClusterObservability{ + TypeMeta: metav1.TypeMeta{Kind: "MultiClusterObservability"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Annotations: map[string]string{ + mcoconfig.AnnotationKeyImageRepository: "quay.io:443/acm-d", + mcoconfig.AnnotationKeyImageTagSuffix: "tag", + }, + }, + Spec: mcov1beta2.MultiClusterObservabilitySpec{ + InstanceSize: mcoconfig.FourXLarge, + StorageConfig: &mcov1beta2.StorageConfig{ + MetricObjectStorage: &mcoshared.PreConfiguredStorage{ + Key: "key", + Name: "name", + TLSSecretName: "secret", + }, + WriteStorage: []*mcoshared.PreConfiguredStorage{ + { + Key: "write_key", + Name: "write_name", + }, + }, + StorageClass: storageClassName, + AlertmanagerStorageSize: "1Gi", + CompactStorageSize: "1Gi", + RuleStorageSize: "1Gi", + ReceiveStorageSize: "1Gi", + StoreStorageSize: "1Gi", + }, + ObservabilityAddonSpec: &mcoshared.ObservabilityAddonSpec{ + EnableMetrics: true, + Interval: 300, + }, + }, + } + + writeStorageS := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "write_name", + Namespace: mcoconfig.GetDefaultNamespace(), + }, + Type: "Opaque", + Data: map[string][]byte{ + "write_key": []byte(`url: http://remotewrite/endpoint`), + }, + } + s := scheme.Scheme + mcov1beta2.SchemeBuilder.AddToScheme(s) + observatoriumv1alpha1.AddToScheme(s) + + objs := []runtime.Object{mco, writeStorageS} + // Create a fake client to mock API calls. + cl := fake.NewClientBuilder().WithRuntimeObjects(objs...).Build() + + obs, err := newDefaultObservatoriumSpec(cl, mco, storageClassName, "") + if err != nil { + t.Errorf("failed to create obs spec") + } + + if obs.Thanos.Receivers.Resources.Requests.Cpu().String() != "10" || + obs.Thanos.Receivers.Resources.Requests.Memory().String() != "128Gi" || + *obs.Thanos.Receivers.Replicas != 12 || + obs.Thanos.Query.Resources.Requests.Cpu().String() != "7" || + obs.Thanos.Query.Resources.Requests.Memory().String() != "18Gi" || + *obs.Thanos.Query.Replicas != 10 || + obs.Thanos.QueryFrontend.Resources.Requests.Cpu().String() != "4" || + obs.Thanos.QueryFrontend.Resources.Requests.Memory().String() != "12Gi" || + *obs.Thanos.QueryFrontend.Replicas != 10 || + obs.Thanos.Compact.Resources.Requests.Cpu().String() != "6" || + obs.Thanos.Compact.Resources.Requests.Memory().String() != "18Gi" || + *obs.Thanos.Compact.Replicas != 1 || + obs.Thanos.Rule.Resources.Requests.Cpu().String() != "6" || + obs.Thanos.Rule.Resources.Requests.Memory().String() != "15Gi" || + *obs.Thanos.Rule.Replicas != 3 || + obs.Thanos.Store.Resources.Requests.Cpu().String() != "6" || + obs.Thanos.Store.Resources.Requests.Memory().String() != "20Gi" || + *obs.Thanos.Store.Shards != 6 { + t.Errorf("Failed t-shirt size for Obs Spec") + } +} + func TestUpdateObservatoriumCR(t *testing.T) { namespace := mcoconfig.GetDefaultNamespace() @@ -240,6 +326,82 @@ func TestUpdateObservatoriumCR(t *testing.T) { } +func TestTShirtSizeUpdateObservatoriumCR(t *testing.T) { + namespace := mcoconfig.GetDefaultNamespace() + + // A MultiClusterObservability object with metadata and spec. + mco := &mcov1beta2.MultiClusterObservability{ + TypeMeta: metav1.TypeMeta{Kind: "MultiClusterObservability"}, + ObjectMeta: metav1.ObjectMeta{ + Name: mcoconfig.GetDefaultCRName(), + }, + Spec: mcov1beta2.MultiClusterObservabilitySpec{ + InstanceSize: mcoconfig.Large, + StorageConfig: &mcov1beta2.StorageConfig{ + MetricObjectStorage: &mcoshared.PreConfiguredStorage{ + Key: "test", + Name: "test", + }, + StorageClass: storageClassName, + AlertmanagerStorageSize: "1Gi", + CompactStorageSize: "1Gi", + RuleStorageSize: "1Gi", + ReceiveStorageSize: "1Gi", + StoreStorageSize: "1Gi", + }, + ObservabilityAddonSpec: &mcoshared.ObservabilityAddonSpec{ + EnableMetrics: true, + Interval: 300, + }, + }, + } + // Register operator types with the runtime scheme. + s := scheme.Scheme + mcov1beta2.SchemeBuilder.AddToScheme(s) + observatoriumv1alpha1.AddToScheme(s) + + // Create a fake client to mock API calls. + // This should have no extra objects beyond the CMO CRD. + cl := fake.NewClientBuilder().WithRuntimeObjects(mco).Build() + mcoconfig.SetOperandNames(cl) + + _, err := GenerateObservatoriumCR(cl, s, mco) + if err != nil { + t.Errorf("Failed to create observatorium due to %v", err) + } + + // Check if this Observatorium CR already exists + createdObservatoriumCR := &observatoriumv1alpha1.Observatorium{} + cl.Get(context.TODO(), types.NamespacedName{ + Name: mcoconfig.GetDefaultCRName(), + Namespace: namespace, + }, createdObservatoriumCR) + + if createdObservatoriumCR.Spec.Thanos.Receivers.Resources.Requests.Cpu().String() != "5" || + createdObservatoriumCR.Spec.Thanos.Receivers.Resources.Requests.Memory().String() != "24Gi" || + *createdObservatoriumCR.Spec.Thanos.Receivers.Replicas != 6 { + t.Errorf("t-shirt size values for receive not correct") + } + + mco.Spec.InstanceSize = mcoconfig.TwoXLarge + _, err = GenerateObservatoriumCR(cl, s, mco) + if err != nil { + t.Errorf("Failed to update observatorium due to %v", err) + } + + updatedObservatorium := &observatoriumv1alpha1.Observatorium{} + cl.Get(context.TODO(), types.NamespacedName{ + Name: mcoconfig.GetDefaultCRName(), + Namespace: namespace, + }, updatedObservatorium) + + if updatedObservatorium.Spec.Thanos.Receivers.Resources.Requests.Cpu().String() != "6" || + updatedObservatorium.Spec.Thanos.Receivers.Resources.Requests.Memory().String() != "52Gi" || + *updatedObservatorium.Spec.Thanos.Receivers.Replicas != 12 { + t.Errorf("updated t-shirt size values for receive not correct") + } +} + func TestNoUpdateObservatoriumCR(t *testing.T) { var ( namespace = mcoconfig.GetDefaultNamespace() diff --git a/operators/multiclusterobservability/controllers/placementrule/manifestwork.go b/operators/multiclusterobservability/controllers/placementrule/manifestwork.go index 92f67f496..f1ee0fef6 100644 --- a/operators/multiclusterobservability/controllers/placementrule/manifestwork.go +++ b/operators/multiclusterobservability/controllers/placementrule/manifestwork.go @@ -971,7 +971,7 @@ func getObservabilityAddon(c client.Client, namespace string, Spec: mcoshared.ObservabilityAddonSpec{ EnableMetrics: mco.Spec.ObservabilityAddonSpec.EnableMetrics, Interval: mco.Spec.ObservabilityAddonSpec.Interval, - Resources: config.GetOBAResources(mco.Spec.ObservabilityAddonSpec), + Resources: config.GetOBAResources(mco.Spec.ObservabilityAddonSpec, mco.Spec.InstanceSize), }, }, nil } diff --git a/operators/multiclusterobservability/pkg/config/config.go b/operators/multiclusterobservability/pkg/config/config.go index 6a4378f5f..7e41d8ccb 100644 --- a/operators/multiclusterobservability/pkg/config/config.go +++ b/operators/multiclusterobservability/pkg/config/config.go @@ -20,14 +20,12 @@ import ( admissionregistrationv1 "k8s.io/api/admissionregistration/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/uuid" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - mcoshared "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/shared" observabilityv1beta2 "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/v1beta2" ) @@ -47,12 +45,11 @@ const ( OpenshiftIngressDefaultCertName = "router-certs-default" OpenshiftIngressRouteCAName = "router-ca" - AnnotationKeyImageRepository = "mco-imageRepository" - AnnotationKeyImageTagSuffix = "mco-imageTagSuffix" - AnnotationMCOPause = "mco-pause" - AnnotationMCOWithoutResourcesRequests = "mco-thanos-without-resources-requests" - AnnotationCertDuration = "mco-cert-duration" - AnnotationDisableMCOAlerting = "mco-disable-alerting" + AnnotationKeyImageRepository = "mco-imageRepository" + AnnotationKeyImageTagSuffix = "mco-imageTagSuffix" + AnnotationMCOPause = "mco-pause" + AnnotationCertDuration = "mco-cert-duration" + AnnotationDisableMCOAlerting = "mco-disable-alerting" MCHUpdatedRequestName = "mch-updated-request" MCOUpdatedRequestName = "mco-updated-request" @@ -162,56 +159,6 @@ const ( RBACQueryProxyImgName = "rbac-query-proxy" RBACQueryProxyKey = "rbac_query_proxy" - RBACQueryProxyCPURequets = "20m" - RBACQueryProxyMemoryRequets = "100Mi" - - GrafanaCPURequets = "4m" - GrafanaMemoryRequets = "100Mi" - GrafanaCPULimits = "500m" - GrafanaMemoryLimits = "1Gi" - - AlertmanagerCPURequets = "4m" - AlertmanagerMemoryRequets = "200Mi" - - ObservatoriumAPICPURequets = "20m" - ObservatoriumAPIMemoryRequets = "128Mi" - - ThanosQueryFrontendCPURequets = "100m" - ThanosQueryFrontendMemoryRequets = "256Mi" - - MemcachedExporterCPURequets = "5m" - MemcachedExporterMemoryRequets = "50Mi" - - ThanosQueryCPURequets = "300m" - ThanosQueryMemoryRequets = "1Gi" - - ThanosCompactCPURequets = "100m" - ThanosCompactMemoryRequets = "512Mi" - - ObservatoriumReceiveControllerCPURequets = "4m" - ObservatoriumReceiveControllerMemoryRequets = "32Mi" - - ThanosReceiveCPURequets = "300m" - ThanosReceiveMemoryRequets = "512Mi" - - ThanosRuleCPURequets = "50m" - ThanosRuleMemoryRequets = "512Mi" - ThanosRuleReloaderCPURequets = "4m" - ThanosRuleReloaderMemoryRequets = "25Mi" - - ThanosCachedCPURequets = "45m" - ThanosCachedMemoryRequets = "128Mi" - ThanosCachedExporterCPURequets = "5m" - ThanosCachedExporterMemoryRequets = "50Mi" - - ThanosStoreCPURequets = "100m" - ThanosStoreMemoryRequets = "1Gi" - - MetricsCollectorCPURequets = "10m" - MetricsCollectorMemoryRequets = "100Mi" - MetricsCollectorCPULimits = "" - MetricsCollectorMemoryLimits = "" - ObservatoriumAPI = "observatorium-api" ThanosCompact = "thanos-compact" ThanosQuery = "thanos-query" @@ -239,9 +186,6 @@ const ( DefaultImagePullPolicy = "IfNotPresent" DefaultImagePullSecret = "multiclusterhub-operator-pull-secret" - - ResourceLimits = "limits" - ResourceRequests = "requests" ) const ( @@ -285,23 +229,6 @@ var ( certDuration = time.Hour * 24 * 365 isAlertingDisabled = false - Replicas1 int32 = 1 - Replicas2 int32 = 2 - Replicas3 int32 = 3 - Replicas = map[string]*int32{ - ObservatoriumAPI: &Replicas2, - ThanosQuery: &Replicas2, - ThanosQueryFrontend: &Replicas2, - Grafana: &Replicas2, - RBACQueryProxy: &Replicas2, - - ThanosRule: &Replicas3, - ThanosReceive: &Replicas3, - ThanosStoreShard: &Replicas3, - ThanosStoreMemcached: &Replicas3, - ThanosQueryFrontendMemcached: &Replicas3, - Alertmanager: &Replicas3, - } // use this map to store the operand name. operandNames = map[string]string{} @@ -325,63 +252,6 @@ var ( multicloudConsoleRouteHost = "" ) -func GetReplicas(component string, advanced *observabilityv1beta2.AdvancedConfig) *int32 { - if advanced == nil { - return Replicas[component] - } - var replicas *int32 - switch component { - case ObservatoriumAPI: - if advanced.ObservatoriumAPI != nil { - replicas = advanced.ObservatoriumAPI.Replicas - } - case ThanosQuery: - if advanced.Query != nil { - replicas = advanced.Query.Replicas - } - case ThanosQueryFrontend: - if advanced.QueryFrontend != nil { - replicas = advanced.QueryFrontend.Replicas - } - case ThanosQueryFrontendMemcached: - if advanced.QueryFrontendMemcached != nil { - replicas = advanced.QueryFrontendMemcached.CommonSpec.Replicas - } - case ThanosRule: - if advanced.Rule != nil { - replicas = advanced.Rule.Replicas - } - case ThanosReceive: - if advanced.Receive != nil { - replicas = advanced.Receive.Replicas - } - case ThanosStoreMemcached: - if advanced.StoreMemcached != nil { - replicas = advanced.StoreMemcached.CommonSpec.Replicas - } - case ThanosStoreShard: - if advanced.Store != nil { - replicas = advanced.Store.Replicas - } - case RBACQueryProxy: - if advanced.RBACQueryProxy != nil { - replicas = advanced.RBACQueryProxy.Replicas - } - case Grafana: - if advanced.Grafana != nil { - replicas = advanced.Grafana.Replicas - } - case Alertmanager: - if advanced.Alertmanager != nil { - replicas = advanced.Alertmanager.Replicas - } - } - if replicas == nil || *replicas == 0 { - replicas = Replicas[component] - } - return replicas -} - // GetCrLabelKey returns the key for the CR label injected into the resources created by the operator. func GetCrLabelKey() string { return crLabelKey @@ -738,23 +608,6 @@ func IsPaused(annotations map[string]string) bool { return false } -// WithoutResourcesRequests returns true if the multiclusterobservability instance has annotation: -// mco-thanos-without-resources-requests: "true" -// This is just for test purpose: the KinD cluster does not have enough resources for the requests. -// We won't expose this annotation to the customer. -func WithoutResourcesRequests(annotations map[string]string) bool { - if annotations == nil { - return false - } - - if annotations[AnnotationMCOWithoutResourcesRequests] != "" && - strings.EqualFold(annotations[AnnotationMCOWithoutResourcesRequests], "true") { - return true - } - - return false -} - // GetTenantUID returns tenant uid. func GetTenantUID() string { if tenantUID == "" { @@ -820,296 +673,6 @@ func GetImagePullSecret(mco observabilityv1beta2.MultiClusterObservabilitySpec) } } -func getDefaultResource(resourceType string, resource corev1.ResourceName, - component string) string { - // No provide the default limits - if resourceType == ResourceLimits && component != Grafana { - return "" - } - switch component { - case ObservatoriumAPI: - if resource == corev1.ResourceCPU { - return ObservatoriumAPICPURequets - } - if resource == corev1.ResourceMemory { - return ObservatoriumAPIMemoryRequets - } - case ThanosCompact: - if resource == corev1.ResourceCPU { - return ThanosCompactCPURequets - } - if resource == corev1.ResourceMemory { - return ThanosCompactMemoryRequets - } - case ThanosQuery: - if resource == corev1.ResourceCPU { - return ThanosQueryCPURequets - } - if resource == corev1.ResourceMemory { - return ThanosQueryMemoryRequets - } - case ThanosQueryFrontend: - if resource == corev1.ResourceCPU { - return ThanosQueryFrontendCPURequets - } - if resource == corev1.ResourceMemory { - return ThanosQueryFrontendMemoryRequets - } - case ThanosRule: - if resource == corev1.ResourceCPU { - return ThanosRuleCPURequets - } - if resource == corev1.ResourceMemory { - return ThanosRuleMemoryRequets - } - case ThanosReceive: - if resource == corev1.ResourceCPU { - return ThanosReceiveCPURequets - } - if resource == corev1.ResourceMemory { - return ThanosReceiveMemoryRequets - } - case ThanosStoreShard: - if resource == corev1.ResourceCPU { - return ThanosStoreCPURequets - } - if resource == corev1.ResourceMemory { - return ThanosStoreMemoryRequets - } - case ThanosQueryFrontendMemcached, ThanosStoreMemcached: - if resource == corev1.ResourceCPU { - return ThanosCachedCPURequets - } - if resource == corev1.ResourceMemory { - return ThanosCachedMemoryRequets - } - case MemcachedExporter: - if resource == corev1.ResourceCPU { - return MemcachedExporterCPURequets - } - if resource == corev1.ResourceMemory { - return MemcachedExporterMemoryRequets - } - case RBACQueryProxy: - if resource == corev1.ResourceCPU { - return RBACQueryProxyCPURequets - } - if resource == corev1.ResourceMemory { - return RBACQueryProxyMemoryRequets - } - case MetricsCollector: - if resource == corev1.ResourceCPU { - return MetricsCollectorCPURequets - } - if resource == corev1.ResourceMemory { - return MetricsCollectorMemoryRequets - } - case Alertmanager: - if resource == corev1.ResourceCPU { - return AlertmanagerCPURequets - } - if resource == corev1.ResourceMemory { - return AlertmanagerMemoryRequets - } - case Grafana: - if resourceType == ResourceRequests { - if resource == corev1.ResourceCPU { - return GrafanaCPURequets - } - if resource == corev1.ResourceMemory { - return GrafanaMemoryRequets - } - } else if resourceType == ResourceLimits { - if resource == corev1.ResourceCPU { - return GrafanaCPULimits - } - if resource == corev1.ResourceMemory { - return GrafanaMemoryLimits - } - } - } - return "" -} - -func getResource(resourceType string, resource corev1.ResourceName, - component string, advanced *observabilityv1beta2.AdvancedConfig) string { - if advanced == nil { - return getDefaultResource(resourceType, resource, component) - } - var resourcesReq *corev1.ResourceRequirements - switch component { - case ObservatoriumAPI: - if advanced.ObservatoriumAPI != nil { - resourcesReq = advanced.ObservatoriumAPI.Resources - } - case ThanosCompact: - if advanced.Compact != nil { - resourcesReq = advanced.Compact.Resources - } - case ThanosQuery: - if advanced.Query != nil { - resourcesReq = advanced.Query.Resources - } - case ThanosQueryFrontend: - if advanced.QueryFrontend != nil { - resourcesReq = advanced.QueryFrontend.Resources - } - case ThanosQueryFrontendMemcached: - if advanced.QueryFrontendMemcached != nil { - resourcesReq = advanced.QueryFrontendMemcached.CommonSpec.Resources - } - case ThanosRule: - if advanced.Rule != nil { - resourcesReq = advanced.Rule.Resources - } - case ThanosReceive: - if advanced.Receive != nil { - resourcesReq = advanced.Receive.Resources - } - case ThanosStoreMemcached: - if advanced.StoreMemcached != nil { - resourcesReq = advanced.StoreMemcached.CommonSpec.Resources - } - case ThanosStoreShard: - if advanced.Store != nil { - resourcesReq = advanced.Store.Resources - } - case RBACQueryProxy: - if advanced.RBACQueryProxy != nil { - resourcesReq = advanced.RBACQueryProxy.Resources - } - case Grafana: - if advanced.Grafana != nil { - resourcesReq = advanced.Grafana.Resources - } - case Alertmanager: - if advanced.Alertmanager != nil { - resourcesReq = advanced.Alertmanager.Resources - } - } - - if resourcesReq != nil { - if resourceType == ResourceRequests { - if len(resourcesReq.Requests) != 0 { - if resource == corev1.ResourceCPU { - return resourcesReq.Requests.Cpu().String() - } else if resource == corev1.ResourceMemory { - return resourcesReq.Requests.Memory().String() - } else { - return getDefaultResource(resourceType, resource, component) - } - } else { - return getDefaultResource(resourceType, resource, component) - } - } - if resourceType == ResourceLimits { - if len(resourcesReq.Limits) != 0 { - if resource == corev1.ResourceCPU { - return resourcesReq.Limits.Cpu().String() - } else if resource == corev1.ResourceMemory { - return resourcesReq.Limits.Memory().String() - } else { - return getDefaultResource(resourceType, resource, component) - } - } else { - return getDefaultResource(resourceType, resource, component) - } - } - } else { - return getDefaultResource(resourceType, resource, component) - } - return "" -} - -func GetResources(component string, advanced *observabilityv1beta2.AdvancedConfig) corev1.ResourceRequirements { - - cpuRequests := getResource(ResourceRequests, corev1.ResourceCPU, component, advanced) - cpuLimits := getResource(ResourceLimits, corev1.ResourceCPU, component, advanced) - memoryRequests := getResource(ResourceRequests, corev1.ResourceMemory, component, advanced) - memoryLimits := getResource(ResourceLimits, corev1.ResourceMemory, component, advanced) - - resourceReq := corev1.ResourceRequirements{} - requests := corev1.ResourceList{} - limits := corev1.ResourceList{} - if cpuRequests == "0" { - cpuRequests = getDefaultResource(ResourceRequests, corev1.ResourceCPU, component) - } - if cpuRequests != "" { - requests[corev1.ResourceName(corev1.ResourceCPU)] = resource.MustParse(cpuRequests) - } - - if memoryRequests == "0" { - memoryRequests = getDefaultResource(ResourceRequests, corev1.ResourceMemory, component) - } - if memoryRequests != "" { - requests[corev1.ResourceName(corev1.ResourceMemory)] = resource.MustParse(memoryRequests) - } - - if cpuLimits == "0" { - cpuLimits = getDefaultResource(ResourceLimits, corev1.ResourceCPU, component) - } - if cpuLimits != "" { - limits[corev1.ResourceName(corev1.ResourceCPU)] = resource.MustParse(cpuLimits) - } - - if memoryLimits == "0" { - memoryLimits = getDefaultResource(ResourceLimits, corev1.ResourceMemory, component) - } - if memoryLimits != "" { - limits[corev1.ResourceName(corev1.ResourceMemory)] = resource.MustParse(memoryLimits) - } - resourceReq.Limits = limits - resourceReq.Requests = requests - - return resourceReq -} - -func GetOBAResources(oba *mcoshared.ObservabilityAddonSpec) *corev1.ResourceRequirements { - cpuRequests := MetricsCollectorCPURequets - cpuLimits := MetricsCollectorCPULimits - memoryRequests := MetricsCollectorMemoryRequets - memoryLimits := MetricsCollectorMemoryLimits - - if oba.Resources != nil { - if len(oba.Resources.Requests) != 0 { - if oba.Resources.Requests.Cpu().String() != "0" { - cpuRequests = oba.Resources.Requests.Cpu().String() - } - if oba.Resources.Requests.Memory().String() != "0" { - memoryRequests = oba.Resources.Requests.Memory().String() - } - } - if len(oba.Resources.Limits) != 0 { - if oba.Resources.Limits.Cpu().String() != "0" { - cpuLimits = oba.Resources.Limits.Cpu().String() - } - if oba.Resources.Limits.Memory().String() != "0" { - memoryLimits = oba.Resources.Limits.Memory().String() - } - } - } - - resourceReq := &corev1.ResourceRequirements{} - requests := corev1.ResourceList{} - limits := corev1.ResourceList{} - if cpuRequests != "" { - requests[corev1.ResourceName(corev1.ResourceCPU)] = resource.MustParse(cpuRequests) - } - if memoryRequests != "" { - requests[corev1.ResourceName(corev1.ResourceMemory)] = resource.MustParse(memoryRequests) - } - if cpuLimits != "" { - limits[corev1.ResourceName(corev1.ResourceCPU)] = resource.MustParse(cpuLimits) - } - if memoryLimits != "" { - limits[corev1.ResourceName(corev1.ResourceMemory)] = resource.MustParse(memoryLimits) - } - resourceReq.Limits = limits - resourceReq.Requests = requests - - return resourceReq -} - func GetOperandName(name string) string { log.V(1).Info("operand is", "key", name, "name", operandNames[name]) return operandNames[name] diff --git a/operators/multiclusterobservability/pkg/config/config_test.go b/operators/multiclusterobservability/pkg/config/config_test.go index d039f0a8c..821666a89 100644 --- a/operators/multiclusterobservability/pkg/config/config_test.go +++ b/operators/multiclusterobservability/pkg/config/config_test.go @@ -17,7 +17,6 @@ import ( observatoriumv1alpha1 "github.com/stolostron/observatorium-operator/api/v1alpha1" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -637,379 +636,6 @@ func Test_checkIsIBMCloud(t *testing.T) { } } -func TestGetResources(t *testing.T) { - caseList := []struct { - name string - componentName string - raw *mcov1beta2.AdvancedConfig - result func(resources corev1.ResourceRequirements) bool - }{ - { - name: "Have requests defined in resources", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == "1" && - resources.Requests.Memory().String() == "1Gi" && - resources.Limits.Cpu().String() == "0" && - resources.Limits.Memory().String() == "0" - }, - }, - { - name: "Have limits defined in resources", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == ObservatoriumAPICPURequets && - resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequets && - resources.Limits.Cpu().String() == "1" && - resources.Limits.Memory().String() == "1Gi" - }, - }, - { - name: "Have limits defined in resources", - componentName: RBACQueryProxy, - raw: &mcov1beta2.AdvancedConfig{ - RBACQueryProxy: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == RBACQueryProxyCPURequets && - resources.Requests.Memory().String() == RBACQueryProxyMemoryRequets && - resources.Limits.Cpu().String() == "1" && - resources.Limits.Memory().String() == "1Gi" - }, - }, - { - name: "Have requests and limits defined in requests", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == "1" && - resources.Requests.Memory().String() == "1Gi" && - resources.Limits.Cpu().String() == "1" && - resources.Limits.Memory().String() == "1Gi" - }, - }, - { - name: "No CPU defined in requests", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Requests: corev1.ResourceList{}, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == ObservatoriumAPICPURequets && - resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequets && - resources.Limits.Cpu().String() == "0" && resources.Limits.Memory().String() == "0" - }, - }, - { - name: "No requests defined in resources", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{}, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == ObservatoriumAPICPURequets && - resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequets && - resources.Limits.Cpu().String() == "0" && resources.Limits.Memory().String() == "0" - }, - }, - { - name: "No resources defined", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{}, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == ObservatoriumAPICPURequets && - resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequets && - resources.Limits.Cpu().String() == "0" && resources.Limits.Memory().String() == "0" - }, - }, - { - name: "No advanced defined", - componentName: ObservatoriumAPI, - raw: nil, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == ObservatoriumAPICPURequets && - resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequets && - resources.Limits.Cpu().String() == "0" && resources.Limits.Memory().String() == "0" - }, - }, - { - name: "No advanced defined", - componentName: Grafana, - raw: nil, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == GrafanaCPURequets && - resources.Requests.Memory().String() == GrafanaMemoryRequets && - resources.Limits.Cpu().String() == GrafanaCPULimits && - resources.Limits.Memory().String() == GrafanaMemoryLimits - }, - }, - { - name: "Have requests defined", - componentName: Grafana, - raw: &mcov1beta2.AdvancedConfig{ - Grafana: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == "1" && - resources.Requests.Memory().String() == GrafanaMemoryRequets && - resources.Limits.Cpu().String() == GrafanaCPULimits && - resources.Limits.Memory().String() == GrafanaMemoryLimits - }, - }, - { - name: "Have limits defined", - componentName: Grafana, - raw: &mcov1beta2.AdvancedConfig{ - Grafana: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == GrafanaCPURequets && - resources.Requests.Memory().String() == GrafanaMemoryRequets && - resources.Limits.Cpu().String() == "1" && - resources.Limits.Memory().String() == GrafanaMemoryLimits - }, - }, - { - name: "Have limits defined", - componentName: Grafana, - raw: &mcov1beta2.AdvancedConfig{ - Grafana: &mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == GrafanaCPURequets && - resources.Requests.Memory().String() == GrafanaMemoryRequets && - resources.Limits.Cpu().String() == "1" && - resources.Limits.Memory().String() == "1Gi" - }, - }, - { - name: "Have limits defined", - componentName: ThanosQueryFrontendMemcached, - raw: &mcov1beta2.AdvancedConfig{ - QueryFrontendMemcached: &mcov1beta2.CacheConfig{ - CommonSpec: mcov1beta2.CommonSpec{ - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == ThanosCachedCPURequets && - resources.Requests.Memory().String() == ThanosCachedMemoryRequets && - resources.Limits.Cpu().String() == "1" && - resources.Limits.Memory().String() == "1Gi" - }, - }, - } - - for _, c := range caseList { - t.Run(c.componentName+":"+c.name, func(t *testing.T) { - resources := GetResources(c.componentName, c.raw) - if !c.result(resources) { - t.Errorf("case (%v) output (%v) is not the expected", c.componentName+":"+c.name, resources) - } - }) - } -} - -func TestGetReplicas(t *testing.T) { - var replicas0 int32 = 0 - caseList := []struct { - name string - componentName string - raw *mcov1beta2.AdvancedConfig - result func(replicas *int32) bool - }{ - { - name: "Have replicas defined", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{ - Replicas: &Replicas1, - }, - }, - result: func(replicas *int32) bool { - return replicas == &Replicas1 - }, - }, - { - name: "Do not allow to set 0", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{ - Replicas: &replicas0, - }, - }, - result: func(replicas *int32) bool { - return replicas == &Replicas2 - }, - }, - { - name: "No advanced defined", - componentName: ObservatoriumAPI, - raw: nil, - result: func(replicas *int32) bool { - return replicas == &Replicas2 - }, - }, - { - name: "No replicas defined", - componentName: ObservatoriumAPI, - raw: &mcov1beta2.AdvancedConfig{ - ObservatoriumAPI: &mcov1beta2.CommonSpec{}, - }, - result: func(replicas *int32) bool { - return replicas == &Replicas2 - }, - }, - } - for _, c := range caseList { - t.Run(c.componentName+":"+c.name, func(t *testing.T) { - replicas := GetReplicas(c.componentName, c.raw) - if !c.result(replicas) { - t.Errorf("case (%v) output (%v) is not the expected", c.componentName+":"+c.name, replicas) - } - }) - } -} - -func TestGetOBAResources(t *testing.T) { - caseList := []struct { - name string - componentName string - raw *mcoshared.ObservabilityAddonSpec - result func(resources corev1.ResourceRequirements) bool - }{ - { - name: "Have requests defined", - componentName: ObservatoriumAPI, - raw: &mcoshared.ObservabilityAddonSpec{ - Resources: &corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == "1" && - resources.Requests.Memory().String() == "1Gi" && - resources.Limits.Cpu().String() == "0" && - resources.Limits.Memory().String() == "0" - }, - }, - { - name: "Have limits defined", - componentName: ObservatoriumAPI, - raw: &mcoshared.ObservabilityAddonSpec{ - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == MetricsCollectorCPURequets && - resources.Requests.Memory().String() == MetricsCollectorMemoryRequets && - resources.Limits.Cpu().String() == "1" && - resources.Limits.Memory().String() == "0" - }, - }, - { - name: "no resources defined", - componentName: ObservatoriumAPI, - raw: &mcoshared.ObservabilityAddonSpec{ - Resources: &corev1.ResourceRequirements{}, - }, - result: func(resources corev1.ResourceRequirements) bool { - return resources.Requests.Cpu().String() == MetricsCollectorCPURequets && - resources.Requests.Memory().String() == MetricsCollectorMemoryRequets && - resources.Limits.Cpu().String() == "0" && - resources.Limits.Memory().String() == "0" - }, - }, - } - for _, c := range caseList { - t.Run(c.componentName+":"+c.name, func(t *testing.T) { - resources := GetOBAResources(c.raw) - if !c.result(*resources) { - t.Errorf("case (%v) output (%v) is not the expected", c.componentName+":"+c.name, resources) - } - }) - } -} - func TestGetOperandName(t *testing.T) { caseList := []struct { name string diff --git a/operators/multiclusterobservability/pkg/config/resources.go b/operators/multiclusterobservability/pkg/config/resources.go new file mode 100644 index 000000000..9778e387a --- /dev/null +++ b/operators/multiclusterobservability/pkg/config/resources.go @@ -0,0 +1,374 @@ +// Copyright (c) Red Hat, Inc. +// Copyright Contributors to the Open Cluster Management project +// Licensed under the Apache License 2.0 + +package config + +import ( + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + + mcoshared "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/shared" + observabilityv1beta2 "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/v1beta2" +) + +const ( + ResourceLimits = "limits" + ResourceRequests = "requests" + AnnotationMCOWithoutResourcesRequests = "mco-thanos-without-resources-requests" +) + +// getDefaultResourceCPU returns the default resource CPU request for a particular o11y workload. +func getDefaultResourceCPU(component string, tshirtSize observabilityv1beta2.TShirtSize) string { + switch component { + case ObservatoriumAPI: + return ObservatoriumAPICPURequest[tshirtSize] + case ThanosCompact: + return ThanosCompactCPURequest[tshirtSize] + case ThanosQuery: + return ThanosQueryCPURequest[tshirtSize] + case ThanosQueryFrontend: + return ThanosQueryFrontendCPURequest[tshirtSize] + case ThanosRule: + return ThanosRuleCPURequest[tshirtSize] + case ThanosReceive: + return ThanosReceiveCPURequest[tshirtSize] + case ThanosStoreShard: + return ThanosStoreCPURequest[tshirtSize] + case ThanosQueryFrontendMemcached: + return ThanosQueryMemcachedCPURequest[tshirtSize] + case ThanosStoreMemcached: + return ThanosStoreMemcachedCPURequest[tshirtSize] + case MemcachedExporter: + return MemcachedExporterCPURequest[tshirtSize] + case RBACQueryProxy: + return RBACQueryProxyCPURequest[tshirtSize] + case MetricsCollector: + return MetricsCollectorCPURequest[tshirtSize] + case Alertmanager: + return AlertmanagerCPURequest[tshirtSize] + case Grafana: + return GrafanaCPURequest[tshirtSize] + default: + return "" + } +} + +// getDefaultResourceMemory returns the default resource memory request for a particular o11y workload. +func getDefaultResourceMemory(component string, tshirtSize observabilityv1beta2.TShirtSize) string { + switch component { + case ObservatoriumAPI: + return ObservatoriumAPIMemoryRequest[tshirtSize] + case ThanosCompact: + return ThanosCompactMemoryRequest[tshirtSize] + case ThanosQuery: + return ThanosQueryMemoryRequest[tshirtSize] + case ThanosQueryFrontend: + return ThanosQueryFrontendMemoryRequest[tshirtSize] + case ThanosRule: + return ThanosRuleMemoryRequest[tshirtSize] + case ThanosReceive: + return ThanosReceiveMemoryRequest[tshirtSize] + case ThanosStoreShard: + return ThanosStoreMemoryRequest[tshirtSize] + case ThanosQueryFrontendMemcached: + return ThanosQueryMemcachedMemoryRequest[tshirtSize] + case ThanosStoreMemcached: + return ThanosStoreMemcachedMemoryRequest[tshirtSize] + case MemcachedExporter: + return MemcachedExporterMemoryRequest[tshirtSize] + case RBACQueryProxy: + return RBACQueryProxyMemoryRequest[tshirtSize] + case MetricsCollector: + return MetricsCollectorMemoryRequest[tshirtSize] + case Alertmanager: + return AlertmanagerMemoryRequest[tshirtSize] + case Grafana: + return GrafanaMemoryRequest[tshirtSize] + default: + return "" + } +} + +// getDefaultResourceMemoryLimit returns the default resource memory limit for a particular o11y workload. +func getDefaultResourceMemoryLimit(component string) string { + switch component { + case Grafana: + return GrafanaMemoryLimit + default: + return "" + } +} + +// getDefaultResourceCPULimit returns the default resource CPU limit for a particular o11y workload. +func getDefaultResourceCPULimit(component string) string { + switch component { + case Grafana: + return GrafanaCPULimit + default: + return "" + } +} + +// getDefaultResourceRequirements returns the default resource requirements for a particular o11y workload. +func getDefaultResourceRequirements(component string, tshirtSize observabilityv1beta2.TShirtSize) corev1.ResourceRequirements { + requests := corev1.ResourceList{} + limits := corev1.ResourceList{} + + memoryRequest := getDefaultResourceMemory(component, tshirtSize) + cpuRequest := getDefaultResourceCPU(component, tshirtSize) + + memoryLimit := getDefaultResourceMemoryLimit(component) + cpuLimit := getDefaultResourceCPULimit(component) + + requests[corev1.ResourceName(corev1.ResourceMemory)] = resource.MustParse(memoryRequest) + requests[corev1.ResourceName(corev1.ResourceCPU)] = resource.MustParse(cpuRequest) + + if memoryLimit != "" { + limits[corev1.ResourceName(corev1.ResourceMemory)] = resource.MustParse(memoryLimit) + } + + if cpuLimit != "" { + limits[corev1.ResourceName(corev1.ResourceCPU)] = resource.MustParse(cpuLimit) + } + + return corev1.ResourceRequirements{ + Requests: requests, + Limits: limits, + } +} + +// getAdvancedConfigResourceOverride returns the AdvancedConfig overridden resource requirements for a particular o11y workload. +func getAdvancedConfigResourceOverride(component string, tshirtSize observabilityv1beta2.TShirtSize, advanced *observabilityv1beta2.AdvancedConfig) corev1.ResourceRequirements { + resourcesReq := &corev1.ResourceRequirements{} + switch component { + case ObservatoriumAPI: + if advanced.ObservatoriumAPI != nil { + resourcesReq = advanced.ObservatoriumAPI.Resources + } + case ThanosCompact: + if advanced.Compact != nil { + resourcesReq = advanced.Compact.Resources + } + case ThanosQuery: + if advanced.Query != nil { + resourcesReq = advanced.Query.Resources + } + case ThanosQueryFrontend: + if advanced.QueryFrontend != nil { + resourcesReq = advanced.QueryFrontend.Resources + } + case ThanosQueryFrontendMemcached: + if advanced.QueryFrontendMemcached != nil { + resourcesReq = advanced.QueryFrontendMemcached.Resources + } + case ThanosRule: + if advanced.Rule != nil { + resourcesReq = advanced.Rule.Resources + } + case ThanosReceive: + if advanced.Receive != nil { + resourcesReq = advanced.Receive.Resources + } + case ThanosStoreMemcached: + if advanced.StoreMemcached != nil { + resourcesReq = advanced.StoreMemcached.Resources + } + case ThanosStoreShard: + if advanced.Store != nil { + resourcesReq = advanced.Store.Resources + } + case RBACQueryProxy: + if advanced.RBACQueryProxy != nil { + resourcesReq = advanced.RBACQueryProxy.Resources + } + case Grafana: + if advanced.Grafana != nil { + resourcesReq = advanced.Grafana.Resources + } + case Alertmanager: + if advanced.Alertmanager != nil { + resourcesReq = advanced.Alertmanager.Resources + } + } + + final := corev1.ResourceRequirements{} + // Validate config and combine defaults. + if resourcesReq != nil { + if len(resourcesReq.Requests) != 0 { + final.Requests = resourcesReq.Requests + if resourcesReq.Requests.Cpu().String() == "0" { + final.Requests[corev1.ResourceCPU] = getDefaultResourceRequirements(component, tshirtSize).Requests[corev1.ResourceCPU] + } + if resourcesReq.Requests.Memory().String() == "0" { + final.Requests[corev1.ResourceMemory] = getDefaultResourceRequirements(component, tshirtSize).Requests[corev1.ResourceMemory] + } + } else { + final.Requests = getDefaultResourceRequirements(component, tshirtSize).Requests + } + + if len(resourcesReq.Limits) != 0 { + final.Limits = resourcesReq.Limits + if resourcesReq.Limits.Cpu().String() == "0" { + final.Limits[corev1.ResourceCPU] = getDefaultResourceRequirements(component, tshirtSize).Limits[corev1.ResourceCPU] + } + if resourcesReq.Limits.Memory().String() == "0" { + final.Limits[corev1.ResourceMemory] = getDefaultResourceRequirements(component, tshirtSize).Limits[corev1.ResourceMemory] + } + } else { + final.Limits = getDefaultResourceRequirements(component, tshirtSize).Limits + } + + return final + } + + return getDefaultResourceRequirements(component, tshirtSize) +} + +// GetResources returns the pre-set resource requirements for a particular o11y workload. +// Always default unless configured via advancedConfig, in which case it is overridden. +func GetResources(component string, tshirtSize observabilityv1beta2.TShirtSize, advanced *observabilityv1beta2.AdvancedConfig) corev1.ResourceRequirements { + if tshirtSize == "" { + tshirtSize = Default + } + + resourceReq := getDefaultResourceRequirements(component, tshirtSize) + if advanced != nil { + resourceReq = getAdvancedConfigResourceOverride(component, tshirtSize, advanced) + } + + return resourceReq +} + +// GetOBAResources returns the pre-set resource requirements for metrics collector. +func GetOBAResources(oba *mcoshared.ObservabilityAddonSpec, tshirtSize observabilityv1beta2.TShirtSize) *corev1.ResourceRequirements { + if tshirtSize == "" { + tshirtSize = Default + } + + cpuRequests := MetricsCollectorCPURequest[tshirtSize] + cpuLimits := MetricsCollectorCPULimits + memoryRequests := MetricsCollectorMemoryRequest[tshirtSize] + memoryLimits := MetricsCollectorMemoryLimits + resourceReq := &corev1.ResourceRequirements{} + + if oba.Resources != nil { + if len(oba.Resources.Requests) != 0 { + if oba.Resources.Requests.Cpu().String() != "0" { + cpuRequests = oba.Resources.Requests.Cpu().String() + } + if oba.Resources.Requests.Memory().String() != "0" { + memoryRequests = oba.Resources.Requests.Memory().String() + } + } + if len(oba.Resources.Limits) != 0 { + if oba.Resources.Limits.Cpu().String() != "0" { + cpuLimits = oba.Resources.Limits.Cpu().String() + } + if oba.Resources.Limits.Memory().String() != "0" { + memoryLimits = oba.Resources.Limits.Memory().String() + } + } + } + + requests := corev1.ResourceList{} + limits := corev1.ResourceList{} + if cpuRequests != "" { + requests[corev1.ResourceName(corev1.ResourceCPU)] = resource.MustParse(cpuRequests) + } + if memoryRequests != "" { + requests[corev1.ResourceName(corev1.ResourceMemory)] = resource.MustParse(memoryRequests) + } + if cpuLimits != "" { + limits[corev1.ResourceName(corev1.ResourceCPU)] = resource.MustParse(cpuLimits) + } + if memoryLimits != "" { + limits[corev1.ResourceName(corev1.ResourceMemory)] = resource.MustParse(memoryLimits) + } + resourceReq.Limits = limits + resourceReq.Requests = requests + + return resourceReq +} + +// GetReplicas returns the default replicas for a particular o11y workload. +func GetReplicas(component string, tshirtSize observabilityv1beta2.TShirtSize, advanced *observabilityv1beta2.AdvancedConfig) *int32 { + if tshirtSize == "" { + tshirtSize = Default + } + + if advanced == nil { + return Replicas[component][tshirtSize] + } + var replicas *int32 + switch component { + case ObservatoriumAPI: + if advanced.ObservatoriumAPI != nil { + replicas = advanced.ObservatoriumAPI.Replicas + } + case ThanosQuery: + if advanced.Query != nil { + replicas = advanced.Query.Replicas + } + case ThanosQueryFrontend: + if advanced.QueryFrontend != nil { + replicas = advanced.QueryFrontend.Replicas + } + case ThanosQueryFrontendMemcached: + if advanced.QueryFrontendMemcached != nil { + replicas = advanced.QueryFrontendMemcached.Replicas + } + case ThanosRule: + if advanced.Rule != nil { + replicas = advanced.Rule.Replicas + } + case ThanosReceive: + if advanced.Receive != nil { + replicas = advanced.Receive.Replicas + } + case ThanosStoreMemcached: + if advanced.StoreMemcached != nil { + replicas = advanced.StoreMemcached.Replicas + } + case ThanosStoreShard: + if advanced.Store != nil { + replicas = advanced.Store.Replicas + } + case RBACQueryProxy: + if advanced.RBACQueryProxy != nil { + replicas = advanced.RBACQueryProxy.Replicas + } + case Grafana: + if advanced.Grafana != nil { + replicas = advanced.Grafana.Replicas + } + case Alertmanager: + if advanced.Alertmanager != nil { + replicas = advanced.Alertmanager.Replicas + } + } + + if replicas == nil || *replicas == 0 { + replicas = Replicas[component][tshirtSize] + } + return replicas +} + +// WithoutResourcesRequests returns true if the multiclusterobservability instance has annotation: +// mco-thanos-without-resources-requests: "true" +// This is just for test purpose: the KinD cluster does not have enough resources for the requests. +// We won't expose this annotation to the customer. +func WithoutResourcesRequests(annotations map[string]string) bool { + if annotations == nil { + return false + } + + if annotations[AnnotationMCOWithoutResourcesRequests] != "" && + strings.EqualFold(annotations[AnnotationMCOWithoutResourcesRequests], "true") { + return true + } + + return false +} diff --git a/operators/multiclusterobservability/pkg/config/resources_map.go b/operators/multiclusterobservability/pkg/config/resources_map.go new file mode 100644 index 000000000..10631bd32 --- /dev/null +++ b/operators/multiclusterobservability/pkg/config/resources_map.go @@ -0,0 +1,496 @@ +// Copyright (c) Red Hat, Inc. +// Copyright Contributors to the Open Cluster Management project +// Licensed under the Apache License 2.0 + +package config + +import ( + observabilityv1beta2 "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/v1beta2" +) + +const ( + // To be only used for testing. + Minimal observabilityv1beta2.TShirtSize = "minimal" + + // To be used for actual setups. + Default observabilityv1beta2.TShirtSize = "default" + Small observabilityv1beta2.TShirtSize = "small" + Medium observabilityv1beta2.TShirtSize = "medium" + Large observabilityv1beta2.TShirtSize = "large" + XLarge observabilityv1beta2.TShirtSize = "xlarge" + TwoXLarge observabilityv1beta2.TShirtSize = "2xlarge" + FourXLarge observabilityv1beta2.TShirtSize = "4xlarge" +) + +type ResourceSizeMap map[observabilityv1beta2.TShirtSize]string + +// Specifies resources for all components and their respective TShirt sizes. +var ( + RBACQueryProxyCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "250m", + Default: "20m", + Small: "250m", + Medium: "500m", + Large: "500m", + XLarge: "1", + TwoXLarge: "1", + FourXLarge: "1.5", + } + RBACQueryProxyMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "128Mi", + Default: "100Mi", + Small: "256Mi", + Medium: "512Mi", + Large: "1Gi", + XLarge: "2Gi", + TwoXLarge: "2Gi", + FourXLarge: "4Gi", + } + + GrafanaCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "4m", + Default: "4m", + Small: "4m", + Medium: "4m", + Large: "4m", + XLarge: "4m", + TwoXLarge: "4m", + FourXLarge: "4m", + } + GrafanaMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "100Mi", + Default: "100Mi", + Small: "100Mi", + Medium: "100Mi", + Large: "100Mi", + XLarge: "100Mi", + TwoXLarge: "100Mi", + FourXLarge: "100Mi", + } + + AlertmanagerCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "250m", + Default: "4m", + Small: "500m", + Medium: "500m", + Large: "1", + XLarge: "1", + TwoXLarge: "1500m", + FourXLarge: "2", + } + AlertmanagerMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "128Mi", + Default: "200Mi", + Small: "128Mi", + Medium: "256Mi", + Large: "512Mi", + XLarge: "512Mi", + TwoXLarge: "1Gi", + FourXLarge: "1Gi", + } + + ObservatoriumAPICPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "250m", + Default: "20m", + Small: "250m", + Medium: "500m", + Large: "500m", + XLarge: "1", + TwoXLarge: "1", + FourXLarge: "1.5", + } + ObservatoriumAPIMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "128Mi", + Default: "128Mi", + Small: "256Mi", + Medium: "512Mi", + Large: "1Gi", + XLarge: "2Gi", + TwoXLarge: "2Gi", + FourXLarge: "4Gi", + } + + ThanosQueryFrontendCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "500m", + Default: "100m", + Small: "1", + Medium: "1", + Large: "2", + XLarge: "2", + TwoXLarge: "4", + FourXLarge: "4", + } + ThanosQueryFrontendMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "256Mi", + Default: "256Mi", + Small: "512Mi", + Medium: "2Gi", + Large: "5Gi", + XLarge: "8Gi", + TwoXLarge: "10Gi", + FourXLarge: "12Gi", + } + + MemcachedExporterCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "5m", + Default: "5m", + Small: "5m", + Medium: "5m", + Large: "5m", + XLarge: "5m", + TwoXLarge: "5m", + FourXLarge: "5m", + } + MemcachedExporterMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "50Mi", + Default: "50Mi", + Small: "50Mi", + Medium: "50Mi", + Large: "50Mi", + XLarge: "50Mi", + TwoXLarge: "50Mi", + FourXLarge: "50Mi", + } + + ThanosQueryCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "1", + Default: "300m", + Small: "1500m", + Medium: "2", + Large: "4", + XLarge: "6", + TwoXLarge: "6", + FourXLarge: "7", + } + ThanosQueryMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "1Gi", + Default: "1Gi", + Small: "4Gi", + Medium: "6Gi", + Large: "8Gi", + XLarge: "10Gi", + TwoXLarge: "15Gi", + FourXLarge: "18Gi", + } + + ThanosCompactCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "250m", + Default: "100m", + Small: "500m", + Medium: "1", + Large: "3", + XLarge: "3", + TwoXLarge: "4", + FourXLarge: "6", + } + ThanosCompactMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "512Mi", + Default: "512Mi", + Small: "1Gi", + Medium: "2Gi", + Large: "4Gi", + XLarge: "8Gi", + TwoXLarge: "12Gi", + FourXLarge: "18Gi", + } + + ObservatoriumReceiveControllerCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "4m", + Default: "4m", + Small: "4m", + Medium: "4m", + Large: "4m", + XLarge: "4m", + TwoXLarge: "4m", + FourXLarge: "4m", + } + ObservatoriumReceiveControllerMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "32Mi", + Default: "32Mi", + Small: "32Mi", + Medium: "32Mi", + Large: "32Mi", + XLarge: "32Mi", + TwoXLarge: "32Mi", + FourXLarge: "32Mi", + } + + ThanosReceiveCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "500m", + Default: "300m", + Small: "2", + Medium: "4", + Large: "5", + XLarge: "5", + TwoXLarge: "6", + FourXLarge: "10", + } + ThanosReceiveMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "2Gi", + Default: "512Mi", + Small: "6Gi", + Medium: "12Gi", + Large: "24Gi", + XLarge: "36Gi", + TwoXLarge: "52Gi", + FourXLarge: "128Gi", + } + + ThanosRuleCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "250m", + Default: "50m", + Small: "500m", + Medium: "1", + Large: "3", + XLarge: "3", + TwoXLarge: "4", + FourXLarge: "6", + } + ThanosRuleMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "512Mi", + Default: "512Mi", + Small: "1Gi", + Medium: "2Gi", + Large: "4Gi", + XLarge: "6Gi", + TwoXLarge: "10Gi", + FourXLarge: "15Gi", + } + + ThanosRuleReloaderCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "4m", + Default: "4m", + Small: "4m", + Medium: "4m", + Large: "4m", + XLarge: "4m", + TwoXLarge: "4m", + FourXLarge: "4m", + } + ThanosRuleReloaderMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "25Mi", + Default: "25Mi", + Small: "25Mi", + Medium: "25Mi", + Large: "25Mi", + XLarge: "25Mi", + TwoXLarge: "25Mi", + FourXLarge: "25Mi", + } + + ThanosStoreMemcachedCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "100m", + Default: "45m", + Small: "250m", + Medium: "250m", + Large: "500m", + XLarge: "1", + TwoXLarge: "1", + FourXLarge: "2", + } + ThanosStoreMemcachedMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "1Gi", + Default: "128Mi", + Small: "1Gi", + Medium: "2Gi", + Large: "2Gi", + XLarge: "4Gi", + TwoXLarge: "4Gi", + FourXLarge: "6Gi", + } + + ThanosQueryMemcachedCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "45m", + Default: "45m", + Small: "100m", + Medium: "250m", + Large: "500m", + XLarge: "1", + TwoXLarge: "1", + FourXLarge: "1500m", + } + ThanosQueryMemcachedMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "128Mi", + Default: "128Mi", + Small: "256Mi", + Medium: "256Mi", + Large: "512Mi", + XLarge: "512Mi", + TwoXLarge: "1Gi", + FourXLarge: "1Gi", + } + + ThanosStoreCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "2", + Default: "100m", + Small: "2", + Medium: "3", + Large: "3", + XLarge: "3", + TwoXLarge: "4", + FourXLarge: "6", + } + ThanosStoreMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "1Gi", + Default: "1Gi", + Small: "4Gi", + Medium: "6Gi", + Large: "8Gi", + XLarge: "12Gi", + TwoXLarge: "15Gi", + FourXLarge: "20Gi", + } + + MetricsCollectorCPURequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "10m", + Default: "10m", + Small: "10m", + Medium: "10m", + Large: "10m", + XLarge: "10m", + TwoXLarge: "10m", + FourXLarge: "10m", + } + MetricsCollectorMemoryRequest ResourceSizeMap = map[observabilityv1beta2.TShirtSize]string{ + Minimal: "100Mi", + Default: "100Mi", + Small: "100Mi", + Medium: "100Mi", + Large: "100Mi", + XLarge: "100Mi", + TwoXLarge: "100Mi", + FourXLarge: "100Mi", + } +) + +const ( + GrafanaCPULimit = "500m" + GrafanaMemoryLimit = "1Gi" + + MetricsCollectorCPULimits = "" + MetricsCollectorMemoryLimits = "" +) + +type ReplicaMap map[observabilityv1beta2.TShirtSize]*int32 + +func intptr(i int32) *int32 { + return &i +} + +// Specifies replicas for all components. +// TODO(saswatamcode): Figure out the right values for these. They are all the same at the moment. +var ( + Replicas = map[string]ReplicaMap{ + ObservatoriumAPI: { + Minimal: intptr(3), + Default: intptr(2), + Small: intptr(3), + Medium: intptr(3), + Large: intptr(6), + XLarge: intptr(6), + TwoXLarge: intptr(8), + FourXLarge: intptr(12), + }, + ThanosQuery: { + Minimal: intptr(2), + Default: intptr(2), + Small: intptr(3), + Medium: intptr(4), + Large: intptr(6), + XLarge: intptr(8), + TwoXLarge: intptr(8), + FourXLarge: intptr(10), + }, + ThanosQueryFrontend: { + Minimal: intptr(2), + Default: intptr(2), + Small: intptr(3), + Medium: intptr(3), + Large: intptr(3), + XLarge: intptr(6), + TwoXLarge: intptr(8), + FourXLarge: intptr(10), + }, + Grafana: { + Minimal: intptr(2), + Default: intptr(2), + Small: intptr(2), + Medium: intptr(2), + Large: intptr(2), + XLarge: intptr(2), + TwoXLarge: intptr(2), + FourXLarge: intptr(2), + }, + RBACQueryProxy: { + Minimal: intptr(3), + Default: intptr(2), + Small: intptr(3), + Medium: intptr(3), + Large: intptr(6), + XLarge: intptr(6), + TwoXLarge: intptr(8), + FourXLarge: intptr(12), + }, + + ThanosRule: { + Minimal: intptr(3), + Default: intptr(3), + Small: intptr(3), + Medium: intptr(3), + Large: intptr(3), + XLarge: intptr(3), + TwoXLarge: intptr(3), + FourXLarge: intptr(3), + }, + ThanosReceive: { + Minimal: intptr(6), + Default: intptr(3), + Small: intptr(6), + Medium: intptr(6), + Large: intptr(6), + XLarge: intptr(9), + TwoXLarge: intptr(12), + FourXLarge: intptr(12), + }, + ThanosStoreShard: { + Minimal: intptr(3), + Default: intptr(3), + Small: intptr(3), + Medium: intptr(3), + Large: intptr(6), + XLarge: intptr(6), + TwoXLarge: intptr(6), + FourXLarge: intptr(6), + }, + ThanosStoreMemcached: { + Minimal: intptr(3), + Default: intptr(3), + Small: intptr(3), + Medium: intptr(3), + Large: intptr(3), + XLarge: intptr(3), + TwoXLarge: intptr(6), + FourXLarge: intptr(6), + }, + ThanosQueryFrontendMemcached: { + Minimal: intptr(3), + Default: intptr(3), + Small: intptr(3), + Medium: intptr(3), + Large: intptr(3), + XLarge: intptr(3), + TwoXLarge: intptr(3), + FourXLarge: intptr(3), + }, + Alertmanager: { + Minimal: intptr(3), + Default: intptr(3), + Small: intptr(3), + Medium: intptr(3), + Large: intptr(3), + XLarge: intptr(3), + TwoXLarge: intptr(3), + FourXLarge: intptr(3), + }, + } +) diff --git a/operators/multiclusterobservability/pkg/config/resources_test.go b/operators/multiclusterobservability/pkg/config/resources_test.go new file mode 100644 index 000000000..000d97e85 --- /dev/null +++ b/operators/multiclusterobservability/pkg/config/resources_test.go @@ -0,0 +1,388 @@ +// Copyright (c) Red Hat, Inc. +// Copyright Contributors to the Open Cluster Management project +// Licensed under the Apache License 2.0 + +package config + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + + mcoshared "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/shared" + mcov1beta2 "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/v1beta2" +) + +func TestGetResources(t *testing.T) { + caseList := []struct { + name string + componentName string + raw *mcov1beta2.AdvancedConfig + result func(resources corev1.ResourceRequirements) bool + }{ + { + name: "Have requests defined in resources", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == "1" && + resources.Requests.Memory().String() == "1Gi" && + resources.Limits.Cpu().String() == "0" && + resources.Limits.Memory().String() == "0" + }, + }, + { + name: "Have limits defined in resources", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == ObservatoriumAPICPURequest[Default] && + resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequest[Default] && + resources.Limits.Cpu().String() == "1" && + resources.Limits.Memory().String() == "1Gi" + }, + }, + { + name: "Have limits defined in resources", + componentName: RBACQueryProxy, + raw: &mcov1beta2.AdvancedConfig{ + RBACQueryProxy: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == RBACQueryProxyCPURequest[Default] && + resources.Requests.Memory().String() == RBACQueryProxyMemoryRequest[Default] && + resources.Limits.Cpu().String() == "1" && + resources.Limits.Memory().String() == "1Gi" + }, + }, + { + name: "Have requests and limits defined in requests", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == "1" && + resources.Requests.Memory().String() == "1Gi" && + resources.Limits.Cpu().String() == "1" && + resources.Limits.Memory().String() == "1Gi" + }, + }, + { + name: "No CPU defined in requests", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{}, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == ObservatoriumAPICPURequest[Default] && + resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequest[Default] && + resources.Limits.Cpu().String() == "0" && resources.Limits.Memory().String() == "0" + }, + }, + { + name: "No requests defined in resources", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{}, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == ObservatoriumAPICPURequest[Default] && + resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequest[Default] && + resources.Limits.Cpu().String() == "0" && resources.Limits.Memory().String() == "0" + }, + }, + { + name: "No resources defined", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{}, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == ObservatoriumAPICPURequest[Default] && + resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequest[Default] && + resources.Limits.Cpu().String() == "0" && resources.Limits.Memory().String() == "0" + }, + }, + { + name: "No advanced defined", + componentName: ObservatoriumAPI, + raw: nil, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == ObservatoriumAPICPURequest[Default] && + resources.Requests.Memory().String() == ObservatoriumAPIMemoryRequest[Default] && + resources.Limits.Cpu().String() == "0" && resources.Limits.Memory().String() == "0" + }, + }, + { + name: "No advanced defined", + componentName: Grafana, + raw: nil, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == GrafanaCPURequest[Default] && + resources.Requests.Memory().String() == GrafanaMemoryRequest[Default] && + resources.Limits.Cpu().String() == GrafanaCPULimit && + resources.Limits.Memory().String() == GrafanaMemoryLimit + }, + }, + { + name: "Have requests defined", + componentName: Grafana, + raw: &mcov1beta2.AdvancedConfig{ + Grafana: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + }, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == "1" && + resources.Requests.Memory().String() == GrafanaMemoryRequest[Default] && + resources.Limits.Cpu().String() == GrafanaCPULimit && + resources.Limits.Memory().String() == GrafanaMemoryLimit + }, + }, + { + name: "Have limits defined", + componentName: Grafana, + raw: &mcov1beta2.AdvancedConfig{ + Grafana: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + }, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == GrafanaCPURequest[Default] && + resources.Requests.Memory().String() == GrafanaMemoryRequest[Default] && + resources.Limits.Cpu().String() == "1" && + resources.Limits.Memory().String() == GrafanaMemoryLimit + }, + }, + { + name: "Have limits defined", + componentName: Grafana, + raw: &mcov1beta2.AdvancedConfig{ + Grafana: &mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == GrafanaCPURequest[Default] && + resources.Requests.Memory().String() == GrafanaMemoryRequest[Default] && + resources.Limits.Cpu().String() == "1" && + resources.Limits.Memory().String() == "1Gi" + }, + }, + { + name: "Have limits defined", + componentName: ThanosQueryFrontendMemcached, + raw: &mcov1beta2.AdvancedConfig{ + QueryFrontendMemcached: &mcov1beta2.CacheConfig{ + CommonSpec: mcov1beta2.CommonSpec{ + Resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == ThanosQueryMemcachedCPURequest[Default] && + resources.Requests.Memory().String() == ThanosQueryMemcachedMemoryRequest[Default] && + resources.Limits.Cpu().String() == "1" && + resources.Limits.Memory().String() == "1Gi" + }, + }, + } + + for _, c := range caseList { + t.Run(c.componentName+":"+c.name, func(t *testing.T) { + resources := GetResources(c.componentName, Default, c.raw) + if !c.result(resources) { + t.Errorf("case (%v) output (%v) is not the expected", c.componentName+":"+c.name, resources) + } + }) + } +} + +func TestGetReplicas(t *testing.T) { + var replicas0 int32 = 0 + caseList := []struct { + name string + componentName string + raw *mcov1beta2.AdvancedConfig + result func(replicas *int32) bool + }{ + { + name: "Have replicas defined", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{ + Replicas: intptr(1), + }, + }, + result: func(replicas *int32) bool { + return *replicas == 1 + }, + }, + { + name: "Do not allow to set 0", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{ + Replicas: &replicas0, + }, + }, + result: func(replicas *int32) bool { + return *replicas == 2 + }, + }, + { + name: "No advanced defined", + componentName: ObservatoriumAPI, + raw: nil, + result: func(replicas *int32) bool { + return *replicas == 2 + }, + }, + { + name: "No replicas defined", + componentName: ObservatoriumAPI, + raw: &mcov1beta2.AdvancedConfig{ + ObservatoriumAPI: &mcov1beta2.CommonSpec{}, + }, + result: func(replicas *int32) bool { + return *replicas == 2 + }, + }, + } + for _, c := range caseList { + t.Run(c.componentName+":"+c.name, func(t *testing.T) { + replicas := GetReplicas(c.componentName, Default, c.raw) + if !c.result(replicas) { + t.Errorf("case (%v) output (%v) is not the expected", c.componentName+":"+c.name, replicas) + } + }) + } +} + +func TestGetOBAResources(t *testing.T) { + caseList := []struct { + name string + componentName string + raw *mcoshared.ObservabilityAddonSpec + result func(resources corev1.ResourceRequirements) bool + }{ + { + name: "Have requests defined", + componentName: ObservatoriumAPI, + raw: &mcoshared.ObservabilityAddonSpec{ + Resources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == "1" && + resources.Requests.Memory().String() == "1Gi" && + resources.Limits.Cpu().String() == "0" && + resources.Limits.Memory().String() == "0" + }, + }, + { + name: "Have limits defined", + componentName: ObservatoriumAPI, + raw: &mcoshared.ObservabilityAddonSpec{ + Resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + }, + }, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == MetricsCollectorCPURequest[Default] && + resources.Requests.Memory().String() == MetricsCollectorMemoryRequest[Default] && + resources.Limits.Cpu().String() == "1" && + resources.Limits.Memory().String() == "0" + }, + }, + { + name: "no resources defined", + componentName: ObservatoriumAPI, + raw: &mcoshared.ObservabilityAddonSpec{ + Resources: &corev1.ResourceRequirements{}, + }, + result: func(resources corev1.ResourceRequirements) bool { + return resources.Requests.Cpu().String() == MetricsCollectorCPURequest[Default] && + resources.Requests.Memory().String() == MetricsCollectorMemoryRequest[Default] && + resources.Limits.Cpu().String() == "0" && + resources.Limits.Memory().String() == "0" + }, + }, + } + for _, c := range caseList { + t.Run(c.componentName+":"+c.name, func(t *testing.T) { + resources := GetOBAResources(c.raw, Default) + if !c.result(*resources) { + t.Errorf("case (%v) output (%v) is not the expected", c.componentName+":"+c.name, resources) + } + }) + } +} diff --git a/operators/multiclusterobservability/pkg/rendering/renderer.go b/operators/multiclusterobservability/pkg/rendering/renderer.go index 6a19632d3..7771fa6dc 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer.go @@ -12,7 +12,6 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" obv1beta2 "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/v1beta2" - "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config" mcoconfig "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config" "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/rendering/templates" rendererutil "github.com/stolostron/multicluster-observability-operator/operators/pkg/rendering" @@ -54,7 +53,7 @@ func (r *MCORenderer) Render() ([]*unstructured.Unstructured, error) { } namespace := mcoconfig.GetDefaultNamespace() labels := map[string]string{ - config.GetCrLabelKey(): r.cr.Name, + mcoconfig.GetCrLabelKey(): r.cr.Name, } resources, err := r.renderer.RenderTemplates(genericTemplates, namespace, labels) if err != nil { @@ -112,7 +111,7 @@ func (r *MCORenderer) Render() ([]*unstructured.Unstructured, error) { if err != nil { return nil, err } - crLabelKey := config.GetCrLabelKey() + crLabelKey := mcoconfig.GetCrLabelKey() dep := obj.(*v1.Deployment) dep.ObjectMeta.Labels[crLabelKey] = r.cr.Name dep.Spec.Selector.MatchLabels[crLabelKey] = r.cr.Name @@ -137,7 +136,7 @@ func (r *MCORenderer) Render() ([]*unstructured.Unstructured, error) { if found { spec.Containers[0].Image = image } - dep.Name = mcoconfig.GetOperandName(config.ObservatoriumOperator) + dep.Name = mcoconfig.GetOperandName(mcoconfig.ObservatoriumOperator) } diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager.go b/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager.go index 131e51525..188396eaa 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager.go @@ -57,7 +57,7 @@ func (r *MCORenderer) renderAlertManagerStatefulSet(res *resource.Resource, dep.Spec.Selector.MatchLabels[crLabelKey] = r.cr.Name dep.Spec.Template.ObjectMeta.Labels[crLabelKey] = r.cr.Name dep.Name = mcoconfig.GetOperandName(mcoconfig.Alertmanager) - dep.Spec.Replicas = mcoconfig.GetReplicas(mcoconfig.Alertmanager, r.cr.Spec.AdvancedConfig) + dep.Spec.Replicas = mcoconfig.GetReplicas(mcoconfig.Alertmanager, r.cr.Spec.InstanceSize, r.cr.Spec.AdvancedConfig) spec := &dep.Spec.Template.Spec @@ -75,7 +75,7 @@ func (r *MCORenderer) renderAlertManagerStatefulSet(res *resource.Resource, } spec.Containers[0].Args = args - spec.Containers[0].Resources = mcoconfig.GetResources(mcoconfig.Alertmanager, r.cr.Spec.AdvancedConfig) + spec.Containers[0].Resources = mcoconfig.GetResources(mcoconfig.Alertmanager, r.cr.Spec.InstanceSize, r.cr.Spec.AdvancedConfig) spec.Containers[1].ImagePullPolicy = imagePullPolicy spec.NodeSelector = r.cr.Spec.NodeSelector diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_grafana.go b/operators/multiclusterobservability/pkg/rendering/renderer_grafana.go index 62dd385a6..36962fea5 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_grafana.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_grafana.go @@ -45,7 +45,7 @@ func (r *MCORenderer) renderGrafanaDeployments(res *resource.Resource, } dep := obj.(*v1.Deployment) dep.Name = config.GetOperandName(config.Grafana) - dep.Spec.Replicas = config.GetReplicas(config.Grafana, r.cr.Spec.AdvancedConfig) + dep.Spec.Replicas = config.GetReplicas(config.Grafana, r.cr.Spec.InstanceSize, r.cr.Spec.AdvancedConfig) spec := &dep.Spec.Template.Spec imagePullPolicy := config.GetImagePullPolicy(r.cr.Spec) @@ -57,7 +57,7 @@ func (r *MCORenderer) renderGrafanaDeployments(res *resource.Resource, spec.Containers[0].Image = image } spec.Containers[0].ImagePullPolicy = imagePullPolicy - spec.Containers[0].Resources = config.GetResources(config.Grafana, r.cr.Spec.AdvancedConfig) + spec.Containers[0].Resources = config.GetResources(config.Grafana, r.cr.Spec.InstanceSize, r.cr.Spec.AdvancedConfig) spec.Containers[1].Image = config.DefaultImgRepository + "/" + config.GrafanaDashboardLoaderName + ":" + config.DefaultImgTagSuffix diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_proxy.go b/operators/multiclusterobservability/pkg/rendering/renderer_proxy.go index 9e3e37af3..3416639c1 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_proxy.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_proxy.go @@ -13,7 +13,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/kustomize/api/resource" - "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config" mcoconfig "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config" rendererutil "github.com/stolostron/multicluster-observability-operator/operators/pkg/rendering" "github.com/stolostron/multicluster-observability-operator/operators/pkg/util" @@ -52,11 +51,11 @@ func (r *MCORenderer) renderProxyDeployment(res *resource.Resource, dep.ObjectMeta.Labels[crLabelKey] = r.cr.Name dep.Spec.Selector.MatchLabels[crLabelKey] = r.cr.Name dep.Spec.Template.ObjectMeta.Labels[crLabelKey] = r.cr.Name - dep.Name = mcoconfig.GetOperandName(config.RBACQueryProxy) - dep.Spec.Replicas = config.GetReplicas(config.RBACQueryProxy, r.cr.Spec.AdvancedConfig) + dep.Name = mcoconfig.GetOperandName(mcoconfig.RBACQueryProxy) + dep.Spec.Replicas = mcoconfig.GetReplicas(mcoconfig.RBACQueryProxy, r.cr.Spec.InstanceSize, r.cr.Spec.AdvancedConfig) spec := &dep.Spec.Template.Spec - imagePullPolicy := config.GetImagePullPolicy(r.cr.Spec) + imagePullPolicy := mcoconfig.GetImagePullPolicy(r.cr.Spec) spec.Containers[0].ImagePullPolicy = imagePullPolicy args0 := spec.Containers[0].Args for idx := range args0 { @@ -69,7 +68,7 @@ func (r *MCORenderer) renderProxyDeployment(res *resource.Resource, ) } spec.Containers[0].Args = args0 - spec.Containers[0].Resources = mcoconfig.GetResources(mcoconfig.RBACQueryProxy, r.cr.Spec.AdvancedConfig) + spec.Containers[0].Resources = mcoconfig.GetResources(mcoconfig.RBACQueryProxy, r.cr.Spec.InstanceSize, r.cr.Spec.AdvancedConfig) spec.Containers[1].ImagePullPolicy = imagePullPolicy args1 := spec.Containers[1].Args @@ -83,8 +82,8 @@ func (r *MCORenderer) renderProxyDeployment(res *resource.Resource, {Name: mcoconfig.GetImagePullSecret(r.cr.Spec)}, } - spec.Containers[0].Image = config.DefaultImgRepository + "/" + config.RBACQueryProxyImgName + - ":" + config.DefaultImgTagSuffix + spec.Containers[0].Image = mcoconfig.DefaultImgRepository + "/" + mcoconfig.RBACQueryProxyImgName + + ":" + mcoconfig.DefaultImgTagSuffix //replace the proxy image found, image := mcoconfig.ReplaceImage( r.cr.Annotations, diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_thanos.go b/operators/multiclusterobservability/pkg/rendering/renderer_thanos.go index 383b1ea83..2f2b7fc9f 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_thanos.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_thanos.go @@ -76,7 +76,7 @@ func (r *MCORenderer) RenderThanosConfig(res *resource.Resource, return nil, err } addr := []string{} - for i := 0; i < int(*mcoconfig.GetReplicas("alertmanager", r.cr.Spec.AdvancedConfig)); i++ { + for i := 0; i < int(*mcoconfig.GetReplicas("alertmanager", r.cr.Spec.InstanceSize, r.cr.Spec.AdvancedConfig)); i++ { addr = append(addr, "observability-alertmanager-"+strconv.Itoa(i)+ ".alertmanager-operated.open-cluster-management-observability.svc:9095") }