From 37ac629d2eea77d7eb2ab6f25facd4b3b0b92043 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Tue, 30 Jan 2024 15:50:58 +0100 Subject: [PATCH] extract webhooks to seperate package to avoid import cycle (#766) * extract webhooks to seperate package to avoid import cycle Signed-off-by: Andreas Gerstmayr * move defaults tests Signed-off-by: Andreas Gerstmayr --------- Signed-off-by: Andreas Gerstmayr --- .../v1alpha1/tempomonolithic_defaults.go | 49 + ...st.go => tempomonolithic_defaults_test.go} | 0 .../tempo/v1alpha1/tempomonolithic_webhook.go | 96 -- apis/tempo/v1alpha1/zz_generated.deepcopy.go | 18 +- cmd/generate/main.go | 3 +- cmd/start/main.go | 6 +- .../tempo/tempostack_create_or_update.go | 3 +- docs/operator/api.md | 58 -- internal/webhooks/tempomonolithic_webhook.go | 84 ++ .../webhooks}/tempostack_webhook.go | 75 +- .../webhooks}/tempostack_webhook_test.go | 836 +++++++++--------- .../webhooks}/webhook_suite_test.go | 9 +- 12 files changed, 605 insertions(+), 632 deletions(-) create mode 100644 apis/tempo/v1alpha1/tempomonolithic_defaults.go rename apis/tempo/v1alpha1/{tempomonolithic_webhook_test.go => tempomonolithic_defaults_test.go} (100%) delete mode 100644 apis/tempo/v1alpha1/tempomonolithic_webhook.go create mode 100644 internal/webhooks/tempomonolithic_webhook.go rename {apis/tempo/v1alpha1 => internal/webhooks}/tempostack_webhook.go (86%) rename {apis/tempo/v1alpha1 => internal/webhooks}/tempostack_webhook_test.go (62%) rename {apis/tempo/v1alpha1 => internal/webhooks}/webhook_suite_test.go (92%) diff --git a/apis/tempo/v1alpha1/tempomonolithic_defaults.go b/apis/tempo/v1alpha1/tempomonolithic_defaults.go new file mode 100644 index 000000000..7f4ca0973 --- /dev/null +++ b/apis/tempo/v1alpha1/tempomonolithic_defaults.go @@ -0,0 +1,49 @@ +package v1alpha1 + +import "k8s.io/apimachinery/pkg/api/resource" + +var ( + tenGBQuantity = resource.MustParse("10Gi") +) + +// Default sets all default values in a central place, instead of setting it at every place where the value is accessed. +// NOTE: This function is called inside the Reconcile loop, NOT in the webhook. +// We want to keep the CR as minimal as the user configures it, and not modify it in any way (except for upgrades). +func (r *TempoMonolithic) Default() { + if r.Spec.Storage == nil { + r.Spec.Storage = &MonolithicStorageSpec{} + } + + if r.Spec.Storage.Traces.Backend == "" { + r.Spec.Storage.Traces.Backend = MonolithicTracesStorageBackendMemory + } + + if r.Spec.Storage.Traces.Backend != MonolithicTracesStorageBackendMemory && r.Spec.Storage.Traces.WAL == nil { + r.Spec.Storage.Traces.WAL = &MonolithicTracesStorageWALSpec{ + Size: tenGBQuantity, + } + } + + if r.Spec.Storage.Traces.Backend == MonolithicTracesStorageBackendPV && r.Spec.Storage.Traces.PV == nil { + r.Spec.Storage.Traces.PV = &MonolithicTracesStoragePVSpec{ + Size: tenGBQuantity, + } + } + + if r.Spec.Ingestion == nil { + r.Spec.Ingestion = &MonolithicIngestionSpec{} + } + if r.Spec.Ingestion.OTLP == nil { + r.Spec.Ingestion.OTLP = &MonolithicIngestionOTLPSpec{} + } + if r.Spec.Ingestion.OTLP.GRPC == nil { + r.Spec.Ingestion.OTLP.GRPC = &MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + } + } + if r.Spec.Ingestion.OTLP.HTTP == nil { + r.Spec.Ingestion.OTLP.HTTP = &MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + } + } +} diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go b/apis/tempo/v1alpha1/tempomonolithic_defaults_test.go similarity index 100% rename from apis/tempo/v1alpha1/tempomonolithic_webhook_test.go rename to apis/tempo/v1alpha1/tempomonolithic_defaults_test.go diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook.go b/apis/tempo/v1alpha1/tempomonolithic_webhook.go deleted file mode 100644 index e0d240986..000000000 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook.go +++ /dev/null @@ -1,96 +0,0 @@ -package v1alpha1 - -import ( - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/validation/field" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// SetupWebhookWithManager will setup the manager to manage the webhooks. -func (r *TempoMonolithic) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -// Default sets all default values in a central place, instead of setting it at every place where the value is accessed. -// NOTE: This function is called inside the Reconcile loop, NOT in the webhook. -// We want to keep the CR as minimal as the user configures it, and not modify it in any way (except for upgrades). -func (r *TempoMonolithic) Default() { - if r.Spec.Storage == nil { - r.Spec.Storage = &MonolithicStorageSpec{} - } - - if r.Spec.Storage.Traces.Backend == "" { - r.Spec.Storage.Traces.Backend = MonolithicTracesStorageBackendMemory - } - - if r.Spec.Storage.Traces.Backend != MonolithicTracesStorageBackendMemory && r.Spec.Storage.Traces.WAL == nil { - r.Spec.Storage.Traces.WAL = &MonolithicTracesStorageWALSpec{ - Size: tenGBQuantity, - } - } - - if r.Spec.Storage.Traces.Backend == MonolithicTracesStorageBackendPV && r.Spec.Storage.Traces.PV == nil { - r.Spec.Storage.Traces.PV = &MonolithicTracesStoragePVSpec{ - Size: tenGBQuantity, - } - } - - if r.Spec.Ingestion == nil { - r.Spec.Ingestion = &MonolithicIngestionSpec{} - } - if r.Spec.Ingestion.OTLP == nil { - r.Spec.Ingestion.OTLP = &MonolithicIngestionOTLPSpec{} - } - if r.Spec.Ingestion.OTLP.GRPC == nil { - r.Spec.Ingestion.OTLP.GRPC = &MonolithicIngestionOTLPProtocolsGRPCSpec{ - Enabled: true, - } - } - if r.Spec.Ingestion.OTLP.HTTP == nil { - r.Spec.Ingestion.OTLP.HTTP = &MonolithicIngestionOTLPProtocolsHTTPSpec{ - Enabled: true, - } - } -} - -//+kubebuilder:webhook:path=/validate-tempo-grafana-com-v1alpha1-tempomonolithic,mutating=false,failurePolicy=fail,sideEffects=None,groups=tempo.grafana.com,resources=tempomonolithics,verbs=create;update,versions=v1alpha1,name=vtempomonolithic.kb.io,admissionReviewVersions=v1 - -var _ webhook.Validator = &TempoMonolithic{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *TempoMonolithic) ValidateCreate() (admission.Warnings, error) { - return r.validate() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *TempoMonolithic) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - return r.validate() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *TempoMonolithic) ValidateDelete() (admission.Warnings, error) { - // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. - return r.validate() -} - -func (tempo *TempoMonolithic) validate() (admission.Warnings, error) { - log := ctrl.Log.WithName("tempomonolithic-webhook") - log.V(1).Info("running validating webhook", "name", tempo.Name) - - allWarnings := admission.Warnings{} - allErrors := field.ErrorList{} - - if tempo.Spec.ExtraConfig != nil && len(tempo.Spec.ExtraConfig.Tempo.Raw) > 0 { - allWarnings = append(allWarnings, "overriding Tempo configuration could potentially break the deployment, use it carefully") - } - - if len(allErrors) == 0 { - return allWarnings, nil - } - return allWarnings, apierrors.NewInvalid(tempo.GroupVersionKind().GroupKind(), tempo.Name, allErrors) -} diff --git a/apis/tempo/v1alpha1/zz_generated.deepcopy.go b/apis/tempo/v1alpha1/zz_generated.deepcopy.go index ea10afd40..2ffc63737 100644 --- a/apis/tempo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/tempo/v1alpha1/zz_generated.deepcopy.go @@ -8,7 +8,7 @@ package v1alpha1 import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" + runtime "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -165,22 +165,6 @@ func (in *ComponentStatus) DeepCopy() *ComponentStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Defaulter) DeepCopyInto(out *Defaulter) { - *out = *in - in.ctrlConfig.DeepCopyInto(&out.ctrlConfig) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Defaulter. -func (in *Defaulter) DeepCopy() *Defaulter { - if in == nil { - return nil - } - out := new(Defaulter) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExtraConfigSpec) DeepCopyInto(out *ExtraConfigSpec) { *out = *in diff --git a/cmd/generate/main.go b/cmd/generate/main.go index ddf3483b5..8ab779279 100644 --- a/cmd/generate/main.go +++ b/cmd/generate/main.go @@ -21,6 +21,7 @@ import ( controllers "github.com/grafana/tempo-operator/controllers/tempo" "github.com/grafana/tempo-operator/internal/manifests" "github.com/grafana/tempo-operator/internal/manifests/manifestutils" + "github.com/grafana/tempo-operator/internal/webhooks" ) // yamlOrJsonDecoderBufferSize determines how far into the stream @@ -42,7 +43,7 @@ func loadSpec(r io.Reader) (v1alpha1.TempoStack, error) { func build(params manifestutils.Params) ([]client.Object, error) { // apply default values from Defaulter webhook - defaulterWebhook := v1alpha1.NewDefaulter(params.CtrlConfig) + defaulterWebhook := webhooks.NewDefaulter(params.CtrlConfig) err := defaulterWebhook.Default(context.Background(), ¶ms.Tempo) if err != nil { return nil, err diff --git a/cmd/start/main.go b/cmd/start/main.go index d9bb5b119..ef3ce598b 100644 --- a/cmd/start/main.go +++ b/cmd/start/main.go @@ -17,11 +17,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" - tempov1alpha1 "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" "github.com/grafana/tempo-operator/cmd" controllers "github.com/grafana/tempo-operator/controllers/tempo" "github.com/grafana/tempo-operator/internal/upgrade" "github.com/grafana/tempo-operator/internal/version" + "github.com/grafana/tempo-operator/internal/webhooks" //+kubebuilder:scaffold:imports ) @@ -78,11 +78,11 @@ func start(c *cobra.Command, args []string) { enableWebhooks := os.Getenv("ENABLE_WEBHOOKS") != "false" if enableWebhooks { - if err = (&tempov1alpha1.TempoStack{}).SetupWebhookWithManager(mgr, ctrlConfig); err != nil { + if err = (&webhooks.TempoStackWebhook{}).SetupWebhookWithManager(mgr, ctrlConfig); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "TempoStack") os.Exit(1) } - if err = (&tempov1alpha1.TempoMonolithic{}).SetupWebhookWithManager(mgr); err != nil { + if err = (&webhooks.TempoMonolithicWebhook{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "TempoMonolithic") os.Exit(1) } diff --git a/controllers/tempo/tempostack_create_or_update.go b/controllers/tempo/tempostack_create_or_update.go index c2d912187..529d42a46 100644 --- a/controllers/tempo/tempostack_create_or_update.go +++ b/controllers/tempo/tempostack_create_or_update.go @@ -22,6 +22,7 @@ import ( "github.com/grafana/tempo-operator/internal/manifests/manifestutils" "github.com/grafana/tempo-operator/internal/status" "github.com/grafana/tempo-operator/internal/tlsprofile" + "github.com/grafana/tempo-operator/internal/webhooks" ) func listErrors(fieldErrs field.ErrorList) string { @@ -81,7 +82,7 @@ func (r *TempoStackReconciler) createOrUpdate(ctx context.Context, log logr.Logg } } - if err = v1alpha1.ValidateTenantConfigs(tempo); err != nil { + if err = webhooks.ValidateTenantConfigs(tempo); err != nil { return &status.ConfigurationError{ Message: fmt.Sprintf("Invalid tenants configuration: %s", err), Reason: v1alpha1.ReasonInvalidTenantsConfiguration, diff --git a/docs/operator/api.md b/docs/operator/api.md index abf7529a2..1e6b8fc4f 100644 --- a/docs/operator/api.md +++ b/docs/operator/api.md @@ -518,56 +518,6 @@ PodStatusMap -## Defaulter { #tempo-grafana-com-v1alpha1-Defaulter } - -
- -

Defaulter implements the CustomDefaulter interface.

- -
- - - - - - - - - - - - - - - - - - - - - - - - - -
FieldDescription
- -ctrlConfig
- - - - - -Feature Gates.ProjectConfig - - - - - -
- -
- ## ExtraConfigSpec { #tempo-grafana-com-v1alpha1-ExtraConfigSpec }

@@ -4308,14 +4258,6 @@ responsible for decrypting traffic.

and re-encrypt using a new certificate.

-

"passthrough"

- - - -

"edge"

- - - diff --git a/internal/webhooks/tempomonolithic_webhook.go b/internal/webhooks/tempomonolithic_webhook.go new file mode 100644 index 000000000..5ce736827 --- /dev/null +++ b/internal/webhooks/tempomonolithic_webhook.go @@ -0,0 +1,84 @@ +package webhooks + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + tempov1alpha1 "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" +) + +// TempoMonolithicWebhook provides webhooks for TempoMonolithic CR. +type TempoMonolithicWebhook struct { +} + +// SetupWebhookWithManager will setup the manager to manage the webhooks. +func (w *TempoMonolithicWebhook) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&tempov1alpha1.TempoMonolithic{}). + WithValidator(&monolithicValidator{client: mgr.GetClient()}). + Complete() +} + +//+kubebuilder:webhook:path=/validate-tempo-grafana-com-v1alpha1-tempomonolithic,mutating=false,failurePolicy=fail,sideEffects=None,groups=tempo.grafana.com,resources=tempomonolithics,verbs=create;update,versions=v1alpha1,name=vtempomonolithic.kb.io,admissionReviewVersions=v1 + +type monolithicValidator struct { + client client.Client +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. +func (v *monolithicValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return v.validate(ctx, obj) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. +func (v *monolithicValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + return v.validate(ctx, newObj) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. +func (v *monolithicValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. + return nil, nil +} + +func (v *monolithicValidator) validate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + tempo, ok := obj.(*tempov1alpha1.TempoMonolithic) + if !ok { + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a TempoMonolithic object but got %T", obj)) + } + + log := ctrl.LoggerFrom(ctx).WithName("tempomonolithic-webhook") + log.V(1).Info("running validating webhook", "name", tempo.Name) + + // We do not modify the Kubernetes object in the defaulter webhook, + // but still apply some default values in-memory. + tempo.Default() + + allWarnings := admission.Warnings{} + allErrors := field.ErrorList{} + addValidation := func(warnings admission.Warnings, errors field.ErrorList) { + allWarnings = append(allWarnings, warnings...) + allErrors = append(allErrors, errors...) + } + + addValidation(v.validateExtraConfig(*tempo)) + + if len(allErrors) == 0 { + return allWarnings, nil + } + return allWarnings, apierrors.NewInvalid(tempo.GroupVersionKind().GroupKind(), tempo.Name, allErrors) +} + +func (v *monolithicValidator) validateExtraConfig(tempo tempov1alpha1.TempoMonolithic) (admission.Warnings, field.ErrorList) { //nolint:unparam + if tempo.Spec.ExtraConfig != nil && len(tempo.Spec.ExtraConfig.Tempo.Raw) > 0 { + return admission.Warnings{"overriding Tempo configuration could potentially break the deployment, use it carefully"}, nil + } + return nil, nil +} diff --git a/apis/tempo/v1alpha1/tempostack_webhook.go b/internal/webhooks/tempostack_webhook.go similarity index 86% rename from apis/tempo/v1alpha1/tempostack_webhook.go rename to internal/webhooks/tempostack_webhook.go index 67f525104..b34f2e700 100644 --- a/apis/tempo/v1alpha1/tempostack_webhook.go +++ b/internal/webhooks/tempostack_webhook.go @@ -1,4 +1,4 @@ -package v1alpha1 +package webhooks import ( "context" @@ -20,7 +20,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "github.com/grafana/tempo-operator/apis/config/v1alpha1" + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" "github.com/grafana/tempo-operator/internal/autodetect" "github.com/grafana/tempo-operator/internal/manifests/naming" ) @@ -30,14 +31,18 @@ var ( tenGBQuantity = resource.MustParse("10Gi") ) +// TempoStackWebhook provides webhooks for TempoStack CR. +type TempoStackWebhook struct { +} + const maxLabelLength = 63 -const defaultRouteGatewayTLSTermination = TLSRouteTerminationTypePassthrough -const defaultUITLSTermination = TLSRouteTerminationTypeEdge +const defaultRouteGatewayTLSTermination = v1alpha1.TLSRouteTerminationTypePassthrough +const defaultUITLSTermination = v1alpha1.TLSRouteTerminationTypeEdge // SetupWebhookWithManager initializes the webhook. -func (r *TempoStack) SetupWebhookWithManager(mgr ctrl.Manager, ctrlConfig v1alpha1.ProjectConfig) error { +func (w *TempoStackWebhook) SetupWebhookWithManager(mgr ctrl.Manager, ctrlConfig configv1alpha1.ProjectConfig) error { return ctrl.NewWebhookManagedBy(mgr). - For(r). + For(&v1alpha1.TempoStack{}). WithDefaulter(NewDefaulter(ctrlConfig)). WithValidator(&validator{client: mgr.GetClient(), ctrlConfig: ctrlConfig}). Complete() @@ -46,7 +51,7 @@ func (r *TempoStack) SetupWebhookWithManager(mgr ctrl.Manager, ctrlConfig v1alph //+kubebuilder:webhook:path=/mutate-tempo-grafana-com-v1alpha1-tempostack,mutating=true,failurePolicy=fail,sideEffects=None,groups=tempo.grafana.com,resources=tempostacks,verbs=create;update,versions=v1alpha1,name=mtempostack.tempo.grafana.com,admissionReviewVersions=v1 // NewDefaulter creates a new instance of Defaulter, which implements functions for setting defaults on the Tempo CR. -func NewDefaulter(ctrlConfig v1alpha1.ProjectConfig) *Defaulter { +func NewDefaulter(ctrlConfig configv1alpha1.ProjectConfig) *Defaulter { return &Defaulter{ ctrlConfig: ctrlConfig, } @@ -54,12 +59,12 @@ func NewDefaulter(ctrlConfig v1alpha1.ProjectConfig) *Defaulter { // Defaulter implements the CustomDefaulter interface. type Defaulter struct { - ctrlConfig v1alpha1.ProjectConfig + ctrlConfig configv1alpha1.ProjectConfig } // Default applies default values to a Kubernetes object. func (d *Defaulter) Default(ctx context.Context, obj runtime.Object) error { - r, ok := obj.(*TempoStack) + r, ok := obj.(*v1alpha1.TempoStack) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a TempoStack object but got %T", obj)) } @@ -118,16 +123,16 @@ func (d *Defaulter) Default(ctx context.Context, obj runtime.Object) error { } // if tenant mode is Openshift, ingress type should be route by default. - if r.Spec.Tenants != nil && r.Spec.Tenants.Mode == ModeOpenShift && r.Spec.Template.Gateway.Ingress.Type == "" { - r.Spec.Template.Gateway.Ingress.Type = IngressTypeRoute + if r.Spec.Tenants != nil && r.Spec.Tenants.Mode == v1alpha1.ModeOpenShift && r.Spec.Template.Gateway.Ingress.Type == "" { + r.Spec.Template.Gateway.Ingress.Type = v1alpha1.IngressTypeRoute } - if r.Spec.Template.Gateway.Ingress.Type == IngressTypeRoute && r.Spec.Template.Gateway.Ingress.Route.Termination == "" { + if r.Spec.Template.Gateway.Ingress.Type == v1alpha1.IngressTypeRoute && r.Spec.Template.Gateway.Ingress.Route.Termination == "" { r.Spec.Template.Gateway.Ingress.Route.Termination = defaultRouteGatewayTLSTermination } // Terminate TLS of the JaegerQuery Route on the Edge by default - if r.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type == IngressTypeRoute && r.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Route.Termination == "" { + if r.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type == v1alpha1.IngressTypeRoute && r.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Route.Termination == "" { r.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Route.Termination = defaultUITLSTermination } @@ -145,7 +150,7 @@ func (d *Defaulter) Default(ctx context.Context, obj runtime.Object) error { type validator struct { client client.Client - ctrlConfig v1alpha1.ProjectConfig + ctrlConfig configv1alpha1.ProjectConfig } func (v *validator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { @@ -161,7 +166,7 @@ func (v *validator) ValidateDelete(ctx context.Context, obj runtime.Object) (adm return nil, nil } -func (v *validator) validateServiceAccount(ctx context.Context, tempo TempoStack) field.ErrorList { +func (v *validator) validateServiceAccount(ctx context.Context, tempo v1alpha1.TempoStack) field.ErrorList { var allErrs field.ErrorList // the default service account gets created later in the reconciliation loop @@ -180,7 +185,7 @@ func (v *validator) validateServiceAccount(ctx context.Context, tempo TempoStack return allErrs } -func (v *validator) validateStorageSecret(ctx context.Context, tempo TempoStack) (admission.Warnings, field.ErrorList) { +func (v *validator) validateStorageSecret(ctx context.Context, tempo v1alpha1.TempoStack) (admission.Warnings, field.ErrorList) { storageSecret := &corev1.Secret{} err := v.client.Get(ctx, types.NamespacedName{Namespace: tempo.Namespace, Name: tempo.Spec.Storage.Secret.Name}, storageSecret) if err != nil { @@ -189,10 +194,10 @@ func (v *validator) validateStorageSecret(ctx context.Context, tempo TempoStack) return admission.Warnings{fmt.Sprintf("Secret '%s' does not exist", tempo.Spec.Storage.Secret.Name)}, field.ErrorList{} } - return admission.Warnings{}, ValidateStorageSecret(tempo, *storageSecret) + return admission.Warnings{}, v1alpha1.ValidateStorageSecret(tempo, *storageSecret) } -func (v *validator) validateStorageCA(ctx context.Context, tempo TempoStack) (admission.Warnings, field.ErrorList) { +func (v *validator) validateStorageCA(ctx context.Context, tempo v1alpha1.TempoStack) (admission.Warnings, field.ErrorList) { caConfigMap := &corev1.ConfigMap{} err := v.client.Get(ctx, types.NamespacedName{Namespace: tempo.Namespace, Name: tempo.Spec.Storage.TLS.CA}, caConfigMap) if err != nil { @@ -201,10 +206,10 @@ func (v *validator) validateStorageCA(ctx context.Context, tempo TempoStack) (ad return admission.Warnings{fmt.Sprintf("ConfigMap '%s' does not exist", tempo.Spec.Storage.TLS.CA)}, field.ErrorList{} } - return admission.Warnings{}, ValidateStorageCAConfigMap(*caConfigMap) + return admission.Warnings{}, v1alpha1.ValidateStorageCAConfigMap(*caConfigMap) } -func (v *validator) validateReplicationFactor(tempo TempoStack) field.ErrorList { +func (v *validator) validateReplicationFactor(tempo v1alpha1.TempoStack) field.ErrorList { // Validate minimum quorum on ingestors according to replicas and replication factor replicatonFactor := tempo.Spec.ReplicationFactor // Ingester replicas should not be nil at this point, due defauler. @@ -222,10 +227,10 @@ func (v *validator) validateReplicationFactor(tempo TempoStack) field.ErrorList return nil } -func (v *validator) validateQueryFrontend(tempo TempoStack) field.ErrorList { +func (v *validator) validateQueryFrontend(tempo v1alpha1.TempoStack) field.ErrorList { path := field.NewPath("spec").Child("template").Child("queryFrontend").Child("jaegerQuery").Child("ingress").Child("type") - if tempo.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type != IngressTypeNone && !tempo.Spec.Template.QueryFrontend.JaegerQuery.Enabled { + if tempo.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type != v1alpha1.IngressTypeNone && !tempo.Spec.Template.QueryFrontend.JaegerQuery.Enabled { return field.ErrorList{field.Invalid( path, tempo.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type, @@ -233,7 +238,7 @@ func (v *validator) validateQueryFrontend(tempo TempoStack) field.ErrorList { )} } - if tempo.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type == IngressTypeRoute && !v.ctrlConfig.Gates.OpenShift.OpenShiftRoute { + if tempo.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type == v1alpha1.IngressTypeRoute && !v.ctrlConfig.Gates.OpenShift.OpenShiftRoute { return field.ErrorList{field.Invalid( path, tempo.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type, @@ -255,10 +260,10 @@ func (v *validator) validateQueryFrontend(tempo TempoStack) field.ErrorList { return nil } -func (v *validator) validateGateway(tempo TempoStack) field.ErrorList { +func (v *validator) validateGateway(tempo v1alpha1.TempoStack) field.ErrorList { path := field.NewPath("spec").Child("template").Child("gateway").Child("enabled") if tempo.Spec.Template.Gateway.Enabled { - if tempo.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type != IngressTypeNone { + if tempo.Spec.Template.QueryFrontend.JaegerQuery.Ingress.Type != v1alpha1.IngressTypeNone { return field.ErrorList{ field.Invalid(path, tempo.Spec.Template.Gateway.Enabled, "cannot enable gateway and jaeger query ingress at the same time, please use the Jaeger UI from the gateway", @@ -272,7 +277,7 @@ func (v *validator) validateGateway(tempo TempoStack) field.ErrorList { )} } - if tempo.Spec.Template.Gateway.Ingress.Type == IngressTypeRoute && !v.ctrlConfig.Gates.OpenShift.OpenShiftRoute { + if tempo.Spec.Template.Gateway.Ingress.Type == v1alpha1.IngressTypeRoute && !v.ctrlConfig.Gates.OpenShift.OpenShiftRoute { return field.ErrorList{field.Invalid( field.NewPath("spec").Child("template").Child("gateway").Child("ingress").Child("type"), tempo.Spec.Template.Gateway.Ingress.Type, @@ -291,7 +296,7 @@ func (v *validator) validateGateway(tempo TempoStack) field.ErrorList { return nil } -func (v *validator) validateObservability(tempo TempoStack) field.ErrorList { +func (v *validator) validateObservability(tempo v1alpha1.TempoStack) field.ErrorList { observabilityBase := field.NewPath("spec").Child("observability") metricsBase := observabilityBase.Child("metrics") @@ -351,7 +356,7 @@ func (v *validator) validateObservability(tempo TempoStack) field.ErrorList { return nil } -func (v *validator) validateTenantConfigs(tempo TempoStack) field.ErrorList { +func (v *validator) validateTenantConfigs(tempo v1alpha1.TempoStack) field.ErrorList { if err := ValidateTenantConfigs(tempo); err != nil { return field.ErrorList{ field.Invalid( @@ -363,7 +368,7 @@ func (v *validator) validateTenantConfigs(tempo TempoStack) field.ErrorList { return nil } -func (v *validator) validateStackName(tempo TempoStack) field.ErrorList { +func (v *validator) validateStackName(tempo v1alpha1.TempoStack) field.ErrorList { // We need to check this because the name is used as a label value for app.kubernetes.io/instance // Only validate the length, because the DNS rules are enforced by the functions in the `naming` package. if len(tempo.Name) > maxLabelLength { @@ -377,7 +382,7 @@ func (v *validator) validateStackName(tempo TempoStack) field.ErrorList { return nil } -func (v *validator) validateDeprecatedFields(tempo TempoStack) field.ErrorList { +func (v *validator) validateDeprecatedFields(tempo v1alpha1.TempoStack) field.ErrorList { if tempo.Spec.LimitSpec.Global.Query.MaxSearchBytesPerTrace != nil { return field.ErrorList{ field.Invalid( @@ -400,7 +405,7 @@ func (v *validator) validateDeprecatedFields(tempo TempoStack) field.ErrorList { return nil } -func (v *validator) validateReceiverTLS(tempo TempoStack) field.ErrorList { +func (v *validator) validateReceiverTLS(tempo v1alpha1.TempoStack) field.ErrorList { spec := tempo.Spec.Template.Distributor.TLS if spec.Enabled { if spec.Cert == "" { @@ -416,7 +421,7 @@ func (v *validator) validateReceiverTLS(tempo TempoStack) field.ErrorList { } func (v *validator) validate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - tempo, ok := obj.(*TempoStack) + tempo, ok := obj.(*v1alpha1.TempoStack) if !ok { return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a TempoStack object but got %T", obj)) } @@ -464,13 +469,13 @@ func (v *validator) validate(ctx context.Context, obj runtime.Object) (admission } // ValidateTenantConfigs validates the tenants mode specification. -func ValidateTenantConfigs(tempo TempoStack) error { +func ValidateTenantConfigs(tempo v1alpha1.TempoStack) error { if tempo.Spec.Tenants == nil { return nil } tenants := tempo.Spec.Tenants - if tenants.Mode == ModeStatic { + if tenants.Mode == v1alpha1.ModeStatic { // If the static mode is combined with the gateway, we will need the following fields // otherwise this will just enable tempo multitenancy without the gateway if tempo.Spec.Template.Gateway.Enabled { @@ -490,7 +495,7 @@ func ValidateTenantConfigs(tempo TempoStack) error { return fmt.Errorf("spec.tenants.authorization.roleBindings is required in static mode") } } - } else if tenants.Mode == ModeOpenShift { + } else if tenants.Mode == v1alpha1.ModeOpenShift { if !tempo.Spec.Template.Gateway.Enabled { return fmt.Errorf("openshift mode requires gateway enabled") } diff --git a/apis/tempo/v1alpha1/tempostack_webhook_test.go b/internal/webhooks/tempostack_webhook_test.go similarity index 62% rename from apis/tempo/v1alpha1/tempostack_webhook_test.go rename to internal/webhooks/tempostack_webhook_test.go index 07e7717af..4b6c21928 100644 --- a/apis/tempo/v1alpha1/tempostack_webhook_test.go +++ b/internal/webhooks/tempostack_webhook_test.go @@ -1,8 +1,9 @@ -package v1alpha1 +package webhooks import ( "context" "fmt" + "testing" "time" @@ -18,14 +19,15 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "github.com/grafana/tempo-operator/apis/config/v1alpha1" + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" "github.com/grafana/tempo-operator/internal/manifests/naming" ) func TestDefault(t *testing.T) { defaulter := &Defaulter{ - ctrlConfig: v1alpha1.ProjectConfig{ - DefaultImages: v1alpha1.ImagesSpec{ + ctrlConfig: configv1alpha1.ProjectConfig{ + DefaultImages: configv1alpha1.ImagesSpec{ Tempo: "docker.io/grafana/tempo:x.y.z", TempoQuery: "docker.io/grafana/tempo-query:x.y.z", TempoGateway: "docker.io/observatorium/gateway:1.2.3", @@ -37,41 +39,41 @@ func TestDefault(t *testing.T) { defaultDefaultResultLimit := 20 tests := []struct { - input *TempoStack - expected *TempoStack + input *v1alpha1.TempoStack + expected *v1alpha1.TempoStack name string }{ { name: "no action default values are provided", - input: &TempoStack{ + input: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: TempoStackSpec{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 2, - Images: v1alpha1.ImagesSpec{ + Images: configv1alpha1.ImagesSpec{ Tempo: "docker.io/grafana/tempo:1.2.3", TempoQuery: "docker.io/grafana/tempo-query:1.2.3", TempoGateway: "docker.io/observatorium/gateway:1.2.3", TempoGatewayOpa: "docker.io/observatorium/opa-openshift:1.2.4", }, ServiceAccount: "tempo-test", - Retention: RetentionSpec{ - Global: RetentionConfig{ + Retention: v1alpha1.RetentionSpec{ + Global: v1alpha1.RetentionConfig{ Traces: metav1.Duration{Duration: time.Hour}, }, }, StorageSize: resource.MustParse("1Gi"), - LimitSpec: LimitSpec{ - Global: RateLimitSpec{ - Query: QueryLimit{ + LimitSpec: v1alpha1.LimitSpec{ + Global: v1alpha1.RateLimitSpec{ + Query: v1alpha1.QueryLimit{ MaxSearchDuration: metav1.Duration{Duration: 1 * time.Hour}, }, }, }, }, }, - expected: &TempoStack{ + expected: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Labels: map[string]string{ @@ -79,50 +81,50 @@ func TestDefault(t *testing.T) { "tempo.grafana.com/distribution": "upstream", }, }, - Spec: TempoStackSpec{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 2, - Images: v1alpha1.ImagesSpec{ + Images: configv1alpha1.ImagesSpec{ Tempo: "docker.io/grafana/tempo:1.2.3", TempoQuery: "docker.io/grafana/tempo-query:1.2.3", TempoGateway: "docker.io/observatorium/gateway:1.2.3", TempoGatewayOpa: "docker.io/observatorium/opa-openshift:1.2.4", }, ServiceAccount: "tempo-test", - Retention: RetentionSpec{ - Global: RetentionConfig{ + Retention: v1alpha1.RetentionSpec{ + Global: v1alpha1.RetentionConfig{ Traces: metav1.Duration{Duration: time.Hour}, }, }, StorageSize: resource.MustParse("1Gi"), - LimitSpec: LimitSpec{ - Global: RateLimitSpec{ - Query: QueryLimit{ + LimitSpec: v1alpha1.LimitSpec{ + Global: v1alpha1.RateLimitSpec{ + Query: v1alpha1.QueryLimit{ MaxSearchDuration: metav1.Duration{Duration: 1 * time.Hour}, }, }, }, - SearchSpec: SearchSpec{ + SearchSpec: v1alpha1.SearchSpec{ MaxDuration: metav1.Duration{Duration: 0}, DefaultResultLimit: &defaultDefaultResultLimit, }, - Template: TempoTemplateSpec{ - Compactor: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Compactor: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - Distributor: TempoDistributorSpec{ - TempoComponentSpec: TempoComponentSpec{ + Distributor: v1alpha1.TempoDistributorSpec{ + TempoComponentSpec: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - TLS: ReceiversTLSSpec{}, + TLS: v1alpha1.ReceiversTLSSpec{}, }, - Ingester: TempoComponentSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - Querier: TempoComponentSpec{ + Querier: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - QueryFrontend: TempoQueryFrontendSpec{ - TempoComponentSpec: TempoComponentSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + TempoComponentSpec: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, }, @@ -132,12 +134,12 @@ func TestDefault(t *testing.T) { }, { name: "default values are set in the webhook", - input: &TempoStack{ + input: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, }, - expected: &TempoStack{ + expected: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Labels: map[string]string{ @@ -145,45 +147,45 @@ func TestDefault(t *testing.T) { "tempo.grafana.com/distribution": "upstream", }, }, - Spec: TempoStackSpec{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 1, - Images: v1alpha1.ImagesSpec{}, + Images: configv1alpha1.ImagesSpec{}, ServiceAccount: "tempo-test", - Retention: RetentionSpec{ - Global: RetentionConfig{ + Retention: v1alpha1.RetentionSpec{ + Global: v1alpha1.RetentionConfig{ Traces: metav1.Duration{Duration: 48 * time.Hour}, }, }, StorageSize: resource.MustParse("10Gi"), - LimitSpec: LimitSpec{ - Global: RateLimitSpec{ - Query: QueryLimit{ + LimitSpec: v1alpha1.LimitSpec{ + Global: v1alpha1.RateLimitSpec{ + Query: v1alpha1.QueryLimit{ MaxSearchDuration: metav1.Duration{Duration: 0}, }, }, }, - SearchSpec: SearchSpec{ + SearchSpec: v1alpha1.SearchSpec{ MaxDuration: metav1.Duration{Duration: 0}, DefaultResultLimit: &defaultDefaultResultLimit, }, - Template: TempoTemplateSpec{ - Compactor: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Compactor: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - Distributor: TempoDistributorSpec{ - TempoComponentSpec: TempoComponentSpec{ + Distributor: v1alpha1.TempoDistributorSpec{ + TempoComponentSpec: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - TLS: ReceiversTLSSpec{}, + TLS: v1alpha1.ReceiversTLSSpec{}, }, - Ingester: TempoComponentSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - Querier: TempoComponentSpec{ + Querier: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - QueryFrontend: TempoQueryFrontendSpec{ - TempoComponentSpec: TempoComponentSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + TempoComponentSpec: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, }, @@ -193,24 +195,24 @@ func TestDefault(t *testing.T) { }, { name: "use Edge TLS termination if unset", - input: &TempoStack{ + input: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: TempoStackSpec{ - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Spec: v1alpha1.TempoStackSpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - Ingress: IngressSpec{ - Type: IngressTypeRoute, + Ingress: v1alpha1.IngressSpec{ + Type: v1alpha1.IngressTypeRoute, }, }, }, }, }, }, - expected: &TempoStack{ + expected: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Labels: map[string]string{ @@ -218,45 +220,45 @@ func TestDefault(t *testing.T) { "tempo.grafana.com/distribution": "upstream", }, }, - Spec: TempoStackSpec{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 1, - Images: v1alpha1.ImagesSpec{}, + Images: configv1alpha1.ImagesSpec{}, ServiceAccount: "tempo-test", - Retention: RetentionSpec{ - Global: RetentionConfig{ + Retention: v1alpha1.RetentionSpec{ + Global: v1alpha1.RetentionConfig{ Traces: metav1.Duration{Duration: 48 * time.Hour}, }, }, StorageSize: resource.MustParse("10Gi"), - SearchSpec: SearchSpec{ + SearchSpec: v1alpha1.SearchSpec{ MaxDuration: metav1.Duration{Duration: 0}, DefaultResultLimit: &defaultDefaultResultLimit, }, - Template: TempoTemplateSpec{ - Compactor: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Compactor: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - Distributor: TempoDistributorSpec{ - TempoComponentSpec: TempoComponentSpec{ + Distributor: v1alpha1.TempoDistributorSpec{ + TempoComponentSpec: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - TLS: ReceiversTLSSpec{}, + TLS: v1alpha1.ReceiversTLSSpec{}, }, - Ingester: TempoComponentSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - Querier: TempoComponentSpec{ + Querier: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - QueryFrontend: TempoQueryFrontendSpec{ - TempoComponentSpec: TempoComponentSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + TempoComponentSpec: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, - JaegerQuery: JaegerQuerySpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "route", - Route: RouteSpec{ + Route: v1alpha1.RouteSpec{ Termination: "edge", }, }, @@ -278,20 +280,20 @@ func TestDefault(t *testing.T) { } func TestValidateStorageSecret(t *testing.T) { - tempoAzure := TempoStack{ - Spec: TempoStackSpec{ - Storage: ObjectStorageSpec{ - Secret: ObjectStorageSecretSpec{ + tempoAzure := v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Storage: v1alpha1.ObjectStorageSpec{ + Secret: v1alpha1.ObjectStorageSecretSpec{ Name: "testsecret", Type: "azure", }, }, }, } - tempoS3 := TempoStack{ - Spec: TempoStackSpec{ - Storage: ObjectStorageSpec{ - Secret: ObjectStorageSecretSpec{ + tempoS3 := v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Storage: v1alpha1.ObjectStorageSpec{ + Secret: v1alpha1.ObjectStorageSecretSpec{ Name: "testsecret", Type: "s3", }, @@ -299,10 +301,10 @@ func TestValidateStorageSecret(t *testing.T) { }, } - tempoUnknown := TempoStack{ - Spec: TempoStackSpec{ - Storage: ObjectStorageSpec{ - Secret: ObjectStorageSecretSpec{ + tempoUnknown := v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Storage: v1alpha1.ObjectStorageSpec{ + Secret: v1alpha1.ObjectStorageSecretSpec{ Name: "testsecret", Type: "unknown", }, @@ -310,10 +312,10 @@ func TestValidateStorageSecret(t *testing.T) { }, } - tempoEmtpyType := TempoStack{ - Spec: TempoStackSpec{ - Storage: ObjectStorageSpec{ - Secret: ObjectStorageSecretSpec{ + tempoEmtpyType := v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Storage: v1alpha1.ObjectStorageSpec{ + Secret: v1alpha1.ObjectStorageSecretSpec{ Name: "testsecret", Type: "", }, @@ -323,7 +325,7 @@ func TestValidateStorageSecret(t *testing.T) { type Test struct { name string - tempo TempoStack + tempo v1alpha1.TempoStack input corev1.Secret expected field.ErrorList } @@ -447,7 +449,7 @@ func TestValidateStorageSecret(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - errs := ValidateStorageSecret(test.tempo, test.input) + errs := v1alpha1.ValidateStorageSecret(test.tempo, test.input) assert.Equal(t, test.expected, errs) }) } @@ -485,7 +487,7 @@ func TestValidateStorageCAConfigMap(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - errs := ValidateStorageCAConfigMap(test.input) + errs := v1alpha1.ValidateStorageCAConfigMap(test.input) assert.Equal(t, test.expected, errs) }) } @@ -498,15 +500,15 @@ func TestValidateReplicationFactor(t *testing.T) { tests := []struct { name string expected field.ErrorList - input TempoStack + input v1alpha1.TempoStack }{ { name: "no error replicas equal to floor(replication_factor/2) + 1", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - Ingester: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(2)), }, }, @@ -516,11 +518,11 @@ func TestValidateReplicationFactor(t *testing.T) { }, { name: "no error replicas greater than floor(replication_factor/2) + 1", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - Ingester: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(3)), }, }, @@ -530,11 +532,11 @@ func TestValidateReplicationFactor(t *testing.T) { }, { name: "error replicas less than floor(replication_factor/2) + 1", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - Ingester: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: ptr.To(int32(1)), }, }, @@ -561,20 +563,20 @@ func TestValidateQueryFrontend(t *testing.T) { tests := []struct { name string - input TempoStack - ctrlConfig v1alpha1.ProjectConfig + input v1alpha1.TempoStack + ctrlConfig configv1alpha1.ProjectConfig expected field.ErrorList }{ { name: "valid ingress configuration", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "ingress", }, }, @@ -586,14 +588,14 @@ func TestValidateQueryFrontend(t *testing.T) { }, { name: "valid route configuration", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "route", }, }, @@ -601,9 +603,9 @@ func TestValidateQueryFrontend(t *testing.T) { }, }, }, - ctrlConfig: v1alpha1.ProjectConfig{ - Gates: v1alpha1.FeatureGates{ - OpenShift: v1alpha1.OpenShiftFeatureGates{ + ctrlConfig: configv1alpha1.ProjectConfig{ + Gates: configv1alpha1.FeatureGates{ + OpenShift: configv1alpha1.OpenShiftFeatureGates{ OpenShiftRoute: true, }, }, @@ -612,14 +614,14 @@ func TestValidateQueryFrontend(t *testing.T) { }, { name: "ingress enabled but queryfrontend disabled", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: false, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "ingress", }, }, @@ -630,21 +632,21 @@ func TestValidateQueryFrontend(t *testing.T) { expected: field.ErrorList{ field.Invalid( ingressTypePath, - IngressTypeIngress, + v1alpha1.IngressTypeIngress, "Ingress cannot be enabled if jaegerQuery is disabled", ), }, }, { name: "route enabled but route feature gate disabled", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "route", }, }, @@ -652,9 +654,9 @@ func TestValidateQueryFrontend(t *testing.T) { }, }, }, - ctrlConfig: v1alpha1.ProjectConfig{ - Gates: v1alpha1.FeatureGates{ - OpenShift: v1alpha1.OpenShiftFeatureGates{ + ctrlConfig: configv1alpha1.ProjectConfig{ + Gates: configv1alpha1.FeatureGates{ + OpenShift: configv1alpha1.OpenShiftFeatureGates{ OpenShiftRoute: false, }, }, @@ -662,21 +664,21 @@ func TestValidateQueryFrontend(t *testing.T) { expected: field.ErrorList{ field.Invalid( ingressTypePath, - IngressTypeRoute, + v1alpha1.IngressTypeRoute, "Please enable the featureGates.openshift.openshiftRoute feature gate to use Routes", ), }, }, { name: "monitor tab enabled, missing prometheus endpoint", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - MonitorTab: JaegerQueryMonitor{ + MonitorTab: v1alpha1.JaegerQueryMonitor{ Enabled: true, }, }, @@ -684,8 +686,8 @@ func TestValidateQueryFrontend(t *testing.T) { }, }, }, - ctrlConfig: v1alpha1.ProjectConfig{ - Gates: v1alpha1.FeatureGates{}, + ctrlConfig: configv1alpha1.ProjectConfig{ + Gates: configv1alpha1.FeatureGates{}, }, expected: field.ErrorList{ field.Invalid( @@ -711,26 +713,26 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { tests := []struct { name string - input TempoStack + input v1alpha1.TempoStack expected field.ErrorList }{ { name: "valid configuration enabled both", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, }, }, - Gateway: TempoGatewaySpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, @@ -738,19 +740,19 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { }, { name: "valid config disable gateway and enable jaegerQuery", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "route", }, }, }, - Gateway: TempoGatewaySpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: false, }, }, @@ -760,19 +762,19 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { }, { name: "valid config disable both", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: false, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "route", }, }, }, - Gateway: TempoGatewaySpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: false, }, }, @@ -782,24 +784,24 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { }, { name: "invalid configuration, ingress and gateway enabled", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "ingress", }, }, }, - Gateway: TempoGatewaySpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, @@ -811,15 +813,15 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { }, { name: "invalid configuration, gateway enabled but no tenant configured", - input: TempoStack{ - Spec: TempoStackSpec{ - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, }, }, - Gateway: TempoGatewaySpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, @@ -833,24 +835,24 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { }, { name: "valid ingress configuration", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, }, }, - Gateway: TempoGatewaySpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "ingress", }, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, @@ -858,58 +860,58 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { }, { name: "invalid route, feature gateway disabled", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, }, }, - Gateway: TempoGatewaySpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "route", }, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, expected: field.ErrorList{ field.Invalid( field.NewPath("spec").Child("template").Child("gateway").Child("ingress").Child("type"), - IngressType("route"), + v1alpha1.IngressType("route"), "please enable the featureGates.openshift.openshiftRoute feature gate to use Routes", ), }, }, { name: "invalid configuration, enable two ingesss", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + Template: v1alpha1.TempoTemplateSpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "ingress", }, }, }, - Gateway: TempoGatewaySpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, - Ingress: IngressSpec{ + Ingress: v1alpha1.IngressSpec{ Type: "ingress", }, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, @@ -925,7 +927,7 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - validator := &validator{ctrlConfig: v1alpha1.ProjectConfig{}} + validator := &validator{ctrlConfig: configv1alpha1.ProjectConfig{}} errs := validator.validateGateway(test.input) assert.Equal(t, test.expected, errs) }) @@ -935,32 +937,32 @@ func TestValidateGatewayAndJaegerQuery(t *testing.T) { func TestValidateTenantConfigs(t *testing.T) { tt := []struct { name string - input TempoStack + input v1alpha1.TempoStack wantErr error }{ { name: "missing tenants", - input: TempoStack{ - Spec: TempoStackSpec{}, + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{}, }, }, { name: "another mode", - input: TempoStack{ - Spec: TempoStackSpec{ - Tenants: &TenantsSpec{}, + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Tenants: &v1alpha1.TenantsSpec{}, }, }, }, { name: "static missing authentication", - input: TempoStack{ - Spec: TempoStackSpec{ - Tenants: &TenantsSpec{ - Mode: ModeStatic, + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, @@ -970,14 +972,14 @@ func TestValidateTenantConfigs(t *testing.T) { }, { name: "static missing authorization", - input: TempoStack{ - Spec: TempoStackSpec{ - Tenants: &TenantsSpec{ - Mode: ModeStatic, - Authentication: []AuthenticationSpec{}, - }, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, + Authentication: []v1alpha1.AuthenticationSpec{}, + }, + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, @@ -987,15 +989,15 @@ func TestValidateTenantConfigs(t *testing.T) { }, { name: "static missing roles", - input: TempoStack{ - Spec: TempoStackSpec{ - Tenants: &TenantsSpec{ - Mode: ModeStatic, - Authorization: &AuthorizationSpec{}, - Authentication: []AuthenticationSpec{}, - }, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, + Authorization: &v1alpha1.AuthorizationSpec{}, + Authentication: []v1alpha1.AuthenticationSpec{}, + }, + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, @@ -1005,17 +1007,17 @@ func TestValidateTenantConfigs(t *testing.T) { }, { name: "static missing role bindings", - input: TempoStack{ - Spec: TempoStackSpec{ - Tenants: &TenantsSpec{ - Mode: ModeStatic, - Authorization: &AuthorizationSpec{ - Roles: []RoleSpec{}, + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, + Authorization: &v1alpha1.AuthorizationSpec{ + Roles: []v1alpha1.RoleSpec{}, }, - Authentication: []AuthenticationSpec{}, + Authentication: []v1alpha1.AuthenticationSpec{}, }, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, @@ -1025,16 +1027,16 @@ func TestValidateTenantConfigs(t *testing.T) { }, { name: "openshift: RBAC should not be defined", - input: TempoStack{ - Spec: TempoStackSpec{ - Tenants: &TenantsSpec{ - Mode: ModeOpenShift, - Authorization: &AuthorizationSpec{ - Roles: []RoleSpec{}, + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeOpenShift, + Authorization: &v1alpha1.AuthorizationSpec{ + Roles: []v1alpha1.RoleSpec{}, }, }, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, @@ -1044,18 +1046,18 @@ func TestValidateTenantConfigs(t *testing.T) { }, { name: "openshift: OIDC should not be defined", - input: TempoStack{ - Spec: TempoStackSpec{ - Tenants: &TenantsSpec{ - Mode: ModeOpenShift, - Authentication: []AuthenticationSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeOpenShift, + Authentication: []v1alpha1.AuthenticationSpec{ { - OIDC: &OIDCSpec{}, + OIDC: &v1alpha1.OIDCSpec{}, }, }, }, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, }, @@ -1080,29 +1082,29 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { tt := []struct { name string - input TempoStack - ctrlConfig v1alpha1.ProjectConfig + input v1alpha1.TempoStack + ctrlConfig configv1alpha1.ProjectConfig expected field.ErrorList }{ { name: "not set", - input: TempoStack{ - Spec: TempoStackSpec{}, + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{}, }, }, { name: "createServiceMonitors enabled and prometheusOperator feature gate set", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Metrics: MetricsConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Metrics: v1alpha1.MetricsConfigSpec{ CreateServiceMonitors: true, }, }, }, }, - ctrlConfig: v1alpha1.ProjectConfig{ - Gates: v1alpha1.FeatureGates{ + ctrlConfig: configv1alpha1.ProjectConfig{ + Gates: configv1alpha1.FeatureGates{ PrometheusOperator: true, }, }, @@ -1110,10 +1112,10 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { }, { name: "createServiceMonitors enabled but prometheusOperator feature gate not set", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Metrics: MetricsConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Metrics: v1alpha1.MetricsConfigSpec{ CreateServiceMonitors: true, }, }, @@ -1129,10 +1131,10 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { }, { name: "createPrometheusRules enabled but prometheusOperator feature gate not set", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Metrics: MetricsConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Metrics: v1alpha1.MetricsConfigSpec{ CreatePrometheusRules: true, }, }, @@ -1148,18 +1150,18 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { }, { name: "createPrometheusRules and createServiceMonitors enabled and prometheusOperator feature gate set", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Metrics: MetricsConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Metrics: v1alpha1.MetricsConfigSpec{ CreateServiceMonitors: true, CreatePrometheusRules: true, }, }, }, }, - ctrlConfig: v1alpha1.ProjectConfig{ - Gates: v1alpha1.FeatureGates{ + ctrlConfig: configv1alpha1.ProjectConfig{ + Gates: configv1alpha1.FeatureGates{ PrometheusOperator: true, }, }, @@ -1167,17 +1169,17 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { }, { name: "createPrometheusRules enabled but createServiceMonitors not enabled", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Metrics: MetricsConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Metrics: v1alpha1.MetricsConfigSpec{ CreatePrometheusRules: true, }, }, }, }, - ctrlConfig: v1alpha1.ProjectConfig{ - Gates: v1alpha1.FeatureGates{ + ctrlConfig: configv1alpha1.ProjectConfig{ + Gates: configv1alpha1.FeatureGates{ PrometheusOperator: true, }, }, @@ -1191,10 +1193,10 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { }, { name: "sampling fraction not a float", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Tracing: TracingConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Tracing: v1alpha1.TracingConfigSpec{ SamplingFraction: "a", }, }, @@ -1210,10 +1212,10 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { }, { name: "invalid jaeger agent address", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Tracing: TracingConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Tracing: v1alpha1.TracingConfigSpec{ SamplingFraction: "0.5", JaegerAgentEndpoint: "--invalid--", }, @@ -1230,10 +1232,10 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { }, { name: "valid configuration", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Tracing: TracingConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Tracing: v1alpha1.TracingConfigSpec{ SamplingFraction: "0.5", JaegerAgentEndpoint: "agent:1234", }, @@ -1254,30 +1256,30 @@ func TestValidatorObservabilityTracingConfig(t *testing.T) { func TestValidatorObservabilityGrafana(t *testing.T) { tt := []struct { name string - input TempoStack - ctrlConfig v1alpha1.ProjectConfig + input v1alpha1.TempoStack + ctrlConfig configv1alpha1.ProjectConfig expected field.ErrorList }{ { name: "datasource not enabled", - input: TempoStack{ - Spec: TempoStackSpec{}, + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{}, }, expected: nil, }, { name: "datasource enabled, feature gate not set", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Grafana: GrafanaConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Grafana: v1alpha1.GrafanaConfigSpec{ CreateDatasource: true, }, }, }, }, - ctrlConfig: v1alpha1.ProjectConfig{ - Gates: v1alpha1.FeatureGates{ + ctrlConfig: configv1alpha1.ProjectConfig{ + Gates: configv1alpha1.FeatureGates{ GrafanaOperator: false, }, }, @@ -1291,17 +1293,17 @@ func TestValidatorObservabilityGrafana(t *testing.T) { }, { name: "datasource enabled, feature gate set", - input: TempoStack{ - Spec: TempoStackSpec{ - Observability: ObservabilitySpec{ - Grafana: GrafanaConfigSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + Observability: v1alpha1.ObservabilitySpec{ + Grafana: v1alpha1.GrafanaConfigSpec{ CreateDatasource: true, }, }, }, }, - ctrlConfig: v1alpha1.ProjectConfig{ - Gates: v1alpha1.FeatureGates{ + ctrlConfig: configv1alpha1.ProjectConfig{ + Gates: configv1alpha1.FeatureGates{ GrafanaOperator: true, }, }, @@ -1335,21 +1337,21 @@ func TestValidatorValidate(t *testing.T) { }, { name: "pass all validators", - input: &TempoStack{ + input: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test-obj", Namespace: "abc", }, TypeMeta: gvType, - Spec: TempoStackSpec{ + Spec: v1alpha1.TempoStackSpec{ ServiceAccount: naming.DefaultServiceAccountName("test-obj"), - Storage: ObjectStorageSpec{ - Secret: ObjectStorageSecretSpec{ + Storage: v1alpha1.ObjectStorageSpec{ + Secret: v1alpha1.ObjectStorageSecretSpec{ Name: "not-found", }, }, - Template: TempoTemplateSpec{ - Ingester: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: func(i int32) *int32 { return &i }(1), }, }, @@ -1360,7 +1362,7 @@ func TestValidatorValidate(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - v := &validator{ctrlConfig: v1alpha1.ProjectConfig{}, client: &k8sFake{}} + v := &validator{ctrlConfig: configv1alpha1.ProjectConfig{}, client: &k8sFake{}} _, err := v.validate(context.Background(), tc.input) assert.Equal(t, tc.expected, err) }) @@ -1375,12 +1377,12 @@ func TestValidateName(t *testing.T) { tt := []struct { name string - input TempoStack + input v1alpha1.TempoStack expected field.ErrorList }{ { name: "all good", - input: TempoStack{ + input: v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test-obj", Namespace: "abc", @@ -1389,7 +1391,7 @@ func TestValidateName(t *testing.T) { }, { name: "too long", - input: TempoStack{ + input: v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: longName, Namespace: "abc", @@ -1406,7 +1408,7 @@ func TestValidateName(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - v := &validator{ctrlConfig: v1alpha1.ProjectConfig{}, client: &k8sFake{}} + v := &validator{ctrlConfig: configv1alpha1.ProjectConfig{}, client: &k8sFake{}} assert.Equal(t, tc.expected, v.validateStackName(tc.input)) }) } @@ -1418,20 +1420,20 @@ func TestValidateDeprecatedFields(t *testing.T) { tt := []struct { name string - input TempoStack + input v1alpha1.TempoStack expected field.ErrorList }{ { name: "no deprecated fields", - input: TempoStack{}, + input: v1alpha1.TempoStack{}, }, { name: "deprecated global maxSearchBytesPerTrace set to 0", - input: TempoStack{ - Spec: TempoStackSpec{ - LimitSpec: LimitSpec{ - Global: RateLimitSpec{ - Query: QueryLimit{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + LimitSpec: v1alpha1.LimitSpec{ + Global: v1alpha1.RateLimitSpec{ + Query: v1alpha1.QueryLimit{ MaxSearchBytesPerTrace: &zero, }, }, @@ -1448,11 +1450,11 @@ func TestValidateDeprecatedFields(t *testing.T) { }, { name: "deprecated global maxSearchBytesPerTrace set to 1", - input: TempoStack{ - Spec: TempoStackSpec{ - LimitSpec: LimitSpec{ - Global: RateLimitSpec{ - Query: QueryLimit{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + LimitSpec: v1alpha1.LimitSpec{ + Global: v1alpha1.RateLimitSpec{ + Query: v1alpha1.QueryLimit{ MaxSearchBytesPerTrace: &one, }, }, @@ -1469,12 +1471,12 @@ func TestValidateDeprecatedFields(t *testing.T) { }, { name: "deprecated per-tenant maxSearchBytesPerTrace set to 0", - input: TempoStack{ - Spec: TempoStackSpec{ - LimitSpec: LimitSpec{ - PerTenant: map[string]RateLimitSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ + LimitSpec: v1alpha1.LimitSpec{ + PerTenant: map[string]v1alpha1.RateLimitSpec{ "tenant1": { - Query: QueryLimit{ + Query: v1alpha1.QueryLimit{ MaxSearchBytesPerTrace: &zero, }, }, @@ -1494,7 +1496,7 @@ func TestValidateDeprecatedFields(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - v := &validator{ctrlConfig: v1alpha1.ProjectConfig{}} + v := &validator{ctrlConfig: configv1alpha1.ProjectConfig{}} assert.Equal(t, tc.expected, v.validateDeprecatedFields(tc.input)) }) } @@ -1503,26 +1505,26 @@ func TestValidateDeprecatedFields(t *testing.T) { func TestValidateReceiverTLSAndGateway(t *testing.T) { tests := []struct { name string - input TempoStack + input v1alpha1.TempoStack expected field.ErrorList }{ { name: "valid configuration disable both", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: false, }, - Distributor: TempoDistributorSpec{ - TLS: ReceiversTLSSpec{ + Distributor: v1alpha1.TempoDistributorSpec{ + TLS: v1alpha1.ReceiversTLSSpec{ Enabled: false, }, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, @@ -1530,26 +1532,26 @@ func TestValidateReceiverTLSAndGateway(t *testing.T) { }, { name: "valid configuration enable only gateway", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, }, }, - Distributor: TempoDistributorSpec{ - TLS: ReceiversTLSSpec{ + Distributor: v1alpha1.TempoDistributorSpec{ + TLS: v1alpha1.ReceiversTLSSpec{ Enabled: false, }, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, @@ -1557,22 +1559,22 @@ func TestValidateReceiverTLSAndGateway(t *testing.T) { }, { name: "valid configuration enable only receiver TLS", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: false, }, - Distributor: TempoDistributorSpec{ - TLS: ReceiversTLSSpec{ + Distributor: v1alpha1.TempoDistributorSpec{ + TLS: v1alpha1.ReceiversTLSSpec{ Enabled: true, Cert: "my-cert", }, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, @@ -1580,26 +1582,26 @@ func TestValidateReceiverTLSAndGateway(t *testing.T) { }, { name: "invalid configuration enable both", - input: TempoStack{ - Spec: TempoStackSpec{ + input: v1alpha1.TempoStack{ + Spec: v1alpha1.TempoStackSpec{ ReplicationFactor: 3, - Template: TempoTemplateSpec{ - Gateway: TempoGatewaySpec{ + Template: v1alpha1.TempoTemplateSpec{ + Gateway: v1alpha1.TempoGatewaySpec{ Enabled: true, }, - QueryFrontend: TempoQueryFrontendSpec{ - JaegerQuery: JaegerQuerySpec{ + QueryFrontend: v1alpha1.TempoQueryFrontendSpec{ + JaegerQuery: v1alpha1.JaegerQuerySpec{ Enabled: true, }, }, - Distributor: TempoDistributorSpec{ - TLS: ReceiversTLSSpec{ + Distributor: v1alpha1.TempoDistributorSpec{ + TLS: v1alpha1.ReceiversTLSSpec{ Enabled: true, }, }, }, - Tenants: &TenantsSpec{ - Mode: ModeStatic, + Tenants: &v1alpha1.TenantsSpec{ + Mode: v1alpha1.ModeStatic, }, }, }, @@ -1613,7 +1615,7 @@ func TestValidateReceiverTLSAndGateway(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - validator := &validator{ctrlConfig: v1alpha1.ProjectConfig{}} + validator := &validator{ctrlConfig: configv1alpha1.ProjectConfig{}} errs := validator.validateGateway(test.input) assert.Equal(t, test.expected, errs) }) @@ -1634,21 +1636,21 @@ func TestWarning(t *testing.T) { }{ { name: "no secret exists", - input: &TempoStack{ + input: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test-obj", Namespace: "abc", }, TypeMeta: gvType, - Spec: TempoStackSpec{ + Spec: v1alpha1.TempoStackSpec{ ServiceAccount: naming.DefaultServiceAccountName("test-obj"), - Storage: ObjectStorageSpec{ - Secret: ObjectStorageSecretSpec{ + Storage: v1alpha1.ObjectStorageSpec{ + Secret: v1alpha1.ObjectStorageSecretSpec{ Name: "not-found", }, }, - Template: TempoTemplateSpec{ - Ingester: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: func(i int32) *int32 { return &i }(1), }, }, @@ -1659,25 +1661,25 @@ func TestWarning(t *testing.T) { }, { name: "warning for use extra config", - input: &TempoStack{ + input: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test-obj", Namespace: "abc", }, TypeMeta: gvType, - Spec: TempoStackSpec{ + Spec: v1alpha1.TempoStackSpec{ ServiceAccount: naming.DefaultServiceAccountName("test-obj"), - Storage: ObjectStorageSpec{ - Secret: ObjectStorageSecretSpec{ + Storage: v1alpha1.ObjectStorageSpec{ + Secret: v1alpha1.ObjectStorageSecretSpec{ Name: "not-found", }, }, - Template: TempoTemplateSpec{ - Ingester: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: func(i int32) *int32 { return &i }(1), }, }, - ExtraConfig: &ExtraConfigSpec{ + ExtraConfig: &v1alpha1.ExtraConfigSpec{ Tempo: v1.JSON{Raw: []byte("{}")}, }, }, @@ -1691,21 +1693,21 @@ func TestWarning(t *testing.T) { }, { name: "no extra config used", - input: &TempoStack{ + input: &v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test-obj", Namespace: "abc", }, TypeMeta: gvType, - Spec: TempoStackSpec{ + Spec: v1alpha1.TempoStackSpec{ ServiceAccount: naming.DefaultServiceAccountName("test-obj"), - Storage: ObjectStorageSpec{ - Secret: ObjectStorageSecretSpec{ + Storage: v1alpha1.ObjectStorageSpec{ + Secret: v1alpha1.ObjectStorageSecretSpec{ Name: "not-found", }, }, - Template: TempoTemplateSpec{ - Ingester: TempoComponentSpec{ + Template: v1alpha1.TempoTemplateSpec{ + Ingester: v1alpha1.TempoComponentSpec{ Replicas: func(i int32) *int32 { return &i }(1), }, }, @@ -1720,7 +1722,7 @@ func TestWarning(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - v := &validator{ctrlConfig: v1alpha1.ProjectConfig{}, client: test.client} + v := &validator{ctrlConfig: configv1alpha1.ProjectConfig{}, client: test.client} wrgs, _ := v.validate(context.Background(), test.input) assert.Equal(t, test.expected, wrgs) }) diff --git a/apis/tempo/v1alpha1/webhook_suite_test.go b/internal/webhooks/webhook_suite_test.go similarity index 92% rename from apis/tempo/v1alpha1/webhook_suite_test.go rename to internal/webhooks/webhook_suite_test.go index d385b2619..149d9aae4 100644 --- a/apis/tempo/v1alpha1/webhook_suite_test.go +++ b/internal/webhooks/webhook_suite_test.go @@ -1,4 +1,4 @@ -package v1alpha1 +package webhooks import ( "context" @@ -24,7 +24,8 @@ import ( //+kubebuilder:scaffold:imports - "github.com/grafana/tempo-operator/apis/config/v1alpha1" + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to @@ -63,7 +64,7 @@ var _ = BeforeSuite(func() { Expect(cfg).NotTo(BeNil()) scheme := runtime.NewScheme() - err = AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) Expect(err).NotTo(HaveOccurred()) err = admissionv1beta1.AddToScheme(scheme) @@ -91,7 +92,7 @@ var _ = BeforeSuite(func() { }) Expect(err).NotTo(HaveOccurred()) - err = (&TempoStack{}).SetupWebhookWithManager(mgr, v1alpha1.ProjectConfig{}) + err = (&TempoStackWebhook{}).SetupWebhookWithManager(mgr, configv1alpha1.ProjectConfig{}) Expect(err).NotTo(HaveOccurred()) //+kubebuilder:scaffold:webhook