From 0cc97352baadb7655e30acf20744df585f5881a0 Mon Sep 17 00:00:00 2001 From: Maciej Zimnoch Date: Tue, 3 Dec 2024 21:37:59 +0100 Subject: [PATCH] Lazily initialize assets Assets kept as global variables are moved to structure which is lazily initialized upon first usage. This speeds up binary startup as cpu heavy initialization is done at later stage. Thanks to this, contianers which are running side commands - usually under contstrained resources - doesn't need to process them if they are not using them. ScyllaDB-ignition container which previously required ~2min to start the command: ``` I1203 18:59:28.325125 1 operator/cmd.go:21] maxprocs: Updating GOMAXPROCS=1: using minimum allowed GOMAXPROCS I1203 19:01:37.837439 1 operator/ignition.go:156] run-ignition version "v0.0.0-alpha.0-110-g9d9a586" ``` With this change it's almost immediate: ``` I1203 20:04:35.334993 1 operator/cmd.go:21] maxprocs: Updating GOMAXPROCS=[1]: using minimum allowed GOMAXPROCS I1203 20:04:35.337488 1 operator/ignition.go:156] run-ignition version "v1.16.0-alpha.0-23-g70ff0f6" ``` --- .../monitoring/grafana/v1alpha1/registry.go | 43 ++++++++++++++----- assets/monitoring/prometheus/v1/registry.go | 43 ++++++++++++++----- assets/scylladb/registry.go | 7 ++- pkg/assets/object_template.go | 8 ++-- pkg/controller/scylladbdatacenter/resource.go | 2 +- .../scylladbmonitoring/main_test.go | 2 +- .../scylladbmonitoring/sync_grafana.go | 20 ++++----- .../scylladbmonitoring/sync_grafana_test.go | 2 +- .../scylladbmonitoring/sync_prometheus.go | 24 +++++------ pkg/util/lazy/lazy.go | 26 +++++++++++ test/e2e/fixture/scylla/registry.go | 2 +- .../scylladbmonitoring/scylladbmonitoring.go | 6 +-- 12 files changed, 128 insertions(+), 57 deletions(-) create mode 100644 pkg/util/lazy/lazy.go diff --git a/assets/monitoring/grafana/v1alpha1/registry.go b/assets/monitoring/grafana/v1alpha1/registry.go index a53bef733ee..451d7b37f41 100644 --- a/assets/monitoring/grafana/v1alpha1/registry.go +++ b/assets/monitoring/grafana/v1alpha1/registry.go @@ -7,54 +7,75 @@ import ( "github.com/scylladb/scylla-operator/pkg/assets" "github.com/scylladb/scylla-operator/pkg/helpers" "github.com/scylladb/scylla-operator/pkg/scheme" + "github.com/scylladb/scylla-operator/pkg/util/lazy" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/runtime" ) -func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string) assets.ObjectTemplate[T] { +func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string) *assets.ObjectTemplate[T] { return assets.ParseObjectTemplateOrDie[T](name, tmplString, assets.TemplateFuncs, scheme.Codecs.UniversalDeserializer()) } var ( //go:embed "deployment.yaml" grafanaDeploymentTemplateString string - GrafanaDeploymentTemplate = ParseObjectTemplateOrDie[*appsv1.Deployment]("grafana-deployment", grafanaDeploymentTemplateString) + GrafanaDeploymentTemplate = lazy.New(func() *assets.ObjectTemplate[*appsv1.Deployment] { + return ParseObjectTemplateOrDie[*appsv1.Deployment]("grafana-deployment", grafanaDeploymentTemplateString) + }) //go:embed "serviceaccount.yaml" grafanaSATemplateString string - GrafanaSATemplate = ParseObjectTemplateOrDie[*corev1.ServiceAccount]("grafana-sa", grafanaSATemplateString) + GrafanaSATemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.ServiceAccount] { + return ParseObjectTemplateOrDie[*corev1.ServiceAccount]("grafana-sa", grafanaSATemplateString) + }) //go:embed "configs.cm.yaml" grafanaConfigsTemplateString string - GrafanaConfigsTemplate = ParseObjectTemplateOrDie[*corev1.ConfigMap]("grafana-configs-cm", grafanaConfigsTemplateString) + GrafanaConfigsTemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.ConfigMap] { + return ParseObjectTemplateOrDie[*corev1.ConfigMap]("grafana-configs-cm", grafanaConfigsTemplateString) + }) //go:embed "admin-credentials.secret.yaml" grafanaAdminCredentialsSecretTemplateString string - GrafanaAdminCredentialsSecretTemplate = ParseObjectTemplateOrDie[*corev1.Secret]("grafana-access-credentials-secret", grafanaAdminCredentialsSecretTemplateString) + GrafanaAdminCredentialsSecretTemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.Secret] { + return ParseObjectTemplateOrDie[*corev1.Secret]("grafana-access-credentials-secret", grafanaAdminCredentialsSecretTemplateString) + }) //go:embed "provisioning.cm.yaml" grafanaProvisioningConfigMapTemplateString string - GrafanaProvisioningConfigMapTemplate = ParseObjectTemplateOrDie[*corev1.ConfigMap]("grafana-provisioning-cm", grafanaProvisioningConfigMapTemplateString) + GrafanaProvisioningConfigMapTemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.ConfigMap] { + return ParseObjectTemplateOrDie[*corev1.ConfigMap]("grafana-provisioning-cm", grafanaProvisioningConfigMapTemplateString) + }) //go:embed "dashboards.cm.yaml" grafanaDashboardsConfigMapTemplateString string - GrafanaDashboardsConfigMapTemplate = ParseObjectTemplateOrDie[*corev1.ConfigMap]("grafana-dashboards-cm", grafanaDashboardsConfigMapTemplateString) + GrafanaDashboardsConfigMapTemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.ConfigMap] { + return ParseObjectTemplateOrDie[*corev1.ConfigMap]("grafana-dashboards-cm", grafanaDashboardsConfigMapTemplateString) + }) //go:embed "dashboards/platform/*/*.json" grafanaDashboardsPlatformFS embed.FS - GrafanaDashboardsPlatform = helpers.Must(NewGrafanaDashboardsFromFS(grafanaDashboardsPlatformFS, "dashboards/platform")) + GrafanaDashboardsPlatform = lazy.New(func() GrafanaDashboardsFoldersMap { + return helpers.Must(NewGrafanaDashboardsFromFS(grafanaDashboardsPlatformFS, "dashboards/platform")) + }) //go:embed "dashboards/saas/*/*.json" grafanaDashboardsSAASFS embed.FS - GrafanaDashboardsSAAS = helpers.Must(NewGrafanaDashboardsFromFS(grafanaDashboardsSAASFS, "dashboards/saas")) + GrafanaDashboardsSAAS = lazy.New(func() GrafanaDashboardsFoldersMap { + return helpers.Must(NewGrafanaDashboardsFromFS(grafanaDashboardsSAASFS, "dashboards/saas")) + }) //go:embed "service.yaml" grafanaServiceTemplateString string - GrafanaServiceTemplate = ParseObjectTemplateOrDie[*corev1.Service]("grafana-service", grafanaServiceTemplateString) + GrafanaServiceTemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.Service] { + return ParseObjectTemplateOrDie[*corev1.Service]("grafana-service", grafanaServiceTemplateString) + }) //go:embed "ingress.yaml" grafanaIngressTemplateString string - GrafanaIngressTemplate = ParseObjectTemplateOrDie[*networkingv1.Ingress]("grafana-ingress", grafanaIngressTemplateString) + GrafanaIngressTemplate = lazy.New(func() *assets.ObjectTemplate[*networkingv1.Ingress] { + return ParseObjectTemplateOrDie[*networkingv1.Ingress]("grafana-ingress", grafanaIngressTemplateString) + }) ) diff --git a/assets/monitoring/prometheus/v1/registry.go b/assets/monitoring/prometheus/v1/registry.go index d2824d79029..426749bc358 100644 --- a/assets/monitoring/prometheus/v1/registry.go +++ b/assets/monitoring/prometheus/v1/registry.go @@ -8,54 +8,75 @@ import ( monitoringv1 "github.com/scylladb/scylla-operator/pkg/externalapi/monitoring/v1" "github.com/scylladb/scylla-operator/pkg/helpers" "github.com/scylladb/scylla-operator/pkg/scheme" + "github.com/scylladb/scylla-operator/pkg/util/lazy" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime" ) -func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string) assets.ObjectTemplate[T] { +func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string) *assets.ObjectTemplate[T] { return assets.ParseObjectTemplateOrDie[T](name, tmplString, assets.TemplateFuncs, scheme.Codecs.UniversalDeserializer()) } var ( //go:embed "prometheus.yaml" prometheusTemplateString string - PrometheusTemplate = ParseObjectTemplateOrDie[*monitoringv1.Prometheus]("prometheus", prometheusTemplateString) + PrometheusTemplate = lazy.New(func() *assets.ObjectTemplate[*monitoringv1.Prometheus] { + return ParseObjectTemplateOrDie[*monitoringv1.Prometheus]("prometheus", prometheusTemplateString) + }) //go:embed "serviceaccount.yaml" prometheusSATemplateString string - PrometheusSATemplate = ParseObjectTemplateOrDie[*corev1.ServiceAccount]("prometheus-sa", prometheusSATemplateString) + PrometheusSATemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.ServiceAccount] { + return ParseObjectTemplateOrDie[*corev1.ServiceAccount]("prometheus-sa", prometheusSATemplateString) + }) //go:embed "rolebinding.yaml" prometheusRoleBindingTemplateString string - PrometheusRoleBindingTemplate = ParseObjectTemplateOrDie[*rbacv1.RoleBinding]("prometheus-rolebinding", prometheusRoleBindingTemplateString) + PrometheusRoleBindingTemplate = lazy.New(func() *assets.ObjectTemplate[*rbacv1.RoleBinding] { + return ParseObjectTemplateOrDie[*rbacv1.RoleBinding]("prometheus-rolebinding", prometheusRoleBindingTemplateString) + }) //go:embed "service.yaml" prometheusServiceTemplateString string - PrometheusServiceTemplate = ParseObjectTemplateOrDie[*corev1.Service]("prometheus-service", prometheusServiceTemplateString) + PrometheusServiceTemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.Service] { + return ParseObjectTemplateOrDie[*corev1.Service]("prometheus-service", prometheusServiceTemplateString) + }) //go:embed "scylladb.servicemonitor.yaml" scyllaDBServiceMonitorTemplateString string - ScyllaDBServiceMonitorTemplate = ParseObjectTemplateOrDie[*monitoringv1.ServiceMonitor]("scylladb-servicemonitor", scyllaDBServiceMonitorTemplateString) + ScyllaDBServiceMonitorTemplate = lazy.New(func() *assets.ObjectTemplate[*monitoringv1.ServiceMonitor] { + return ParseObjectTemplateOrDie[*monitoringv1.ServiceMonitor]("scylladb-servicemonitor", scyllaDBServiceMonitorTemplateString) + }) //go:embed "rules/**" prometheusRulesFS embed.FS - PrometheusRules = helpers.Must(NewPrometheusRulesFromFS(prometheusRulesFS)) + PrometheusRules = lazy.New(func() PrometheusRulesMap { + return helpers.Must(NewPrometheusRulesFromFS(prometheusRulesFS)) + }) //go:embed "latency.prometheusrule.yaml" latencyPrometheusRuleTemplateString string - LatencyPrometheusRuleTemplate = ParseObjectTemplateOrDie[*monitoringv1.PrometheusRule]("latency-prometheus-rule", latencyPrometheusRuleTemplateString) + LatencyPrometheusRuleTemplate = lazy.New(func() *assets.ObjectTemplate[*monitoringv1.PrometheusRule] { + return ParseObjectTemplateOrDie[*monitoringv1.PrometheusRule]("latency-prometheus-rule", latencyPrometheusRuleTemplateString) + }) //go:embed "alerts.prometheusrule.yaml" alertsPrometheusRuleTemplateString string - AlertsPrometheusRuleTemplate = ParseObjectTemplateOrDie[*monitoringv1.PrometheusRule]("alerts-prometheus-rule", alertsPrometheusRuleTemplateString) + AlertsPrometheusRuleTemplate = lazy.New(func() *assets.ObjectTemplate[*monitoringv1.PrometheusRule] { + return ParseObjectTemplateOrDie[*monitoringv1.PrometheusRule]("alerts-prometheus-rule", alertsPrometheusRuleTemplateString) + }) //go:embed "table.prometheusrule.yaml" tablePrometheusRuleTemplateString string - TablePrometheusRuleTemplate = ParseObjectTemplateOrDie[*monitoringv1.PrometheusRule]("table-prometheus-rule", tablePrometheusRuleTemplateString) + TablePrometheusRuleTemplate = lazy.New(func() *assets.ObjectTemplate[*monitoringv1.PrometheusRule] { + return ParseObjectTemplateOrDie[*monitoringv1.PrometheusRule]("table-prometheus-rule", tablePrometheusRuleTemplateString) + }) //go:embed "ingress.yaml" prometheusIngressTemplateString string - PrometheusIngressTemplate = ParseObjectTemplateOrDie[*networkingv1.Ingress]("prometheus-ingress", prometheusIngressTemplateString) + PrometheusIngressTemplate = lazy.New(func() *assets.ObjectTemplate[*networkingv1.Ingress] { + return ParseObjectTemplateOrDie[*networkingv1.Ingress]("prometheus-ingress", prometheusIngressTemplateString) + }) ) diff --git a/assets/scylladb/registry.go b/assets/scylladb/registry.go index c95cc7944e9..2f250484729 100644 --- a/assets/scylladb/registry.go +++ b/assets/scylladb/registry.go @@ -5,16 +5,19 @@ import ( "github.com/scylladb/scylla-operator/pkg/assets" "github.com/scylladb/scylla-operator/pkg/scheme" + "github.com/scylladb/scylla-operator/pkg/util/lazy" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) -func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string) assets.ObjectTemplate[T] { +func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string) *assets.ObjectTemplate[T] { return assets.ParseObjectTemplateOrDie[T](name, tmplString, assets.TemplateFuncs, scheme.Codecs.UniversalDeserializer()) } var ( //go:embed "managedconfig.cm.yaml" scyllaDBManagedConfigTemplateString string - ScyllaDBManagedConfigTemplate = ParseObjectTemplateOrDie[*corev1.ConfigMap]("scylladb-managed-config", scyllaDBManagedConfigTemplateString) + ScyllaDBManagedConfigTemplate = lazy.New(func() *assets.ObjectTemplate[*corev1.ConfigMap] { + return ParseObjectTemplateOrDie[*corev1.ConfigMap]("scylladb-managed-config", scyllaDBManagedConfigTemplateString) + }) ) diff --git a/pkg/assets/object_template.go b/pkg/assets/object_template.go index 0232c0917ed..8b3d7fc4fa8 100644 --- a/pkg/assets/object_template.go +++ b/pkg/assets/object_template.go @@ -13,19 +13,19 @@ type ObjectTemplate[T runtime.Object] struct { decoder runtime.Decoder } -func ParseObjectTemplate[T runtime.Object](name, tmplString string, funcMap template.FuncMap, decoder runtime.Decoder) (ObjectTemplate[T], error) { +func ParseObjectTemplate[T runtime.Object](name, tmplString string, funcMap template.FuncMap, decoder runtime.Decoder) (*ObjectTemplate[T], error) { tmpl, err := template.New(name).Funcs(funcMap).Parse(tmplString) if err != nil { - return *new(ObjectTemplate[T]), fmt.Errorf("can't parse template %q: %w", name, err) + return new(ObjectTemplate[T]), fmt.Errorf("can't parse template %q: %w", name, err) } - return ObjectTemplate[T]{ + return &ObjectTemplate[T]{ tmpl: tmpl, decoder: decoder, }, nil } -func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string, funcMap template.FuncMap, decoder runtime.Decoder) ObjectTemplate[T] { +func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string, funcMap template.FuncMap, decoder runtime.Decoder) *ObjectTemplate[T] { return helpers.Must(ParseObjectTemplate[T](name, tmplString, funcMap, decoder)) } diff --git a/pkg/controller/scylladbdatacenter/resource.go b/pkg/controller/scylladbdatacenter/resource.go index 60b0726359b..54f91341df0 100644 --- a/pkg/controller/scylladbdatacenter/resource.go +++ b/pkg/controller/scylladbdatacenter/resource.go @@ -1720,7 +1720,7 @@ func MakeManagedScyllaDBConfig(sdc *scyllav1alpha1.ScyllaDBDatacenter) (*corev1. return pointer.Ptr(false) } - cm, _, err := scylladbassets.ScyllaDBManagedConfigTemplate.RenderObject( + cm, _, err := scylladbassets.ScyllaDBManagedConfigTemplate.Get().RenderObject( map[string]any{ "Namespace": sdc.Namespace, "Name": naming.GetScyllaDBManagedConfigCMName(sdc.Name), diff --git a/pkg/controller/scylladbmonitoring/main_test.go b/pkg/controller/scylladbmonitoring/main_test.go index 47e05544718..e14aac0b9cc 100644 --- a/pkg/controller/scylladbmonitoring/main_test.go +++ b/pkg/controller/scylladbmonitoring/main_test.go @@ -15,7 +15,7 @@ func TestMain(m *testing.M) { // We need to make sure that all prometheus rules (coming from scylladb monitoring) are wired. // Note that this can also mean we just lack coverage but both cases should be fixed. var errs []error - for f, r := range prometheusv1assets.PrometheusRules { + for f, r := range prometheusv1assets.PrometheusRules.Get() { if !r.Accessed() { errs = append(errs, fmt.Errorf("prometheus rule %q has not been used in any test and may not be used in the codebase", f)) } diff --git a/pkg/controller/scylladbmonitoring/sync_grafana.go b/pkg/controller/scylladbmonitoring/sync_grafana.go index 97fd69a6d34..93608960521 100644 --- a/pkg/controller/scylladbmonitoring/sync_grafana.go +++ b/pkg/controller/scylladbmonitoring/sync_grafana.go @@ -109,7 +109,7 @@ func makeGrafanaDeployment(sm *scyllav1alpha1.ScyllaDBMonitoring, soc *scyllav1a return nil, "", fmt.Errorf("dashboardsCMs can't be empty") } - return grafanav1alpha1assets.GrafanaDeploymentTemplate.RenderObject(map[string]any{ + return grafanav1alpha1assets.GrafanaDeploymentTemplate.Get().RenderObject(map[string]any{ "grafanaImage": grafanaImage, "bashToolsImage": bashToolsImage, "scyllaDBMonitoringName": sm.Name, @@ -135,14 +135,14 @@ func makeGrafanaAdminCredentials(sm *scyllav1alpha1.ScyllaDBMonitoring, secrets existingPassword = []byte(rand.String(grafanaPasswordLength)) } - return grafanav1alpha1assets.GrafanaAdminCredentialsSecretTemplate.RenderObject(map[string]any{ + return grafanav1alpha1assets.GrafanaAdminCredentialsSecretTemplate.Get().RenderObject(map[string]any{ "name": secretName, "password": existingPassword, }) } func makeGrafanaSA(sm *scyllav1alpha1.ScyllaDBMonitoring) (*corev1.ServiceAccount, string, error) { - return grafanav1alpha1assets.GrafanaSATemplate.RenderObject(map[string]any{ + return grafanav1alpha1assets.GrafanaSATemplate.Get().RenderObject(map[string]any{ "namespace": sm.Namespace, "scyllaDBMonitoringName": sm.Name, }) @@ -165,7 +165,7 @@ func makeGrafanaConfigs(sm *scyllav1alpha1.ScyllaDBMonitoring) (*corev1.ConfigMa return nil, "", fmt.Errorf("unkown monitoring type: %q", t) } - return grafanav1alpha1assets.GrafanaConfigsTemplate.RenderObject(map[string]any{ + return grafanav1alpha1assets.GrafanaConfigsTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, "enableAnonymousAccess": enableAnonymousAccess, "defaultDashboard": defaultDashboard, @@ -176,16 +176,16 @@ func makeGrafanaDashboards(sm *scyllav1alpha1.ScyllaDBMonitoring) ([]*corev1.Con var dashboardsFoldersMap grafanav1alpha1assets.GrafanaDashboardsFoldersMap switch t := sm.Spec.GetType(); t { case scyllav1alpha1.ScyllaDBMonitoringTypePlatform: - dashboardsFoldersMap = grafanav1alpha1assets.GrafanaDashboardsPlatform + dashboardsFoldersMap = grafanav1alpha1assets.GrafanaDashboardsPlatform.Get() case scyllav1alpha1.ScyllaDBMonitoringTypeSAAS: - dashboardsFoldersMap = grafanav1alpha1assets.GrafanaDashboardsSAAS + dashboardsFoldersMap = grafanav1alpha1assets.GrafanaDashboardsSAAS.Get() default: return nil, fmt.Errorf("unkown monitoring type: %q", t) } var cms []*corev1.ConfigMap for name, folder := range dashboardsFoldersMap { - cm, _, err := grafanav1alpha1assets.GrafanaDashboardsConfigMapTemplate.RenderObject(map[string]any{ + cm, _, err := grafanav1alpha1assets.GrafanaDashboardsConfigMapTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, "dashboardsName": name, "dashboards": folder, @@ -205,13 +205,13 @@ func makeGrafanaDashboards(sm *scyllav1alpha1.ScyllaDBMonitoring) ([]*corev1.Con } func makeGrafanaProvisionings(sm *scyllav1alpha1.ScyllaDBMonitoring) (*corev1.ConfigMap, string, error) { - return grafanav1alpha1assets.GrafanaProvisioningConfigMapTemplate.RenderObject(map[string]any{ + return grafanav1alpha1assets.GrafanaProvisioningConfigMapTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, }) } func makeGrafanaService(sm *scyllav1alpha1.ScyllaDBMonitoring) (*corev1.Service, string, error) { - return grafanav1alpha1assets.GrafanaServiceTemplate.RenderObject(map[string]any{ + return grafanav1alpha1assets.GrafanaServiceTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, }) } @@ -230,7 +230,7 @@ func makeGrafanaIngress(sm *scyllav1alpha1.ScyllaDBMonitoring) (*networkingv1.In return nil, "", nil } - return grafanav1alpha1assets.GrafanaIngressTemplate.RenderObject(map[string]any{ + return grafanav1alpha1assets.GrafanaIngressTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, "dnsDomains": ingressOptions.DNSDomains, "ingressAnnotations": ingressOptions.Annotations, diff --git a/pkg/controller/scylladbmonitoring/sync_grafana_test.go b/pkg/controller/scylladbmonitoring/sync_grafana_test.go index 9cbc70dd58d..a5149296a35 100644 --- a/pkg/controller/scylladbmonitoring/sync_grafana_test.go +++ b/pkg/controller/scylladbmonitoring/sync_grafana_test.go @@ -147,7 +147,7 @@ func Test_makeGrafanaDashboards(t *testing.T) { t.Helper() var expectedPlatformConfigMaps []*corev1.ConfigMap - for dashboardFolderName, dashboardFolder := range grafanav1alpha1assets.GrafanaDashboardsPlatform { + for dashboardFolderName, dashboardFolder := range grafanav1alpha1assets.GrafanaDashboardsPlatform.Get() { cm := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ Kind: "ConfigMap", diff --git a/pkg/controller/scylladbmonitoring/sync_prometheus.go b/pkg/controller/scylladbmonitoring/sync_prometheus.go index 2686b71e55d..8f5a2500000 100644 --- a/pkg/controller/scylladbmonitoring/sync_prometheus.go +++ b/pkg/controller/scylladbmonitoring/sync_prometheus.go @@ -69,28 +69,28 @@ func getPrometheusIngressDomains(sm *scyllav1alpha1.ScyllaDBMonitoring) []string } func makePrometheusSA(sm *scyllav1alpha1.ScyllaDBMonitoring) (*corev1.ServiceAccount, string, error) { - return prometheusv1assets.PrometheusSATemplate.RenderObject(map[string]any{ + return prometheusv1assets.PrometheusSATemplate.Get().RenderObject(map[string]any{ "namespace": sm.Namespace, "scyllaDBMonitoringName": sm.Name, }) } func makePrometheusRoleBinding(sm *scyllav1alpha1.ScyllaDBMonitoring) (*rbacv1.RoleBinding, string, error) { - return prometheusv1assets.PrometheusRoleBindingTemplate.RenderObject(map[string]any{ + return prometheusv1assets.PrometheusRoleBindingTemplate.Get().RenderObject(map[string]any{ "namespace": sm.Namespace, "scyllaDBMonitoringName": sm.Name, }) } func makePrometheusService(sm *scyllav1alpha1.ScyllaDBMonitoring) (*corev1.Service, string, error) { - return prometheusv1assets.PrometheusServiceTemplate.RenderObject(map[string]any{ + return prometheusv1assets.PrometheusServiceTemplate.Get().RenderObject(map[string]any{ "namespace": sm.Namespace, "scyllaDBMonitoringName": sm.Name, }) } func makeScyllaDBServiceMonitor(sm *scyllav1alpha1.ScyllaDBMonitoring) (*monitoringv1.ServiceMonitor, string, error) { - return prometheusv1assets.ScyllaDBServiceMonitorTemplate.RenderObject(map[string]any{ + return prometheusv1assets.ScyllaDBServiceMonitorTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, "endpointsSelector": sm.Spec.EndpointsSelector, }) @@ -98,12 +98,12 @@ func makeScyllaDBServiceMonitor(sm *scyllav1alpha1.ScyllaDBMonitoring) (*monitor func makeLatencyPrometheusRule(sm *scyllav1alpha1.ScyllaDBMonitoring) (*monitoringv1.PrometheusRule, string, error) { const latencyRulesFile = "prometheus.latency.rules.yml" - latencyRules, found := prometheusv1assets.PrometheusRules[latencyRulesFile] + latencyRules, found := prometheusv1assets.PrometheusRules.Get()[latencyRulesFile] if !found { return nil, "", fmt.Errorf("can't find latency rules file %q in the assets", latencyRulesFile) } - return prometheusv1assets.LatencyPrometheusRuleTemplate.RenderObject(map[string]any{ + return prometheusv1assets.LatencyPrometheusRuleTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, "groups": latencyRules.Get(), }) @@ -111,12 +111,12 @@ func makeLatencyPrometheusRule(sm *scyllav1alpha1.ScyllaDBMonitoring) (*monitori func makeAlertsPrometheusRule(sm *scyllav1alpha1.ScyllaDBMonitoring) (*monitoringv1.PrometheusRule, string, error) { const alertsRulesFile = "prometheus.rules.yml" - rule, found := prometheusv1assets.PrometheusRules[alertsRulesFile] + rule, found := prometheusv1assets.PrometheusRules.Get()[alertsRulesFile] if !found { return nil, "", fmt.Errorf("can't find alerts rules file %q in the assets", alertsRulesFile) } - return prometheusv1assets.AlertsPrometheusRuleTemplate.RenderObject(map[string]any{ + return prometheusv1assets.AlertsPrometheusRuleTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, "groups": rule.Get(), }) @@ -124,12 +124,12 @@ func makeAlertsPrometheusRule(sm *scyllav1alpha1.ScyllaDBMonitoring) (*monitorin func makeTablePrometheusRule(sm *scyllav1alpha1.ScyllaDBMonitoring) (*monitoringv1.PrometheusRule, string, error) { const tableRulesFile = "prometheus.table.yml" - rule, found := prometheusv1assets.PrometheusRules[tableRulesFile] + rule, found := prometheusv1assets.PrometheusRules.Get()[tableRulesFile] if !found { return nil, "", fmt.Errorf("can't find table rules file %q in the assets", tableRulesFile) } - return prometheusv1assets.TablePrometheusRuleTemplate.RenderObject(map[string]any{ + return prometheusv1assets.TablePrometheusRuleTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, "groups": rule.Get(), }) @@ -166,7 +166,7 @@ func makePrometheus(sm *scyllav1alpha1.ScyllaDBMonitoring, soc *scyllav1alpha1.S resources = spec.Resources } - return prometheusv1assets.PrometheusTemplate.RenderObject(map[string]any{ + return prometheusv1assets.PrometheusTemplate.Get().RenderObject(map[string]any{ "prometheusVersion": soc.Status.PrometheusVersion, "namespace": sm.Namespace, "scyllaDBMonitoringName": sm.Name, @@ -191,7 +191,7 @@ func makePrometheusIngress(sm *scyllav1alpha1.ScyllaDBMonitoring) (*networkingv1 return nil, "", nil } - return prometheusv1assets.PrometheusIngressTemplate.RenderObject(map[string]any{ + return prometheusv1assets.PrometheusIngressTemplate.Get().RenderObject(map[string]any{ "scyllaDBMonitoringName": sm.Name, "dnsDomains": ingressOptions.DNSDomains, "ingressAnnotations": ingressOptions.Annotations, diff --git a/pkg/util/lazy/lazy.go b/pkg/util/lazy/lazy.go new file mode 100644 index 00000000000..1ba848459e9 --- /dev/null +++ b/pkg/util/lazy/lazy.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 ScyllaDB. + +package lazy + +import "sync" + +type Value[T any] struct { + init sync.Once + newFunc func() T + value T +} + +// New returns a Value which is initialized upon first call to Get using newFunc. +func New[T any](newFunc func() T) *Value[T] { + return &Value[T]{ + init: sync.Once{}, + newFunc: newFunc, + } +} + +func (li *Value[T]) Get() T { + li.init.Do(func() { + li.value = li.newFunc() + }) + return li.value +} diff --git a/test/e2e/fixture/scylla/registry.go b/test/e2e/fixture/scylla/registry.go index 3044be19ad4..c45f7f27c30 100644 --- a/test/e2e/fixture/scylla/registry.go +++ b/test/e2e/fixture/scylla/registry.go @@ -12,7 +12,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) -func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string) assets.ObjectTemplate[T] { +func ParseObjectTemplateOrDie[T runtime.Object](name, tmplString string) *assets.ObjectTemplate[T] { return assets.ParseObjectTemplateOrDie[T](name, tmplString, assets.TemplateFuncs, scheme.Codecs.UniversalDeserializer()) } diff --git a/test/e2e/set/scylladbmonitoring/scylladbmonitoring.go b/test/e2e/set/scylladbmonitoring/scylladbmonitoring.go index 42cf972d7f5..40751b3719e 100644 --- a/test/e2e/set/scylladbmonitoring/scylladbmonitoring.go +++ b/test/e2e/set/scylladbmonitoring/scylladbmonitoring.go @@ -78,7 +78,7 @@ var expectedPlatformFolderDashboardSearchResponse []gapi.FolderDashboardSearchRe var expectedPlatformHomeDashboardUID string var _ = g.BeforeSuite(func() { - for dashboardFolderName, dashboardFolder := range grafanav1alpha1assets.GrafanaDashboardsPlatform { + for dashboardFolderName, dashboardFolder := range grafanav1alpha1assets.GrafanaDashboardsPlatform.Get() { for _, dashboardString := range dashboardFolder { o.Expect(dashboardString).NotTo(o.BeEmpty()) @@ -104,8 +104,8 @@ var _ = g.BeforeSuite(func() { o.Expect(homeDashboardFile).NotTo(o.ContainSubstring("/")) homeDashboardFile += ".gz.base64" - o.Expect(grafanav1alpha1assets.GrafanaDashboardsPlatform).To(o.HaveKey(homeDashboardDir)) - homeDashboardFolder := grafanav1alpha1assets.GrafanaDashboardsPlatform[homeDashboardDir] + o.Expect(grafanav1alpha1assets.GrafanaDashboardsPlatform.Get()).To(o.HaveKey(homeDashboardDir)) + homeDashboardFolder := grafanav1alpha1assets.GrafanaDashboardsPlatform.Get()[homeDashboardDir] o.Expect(homeDashboardFolder).NotTo(o.BeEmpty()) o.Expect(homeDashboardFolder).To(o.HaveKey(homeDashboardFile)) homeDashboardString := homeDashboardFolder[homeDashboardFile]