From edb2a72a0a91de074e0ddadcf77dc168dcdec3fd Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Fri, 15 Dec 2023 19:00:56 +0100 Subject: [PATCH 01/36] Support monolithic deployment mode Signed-off-by: Andreas Gerstmayr --- .gitignore | 1 + PROJECT | 13 + apis/tempo/v1alpha1/tempomonolithic_types.go | 265 ++ .../tempo/v1alpha1/tempomonolithic_webhook.go | 77 + apis/tempo/v1alpha1/zz_generated.deepcopy.go | 424 +++ .../tempo-operator.clusterserviceversion.yaml | 87 +- .../tempo.grafana.com_tempomonolithics.yaml | 228 ++ .../tempo-operator.clusterserviceversion.yaml | 87 +- .../tempo.grafana.com_tempomonolithics.yaml | 228 ++ cmd/start/main.go | 22 +- .../tempo.grafana.com_tempomonolithics.yaml | 219 ++ config/crd/kustomization.yaml | 1 + .../cainjection_in_tempomonolithics.yaml | 7 + .../patches/webhook_in_tempomonolithics.yaml | 16 + .../tempo-operator.clusterserviceversion.yaml | 5 + .../tempo-operator.clusterserviceversion.yaml | 5 + config/rbac/role.yaml | 26 + config/samples/community/kustomization.yaml | 1 + .../tempo_v1alpha1_tempomonolithic.yaml | 8 + config/samples/openshift/kustomization.yaml | 1 + .../tempo_v1alpha1_tempomonolithic.yaml | 8 + config/webhook/manifests.yaml | 40 + controllers/tempo/common.go | 68 + .../tempo/tempomonolithic_controller.go | 78 + controllers/tempo/tempostack_controller.go | 2 +- .../tempo/tempostack_create_or_update.go | 10 - docs/operator/api.md | 2551 +++++++++++++---- docs/spec/tempomonolithic.yaml | 39 + internal/manifests/manifestutils/constants.go | 15 + internal/manifests/monolithic/build.go | 28 + internal/manifests/monolithic/config.go | 162 ++ internal/manifests/monolithic/labels.go | 19 + internal/manifests/monolithic/options.go | 13 + internal/manifests/monolithic/service.go | 126 + internal/manifests/monolithic/statefulset.go | 252 ++ internal/manifests/mutate.go | 42 +- .../manifests/queryfrontend/query_frontend.go | 45 +- .../queryfrontend/query_frontend_test.go | 34 +- 38 files changed, 4668 insertions(+), 585 deletions(-) create mode 100644 apis/tempo/v1alpha1/tempomonolithic_types.go create mode 100644 apis/tempo/v1alpha1/tempomonolithic_webhook.go create mode 100644 bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml create mode 100644 bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml create mode 100644 config/crd/bases/tempo.grafana.com_tempomonolithics.yaml create mode 100644 config/crd/patches/cainjection_in_tempomonolithics.yaml create mode 100644 config/crd/patches/webhook_in_tempomonolithics.yaml create mode 100644 config/samples/community/tempo_v1alpha1_tempomonolithic.yaml create mode 100644 config/samples/openshift/tempo_v1alpha1_tempomonolithic.yaml create mode 100644 controllers/tempo/common.go create mode 100644 controllers/tempo/tempomonolithic_controller.go create mode 100644 docs/spec/tempomonolithic.yaml create mode 100644 internal/manifests/monolithic/build.go create mode 100644 internal/manifests/monolithic/config.go create mode 100644 internal/manifests/monolithic/labels.go create mode 100644 internal/manifests/monolithic/options.go create mode 100644 internal/manifests/monolithic/service.go create mode 100644 internal/manifests/monolithic/statefulset.go diff --git a/.gitignore b/.gitignore index f591b5792..4eff0bac3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ docs/prologue/contributing.md #IDE .idea +.vscode diff --git a/PROJECT b/PROJECT index 37a0e69b5..cf3097871 100644 --- a/PROJECT +++ b/PROJECT @@ -21,4 +21,17 @@ resources: defaulting: true validation: true webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: grafana.com + group: tempo + kind: TempoMonolithic + path: github.com/grafana/tempo-operator/api/v1alpha1 + version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 version: "3" diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go new file mode 100644 index 000000000..ee535f8df --- /dev/null +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -0,0 +1,265 @@ +package v1alpha1 + +import ( + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TempoMonolithicSpec defines the desired state of TempoMonolithic. +type TempoMonolithicSpec struct { + // Storage defines the backend storage configuration + // + // +kubebuilder:validation:Optional + Storage MonolithicStorageSpec `json:"storage"` + + // Ingestion defines the trace ingestion configuration + // + // +kubebuilder:validation:Optional + Ingestion *MonolithicIngestionSpec `json:"ingestion,omitempty"` + + // JaegerUI defines the Jaeger UI configuration + // + // +kubebuilder:validation:Optional + JaegerUI *MonolithicJaegerUISpec `json:"jaegerui,omitempty"` + + // ManagementState defines whether this instance is managed by the operator or self-managed + // + // +kubebuilder:validation:Required + Management ManagementStateType `json:"management,omitempty"` + + // Observability defines observability configuration for the Tempo deployment + // + // +kubebuilder:validation:Optional + Observability *MonolithicObservabilitySpec `json:"observability,omitempty"` + + // ExtraConfig defines any extra (overlay) configuration for components + // + // +kubebuilder:validation:Optional + ExtraConfig *MonolithicExtraConfigSpec `json:"extraConfig,omitempty"` +} + +// MonolithicStorageSpec defines the storage for the Tempo deployment. +type MonolithicStorageSpec struct { + // Traces defines the backend storage configuration for traces + // + // +kubebuilder:validation:Required + Traces MonolithicTracesStorageSpec `json:"traces"` +} + +// MonolithicTracesStorageSpec defines the traces storage for the Tempo deployment. +type MonolithicTracesStorageSpec struct { + // Backend defines the backend for storing traces + // + // +kubebuilder:validation:Required + Backend MonolithicTracesStorageBackend `json:"backend"` + + // WAL defines the write-ahead logging (WAL) configuration + // + // +kubebuilder:validation:Required + WAL *MonolithicTracesStorageWALSpec `json:"wal"` + + // PV defines the Persistent Volume configuration + // + // +kubebuilder:validation:Required + PV *MonolithicTracesStoragePersistentVolumeSpec `json:"pv"` +} + +// MonolithicTracesStorageBackend defines the backend storage for traces. +// +// +kubebuilder:validation:Enum=memory;pv +type MonolithicTracesStorageBackend string + +const ( + // MonolithicTracesStorageBackendMemory specifies a in-memory storage backend. + MonolithicTracesStorageBackendMemory MonolithicTracesStorageBackend = "memory" + // MonolithicTracesStorageBackendPersistentVolume specifies a Persistent Volume storage backend. + MonolithicTracesStorageBackendPersistentVolume MonolithicTracesStorageBackend = "pv" +) + +// MonolithicTracesStorageWALSpec defines the write-ahead logging (WAL) configuration. +type MonolithicTracesStorageWALSpec struct { + // Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. + // + // +kubebuilder:validation:Required + Size resource.Quantity `json:"size"` +} + +// MonolithicTracesStoragePersistentVolumeSpec defines the Persistent Volume configuration. +type MonolithicTracesStoragePersistentVolumeSpec struct { + // Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. + // + // +kubebuilder:validation:Required + Size resource.Quantity `json:"size"` +} + +// MonolithicIngestionSpec defines the ingestion settings. +type MonolithicIngestionSpec struct { + // OTLP defines the ingestion configuration for OTLP + // + // +kubebuilder:validation:Optional + OTLP *MonolithicIngestionOTLPSpec `json:"otlp,omitempty"` + + // TLS defines the TLS configuration for ingestion + // + // +kubebuilder:validation:Optional + TLS *MonolithicIngestionTLSSpec `json:"tls,omitempty"` +} + +// MonolithicIngestionOTLPSpec defines the settings for OTLP ingestion. +type MonolithicIngestionOTLPSpec struct { + // GRPC defines the OTLP/gRPC configuration + // + // +kubebuilder:validation:Optional + GRPC *MonolithicIngestionOTLPProtocolsGRPCSpec `json:"grpc,omitempty"` + + // HTTP defines the OTLP/HTTP configuration + // + // +kubebuilder:validation:Optional + HTTP *MonolithicIngestionOTLPProtocolsHTTPSpec `json:"http,omitempty"` +} + +// MonolithicIngestionOTLPProtocolsGRPCSpec defines the settings for OTLP ingestion over GRPC. +type MonolithicIngestionOTLPProtocolsGRPCSpec struct { + // Enabled defines if OTLP over gRPC is enabled + // + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` +} + +// MonolithicIngestionOTLPProtocolsHTTPSpec defines the settings for OTLP ingestion over HTTP. +type MonolithicIngestionOTLPProtocolsHTTPSpec struct { + // Enabled defines if OTLP over HTTP is enabled + // + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` +} + +// MonolithicIngestionTLSSpec defines the TLS settings for ingestion. +type MonolithicIngestionTLSSpec struct { + // Enabled defines if TLS is enabled for ingestion + // + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` + + // CA defines the name of a secret containing the CA certificate + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:MinLength=1 + CA string `json:"ca"` + + // Cert defines the name of a secret containing the TLS certificate and private key + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:MinLength=1 + Cert string `json:"cert"` +} + +// MonolithicJaegerUISpec defines the settings for the Jaeger UI. +type MonolithicJaegerUISpec struct { + // Enabled defines if the Jaeger UI should be enabled + // + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` + + // Ingress defines the ingress configuration for Jaeger UI + // + // +kubebuilder:validation:Optional + Ingress *MonolithicJaegerUIIngressSpec `json:"ingress,omitempty"` + + // Route defines the route configuration for Jaeger UI + // + // +kubebuilder:validation:Optional + Route *MonolithicJaegerUIRouteSpec `json:"route,omitempty"` +} + +// MonolithicJaegerUIIngressSpec defines the settings for the Jaeger UI ingress. +type MonolithicJaegerUIIngressSpec struct { + // Enabled defines if an Ingress object should be created for Jaeger UI + // + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` +} + +// MonolithicJaegerUIRouteSpec defines the settings for the Jaeger UI route. +type MonolithicJaegerUIRouteSpec struct { + // Enabled defines if a Route object should be created for Jaeger UI + // + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` +} + +// MonolithicObservabilitySpec defines the observability settings of the Tempo deployment. +type MonolithicObservabilitySpec struct { + // Metrics defines the metrics configuration of the Tempo deployment + // + // +kubebuilder:validation:Optional + Metrics *MonolithicObservabilityMetricsSpec `json:"metrics,omitempty"` +} + +// MonolithicObservabilityMetricsSpec defines the metrics settings of the Tempo deployment. +type MonolithicObservabilityMetricsSpec struct { + // ServiceMonitors defines the ServiceMonitor configuration + // + // +kubebuilder:validation:Optional + ServiceMonitors *MonolithicObservabilityMetricsServiceMonitorsSpec `json:"serviceMonitors,omitempty"` + + // ServiceMonitors defines the PrometheusRule configuration + // + // +kubebuilder:validation:Optional + PrometheusRules *MonolithicObservabilityMetricsPrometheusRulesSpec `json:"prometheusRules,omitempty"` +} + +// MonolithicObservabilityMetricsServiceMonitorsSpec defines the ServiceMonitor settings. +type MonolithicObservabilityMetricsServiceMonitorsSpec struct { + // Enabled defines if the operator should create ServiceMonitors for this Tempo deployment + // + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` +} + +// MonolithicObservabilityMetricsPrometheusRulesSpec defines the PrometheusRules settings. +type MonolithicObservabilityMetricsPrometheusRulesSpec struct { + // Enabled defines if the operator should create PrometheusRules for this Tempo deployment + // + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` +} + +// MonolithicExtraConfigSpec defines extra configuration for this deployment. +type MonolithicExtraConfigSpec struct { + // Tempo defines any extra Tempo configuration, which will be merged with the operator's generated Tempo configuration + // +kubebuilder:validation:Optional + Tempo apiextensionsv1.JSON `json:"tempo,omitempty"` +} + +// TempoMonolithicStatus defines the observed state of TempoMonolithic. +type TempoMonolithicStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// TempoMonolithic is the Schema for the tempomonolithics API. +type TempoMonolithic struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec TempoMonolithicSpec `json:"spec,omitempty"` + Status TempoMonolithicStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// TempoMonolithicList contains a list of TempoMonolithic. +type TempoMonolithicList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []TempoMonolithic `json:"items"` +} + +func init() { + SchemeBuilder.Register(&TempoMonolithic{}, &TempoMonolithicList{}) +} diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook.go b/apis/tempo/v1alpha1/tempomonolithic_webhook.go new file mode 100644 index 000000000..ff7d71b5d --- /dev/null +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook.go @@ -0,0 +1,77 @@ +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" + 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() +} + +//+kubebuilder:webhook:path=/mutate-tempo-grafana-com-v1alpha1-tempomonolithic,mutating=true,failurePolicy=fail,sideEffects=None,groups=tempo.grafana.com,resources=tempomonolithics,verbs=create;update,versions=v1alpha1,name=mtempomonolithic.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &TempoMonolithic{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type. +func (r *TempoMonolithic) Default() { + log := ctrl.Log.WithName("tempomonolithic-webhook") + log.V(1).Info("running defaulter webhook", "name", r.Name) + + 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 == MonolithicTracesStorageBackendPersistentVolume && r.Spec.Storage.Traces.PV == nil { + r.Spec.Storage.Traces.PV = &MonolithicTracesStoragePersistentVolumeSpec{ + Size: tenGBQuantity, + } + } + + if r.Spec.Ingestion == nil { + r.Spec.Ingestion = &MonolithicIngestionSpec{ + OTLP: &MonolithicIngestionOTLPSpec{ + GRPC: &MonolithicIngestionOTLPProtocolsGRPCSpec{ + 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 (r *TempoMonolithic) validate() (admission.Warnings, error) { + log := ctrl.Log.WithName("tempomonolithic-webhook") + log.V(1).Info("running validating webhook", "name", r.Name) + return nil, nil +} diff --git a/apis/tempo/v1alpha1/zz_generated.deepcopy.go b/apis/tempo/v1alpha1/zz_generated.deepcopy.go index c6870d8e1..7829eda3a 100644 --- a/apis/tempo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/tempo/v1alpha1/zz_generated.deepcopy.go @@ -366,6 +366,320 @@ func (in *MetricsConfigSpec) DeepCopy() *MetricsConfigSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicExtraConfigSpec) DeepCopyInto(out *MonolithicExtraConfigSpec) { + *out = *in + in.Tempo.DeepCopyInto(&out.Tempo) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicExtraConfigSpec. +func (in *MonolithicExtraConfigSpec) DeepCopy() *MonolithicExtraConfigSpec { + if in == nil { + return nil + } + out := new(MonolithicExtraConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicIngestionOTLPProtocolsGRPCSpec) DeepCopyInto(out *MonolithicIngestionOTLPProtocolsGRPCSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicIngestionOTLPProtocolsGRPCSpec. +func (in *MonolithicIngestionOTLPProtocolsGRPCSpec) DeepCopy() *MonolithicIngestionOTLPProtocolsGRPCSpec { + if in == nil { + return nil + } + out := new(MonolithicIngestionOTLPProtocolsGRPCSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicIngestionOTLPProtocolsHTTPSpec) DeepCopyInto(out *MonolithicIngestionOTLPProtocolsHTTPSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicIngestionOTLPProtocolsHTTPSpec. +func (in *MonolithicIngestionOTLPProtocolsHTTPSpec) DeepCopy() *MonolithicIngestionOTLPProtocolsHTTPSpec { + if in == nil { + return nil + } + out := new(MonolithicIngestionOTLPProtocolsHTTPSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicIngestionOTLPSpec) DeepCopyInto(out *MonolithicIngestionOTLPSpec) { + *out = *in + if in.GRPC != nil { + in, out := &in.GRPC, &out.GRPC + *out = new(MonolithicIngestionOTLPProtocolsGRPCSpec) + **out = **in + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(MonolithicIngestionOTLPProtocolsHTTPSpec) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicIngestionOTLPSpec. +func (in *MonolithicIngestionOTLPSpec) DeepCopy() *MonolithicIngestionOTLPSpec { + if in == nil { + return nil + } + out := new(MonolithicIngestionOTLPSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicIngestionSpec) DeepCopyInto(out *MonolithicIngestionSpec) { + *out = *in + if in.OTLP != nil { + in, out := &in.OTLP, &out.OTLP + *out = new(MonolithicIngestionOTLPSpec) + (*in).DeepCopyInto(*out) + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(MonolithicIngestionTLSSpec) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicIngestionSpec. +func (in *MonolithicIngestionSpec) DeepCopy() *MonolithicIngestionSpec { + if in == nil { + return nil + } + out := new(MonolithicIngestionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicIngestionTLSSpec) DeepCopyInto(out *MonolithicIngestionTLSSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicIngestionTLSSpec. +func (in *MonolithicIngestionTLSSpec) DeepCopy() *MonolithicIngestionTLSSpec { + if in == nil { + return nil + } + out := new(MonolithicIngestionTLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicJaegerUIIngressSpec) DeepCopyInto(out *MonolithicJaegerUIIngressSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicJaegerUIIngressSpec. +func (in *MonolithicJaegerUIIngressSpec) DeepCopy() *MonolithicJaegerUIIngressSpec { + if in == nil { + return nil + } + out := new(MonolithicJaegerUIIngressSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicJaegerUIRouteSpec) DeepCopyInto(out *MonolithicJaegerUIRouteSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicJaegerUIRouteSpec. +func (in *MonolithicJaegerUIRouteSpec) DeepCopy() *MonolithicJaegerUIRouteSpec { + if in == nil { + return nil + } + out := new(MonolithicJaegerUIRouteSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicJaegerUISpec) DeepCopyInto(out *MonolithicJaegerUISpec) { + *out = *in + if in.Ingress != nil { + in, out := &in.Ingress, &out.Ingress + *out = new(MonolithicJaegerUIIngressSpec) + **out = **in + } + if in.Route != nil { + in, out := &in.Route, &out.Route + *out = new(MonolithicJaegerUIRouteSpec) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicJaegerUISpec. +func (in *MonolithicJaegerUISpec) DeepCopy() *MonolithicJaegerUISpec { + if in == nil { + return nil + } + out := new(MonolithicJaegerUISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilityMetricsPrometheusRulesSpec) DeepCopyInto(out *MonolithicObservabilityMetricsPrometheusRulesSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilityMetricsPrometheusRulesSpec. +func (in *MonolithicObservabilityMetricsPrometheusRulesSpec) DeepCopy() *MonolithicObservabilityMetricsPrometheusRulesSpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilityMetricsPrometheusRulesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilityMetricsServiceMonitorsSpec) DeepCopyInto(out *MonolithicObservabilityMetricsServiceMonitorsSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilityMetricsServiceMonitorsSpec. +func (in *MonolithicObservabilityMetricsServiceMonitorsSpec) DeepCopy() *MonolithicObservabilityMetricsServiceMonitorsSpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilityMetricsServiceMonitorsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilityMetricsSpec) DeepCopyInto(out *MonolithicObservabilityMetricsSpec) { + *out = *in + if in.ServiceMonitors != nil { + in, out := &in.ServiceMonitors, &out.ServiceMonitors + *out = new(MonolithicObservabilityMetricsServiceMonitorsSpec) + **out = **in + } + if in.PrometheusRules != nil { + in, out := &in.PrometheusRules, &out.PrometheusRules + *out = new(MonolithicObservabilityMetricsPrometheusRulesSpec) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilityMetricsSpec. +func (in *MonolithicObservabilityMetricsSpec) DeepCopy() *MonolithicObservabilityMetricsSpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilityMetricsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilitySpec) DeepCopyInto(out *MonolithicObservabilitySpec) { + *out = *in + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = new(MonolithicObservabilityMetricsSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilitySpec. +func (in *MonolithicObservabilitySpec) DeepCopy() *MonolithicObservabilitySpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilitySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicStorageSpec) DeepCopyInto(out *MonolithicStorageSpec) { + *out = *in + in.Traces.DeepCopyInto(&out.Traces) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicStorageSpec. +func (in *MonolithicStorageSpec) DeepCopy() *MonolithicStorageSpec { + if in == nil { + return nil + } + out := new(MonolithicStorageSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicTracesStoragePersistentVolumeSpec) DeepCopyInto(out *MonolithicTracesStoragePersistentVolumeSpec) { + *out = *in + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicTracesStoragePersistentVolumeSpec. +func (in *MonolithicTracesStoragePersistentVolumeSpec) DeepCopy() *MonolithicTracesStoragePersistentVolumeSpec { + if in == nil { + return nil + } + out := new(MonolithicTracesStoragePersistentVolumeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicTracesStorageSpec) DeepCopyInto(out *MonolithicTracesStorageSpec) { + *out = *in + if in.WAL != nil { + in, out := &in.WAL, &out.WAL + *out = new(MonolithicTracesStorageWALSpec) + (*in).DeepCopyInto(*out) + } + if in.PV != nil { + in, out := &in.PV, &out.PV + *out = new(MonolithicTracesStoragePersistentVolumeSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicTracesStorageSpec. +func (in *MonolithicTracesStorageSpec) DeepCopy() *MonolithicTracesStorageSpec { + if in == nil { + return nil + } + out := new(MonolithicTracesStorageSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicTracesStorageWALSpec) DeepCopyInto(out *MonolithicTracesStorageWALSpec) { + *out = *in + out.Size = in.Size.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicTracesStorageWALSpec. +func (in *MonolithicTracesStorageWALSpec) DeepCopy() *MonolithicTracesStorageWALSpec { + if in == nil { + return nil + } + out := new(MonolithicTracesStorageWALSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCSpec) DeepCopyInto(out *OIDCSpec) { *out = *in @@ -771,6 +1085,116 @@ func (in *TempoGatewaySpec) DeepCopy() *TempoGatewaySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TempoMonolithic) DeepCopyInto(out *TempoMonolithic) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TempoMonolithic. +func (in *TempoMonolithic) DeepCopy() *TempoMonolithic { + if in == nil { + return nil + } + out := new(TempoMonolithic) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TempoMonolithic) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TempoMonolithicList) DeepCopyInto(out *TempoMonolithicList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TempoMonolithic, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TempoMonolithicList. +func (in *TempoMonolithicList) DeepCopy() *TempoMonolithicList { + if in == nil { + return nil + } + out := new(TempoMonolithicList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TempoMonolithicList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TempoMonolithicSpec) DeepCopyInto(out *TempoMonolithicSpec) { + *out = *in + in.Storage.DeepCopyInto(&out.Storage) + if in.Ingestion != nil { + in, out := &in.Ingestion, &out.Ingestion + *out = new(MonolithicIngestionSpec) + (*in).DeepCopyInto(*out) + } + if in.JaegerUI != nil { + in, out := &in.JaegerUI, &out.JaegerUI + *out = new(MonolithicJaegerUISpec) + (*in).DeepCopyInto(*out) + } + if in.Observability != nil { + in, out := &in.Observability, &out.Observability + *out = new(MonolithicObservabilitySpec) + (*in).DeepCopyInto(*out) + } + if in.ExtraConfig != nil { + in, out := &in.ExtraConfig, &out.ExtraConfig + *out = new(MonolithicExtraConfigSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TempoMonolithicSpec. +func (in *TempoMonolithicSpec) DeepCopy() *TempoMonolithicSpec { + if in == nil { + return nil + } + out := new(TempoMonolithicSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TempoMonolithicStatus) DeepCopyInto(out *TempoMonolithicStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TempoMonolithicStatus. +func (in *TempoMonolithicStatus) DeepCopy() *TempoMonolithicStatus { + if in == nil { + return nil + } + out := new(TempoMonolithicStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TempoQueryFrontendSpec) DeepCopyInto(out *TempoQueryFrontendSpec) { *out = *in diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index bd9ff4e0c..26fcd0a89 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -4,6 +4,20 @@ metadata: annotations: alm-examples: |- [ + { + "apiVersion": "tempo.grafana.com/v1alpha1", + "kind": "TempoMonolithic", + "metadata": { + "name": "sample" + }, + "spec": { + "storage": { + "traces": { + "backend": "memory" + } + } + } + }, { "apiVersion": "tempo.grafana.com/v1alpha1", "kind": "TempoStack", @@ -42,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2023-12-13T18:24:45Z" + createdAt: "2023-12-21T12:56:45Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -57,6 +71,11 @@ spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: TempoMonolithic is the Schema for the tempomonolithics API. + displayName: Tempo Monolithic + kind: TempoMonolithic + name: tempomonolithics.tempo.grafana.com + version: v1alpha1 - description: TempoStack is the spec for Tempo deployments. displayName: TempoStack kind: TempoStack @@ -739,6 +758,32 @@ spec: - list - update - watch + - apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics/finalizers + verbs: + - update + - apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics/status + verbs: + - get + - patch + - update - apiGroups: - tempo.grafana.com resources: @@ -965,6 +1010,26 @@ spec: name: tempo-gateway-opa version: 0.6.0 webhookdefinitions: + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: tempo-operator-controller + failurePolicy: Fail + generateName: mtempomonolithic.kb.io + rules: + - apiGroups: + - tempo.grafana.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - tempomonolithics + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-tempo-grafana-com-v1alpha1-tempomonolithic - admissionReviewVersions: - v1 containerPort: 443 @@ -985,6 +1050,26 @@ spec: targetPort: 9443 type: MutatingAdmissionWebhook webhookPath: /mutate-tempo-grafana-com-v1alpha1-tempostack + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: tempo-operator-controller + failurePolicy: Fail + generateName: vtempomonolithic.kb.io + rules: + - apiGroups: + - tempo.grafana.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - tempomonolithics + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-tempo-grafana-com-v1alpha1-tempomonolithic - admissionReviewVersions: - v1 containerPort: 443 diff --git a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml new file mode 100644 index 000000000..04502402a --- /dev/null +++ b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -0,0 +1,228 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: operator-lifecycle-manager + app.kubernetes.io/name: tempo-operator + app.kubernetes.io/part-of: tempo-operator + name: tempomonolithics.tempo.grafana.com +spec: + group: tempo.grafana.com + names: + kind: TempoMonolithic + listKind: TempoMonolithicList + plural: tempomonolithics + singular: tempomonolithic + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: TempoMonolithic is the Schema for the tempomonolithics API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TempoMonolithicSpec defines the desired state of TempoMonolithic. + properties: + extraConfig: + description: ExtraConfig defines any extra (overlay) configuration + for components + properties: + tempo: + description: Tempo defines any extra Tempo configuration, which + will be merged with the operator's generated Tempo configuration + x-kubernetes-preserve-unknown-fields: true + type: object + ingestion: + description: Ingestion defines the trace ingestion configuration + properties: + otlp: + description: OTLP defines the ingestion configuration for OTLP + properties: + grpc: + description: GRPC defines the OTLP/gRPC configuration + properties: + enabled: + description: Enabled defines if OTLP over gRPC is enabled + type: boolean + required: + - enabled + type: object + http: + description: HTTP defines the OTLP/HTTP configuration + properties: + enabled: + description: Enabled defines if OTLP over HTTP is enabled + type: boolean + required: + - enabled + type: object + type: object + tls: + description: TLS defines the TLS configuration for ingestion + properties: + ca: + description: CA defines the name of a secret containing the + CA certificate + minLength: 1 + type: string + cert: + description: Cert defines the name of a secret containing + the TLS certificate and private key + minLength: 1 + type: string + enabled: + description: Enabled defines if TLS is enabled for ingestion + type: boolean + required: + - ca + - cert + - enabled + type: object + type: object + jaegerui: + description: JaegerUI defines the Jaeger UI configuration + properties: + enabled: + description: Enabled defines if the Jaeger UI should be enabled + type: boolean + ingress: + description: Ingress defines the ingress configuration for Jaeger + UI + properties: + enabled: + description: Enabled defines if an Ingress object should be + created for Jaeger UI + type: boolean + required: + - enabled + type: object + route: + description: Route defines the route configuration for Jaeger + UI + properties: + enabled: + description: Enabled defines if a Route object should be created + for Jaeger UI + type: boolean + required: + - enabled + type: object + required: + - enabled + type: object + management: + description: ManagementState defines whether this instance is managed + by the operator or self-managed + enum: + - Managed + - Unmanaged + type: string + observability: + description: Observability defines observability configuration for + the Tempo deployment + properties: + metrics: + description: Metrics defines the metrics configuration of the + Tempo deployment + properties: + prometheusRules: + description: ServiceMonitors defines the PrometheusRule configuration + properties: + enabled: + description: Enabled defines if the operator should create + PrometheusRules for this Tempo deployment + type: boolean + required: + - enabled + type: object + serviceMonitors: + description: ServiceMonitors defines the ServiceMonitor configuration + properties: + enabled: + description: Enabled defines if the operator should create + ServiceMonitors for this Tempo deployment + type: boolean + required: + - enabled + type: object + type: object + type: object + storage: + description: Storage defines the backend storage configuration + properties: + traces: + description: Traces defines the backend storage configuration + for traces + properties: + backend: + description: Backend defines the backend for storing traces + enum: + - memory + - pv + type: string + pv: + description: PV defines the Persistent Volume configuration + properties: + size: + anyOf: + - type: integer + - type: string + description: Size defines the size of the Persistent Volume + for storing the traces. Defaults to 10Gi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - size + type: object + wal: + description: WAL defines the write-ahead logging (WAL) configuration + properties: + size: + anyOf: + - type: integer + - type: string + description: Size defines the size of the Persistent Volume + for storing the WAL. Defaults to 10Gi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - size + type: object + required: + - backend + - pv + - wal + type: object + required: + - traces + type: object + type: object + status: + description: TempoMonolithicStatus defines the observed state of TempoMonolithic. + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index 697f9809a..8951a1ee8 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -4,6 +4,20 @@ metadata: annotations: alm-examples: |- [ + { + "apiVersion": "tempo.grafana.com/v1alpha1", + "kind": "TempoMonolithic", + "metadata": { + "name": "sample" + }, + "spec": { + "storage": { + "traces": { + "backend": "memory" + } + } + } + }, { "apiVersion": "tempo.grafana.com/v1alpha1", "kind": "TempoStack", @@ -42,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2023-12-13T18:24:44Z" + createdAt: "2023-12-21T12:56:44Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -57,6 +71,11 @@ spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: TempoMonolithic is the Schema for the tempomonolithics API. + displayName: Tempo Monolithic + kind: TempoMonolithic + name: tempomonolithics.tempo.grafana.com + version: v1alpha1 - description: TempoStack is the spec for Tempo deployments. displayName: TempoStack kind: TempoStack @@ -739,6 +758,32 @@ spec: - list - update - watch + - apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics/finalizers + verbs: + - update + - apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics/status + verbs: + - get + - patch + - update - apiGroups: - tempo.grafana.com resources: @@ -976,6 +1021,26 @@ spec: name: tempo-gateway-opa version: 0.6.0 webhookdefinitions: + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: tempo-operator-controller + failurePolicy: Fail + generateName: mtempomonolithic.kb.io + rules: + - apiGroups: + - tempo.grafana.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - tempomonolithics + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-tempo-grafana-com-v1alpha1-tempomonolithic - admissionReviewVersions: - v1 containerPort: 443 @@ -996,6 +1061,26 @@ spec: targetPort: 9443 type: MutatingAdmissionWebhook webhookPath: /mutate-tempo-grafana-com-v1alpha1-tempostack + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: tempo-operator-controller + failurePolicy: Fail + generateName: vtempomonolithic.kb.io + rules: + - apiGroups: + - tempo.grafana.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - tempomonolithics + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-tempo-grafana-com-v1alpha1-tempomonolithic - admissionReviewVersions: - v1 containerPort: 443 diff --git a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml new file mode 100644 index 000000000..04502402a --- /dev/null +++ b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -0,0 +1,228 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: operator-lifecycle-manager + app.kubernetes.io/name: tempo-operator + app.kubernetes.io/part-of: tempo-operator + name: tempomonolithics.tempo.grafana.com +spec: + group: tempo.grafana.com + names: + kind: TempoMonolithic + listKind: TempoMonolithicList + plural: tempomonolithics + singular: tempomonolithic + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: TempoMonolithic is the Schema for the tempomonolithics API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TempoMonolithicSpec defines the desired state of TempoMonolithic. + properties: + extraConfig: + description: ExtraConfig defines any extra (overlay) configuration + for components + properties: + tempo: + description: Tempo defines any extra Tempo configuration, which + will be merged with the operator's generated Tempo configuration + x-kubernetes-preserve-unknown-fields: true + type: object + ingestion: + description: Ingestion defines the trace ingestion configuration + properties: + otlp: + description: OTLP defines the ingestion configuration for OTLP + properties: + grpc: + description: GRPC defines the OTLP/gRPC configuration + properties: + enabled: + description: Enabled defines if OTLP over gRPC is enabled + type: boolean + required: + - enabled + type: object + http: + description: HTTP defines the OTLP/HTTP configuration + properties: + enabled: + description: Enabled defines if OTLP over HTTP is enabled + type: boolean + required: + - enabled + type: object + type: object + tls: + description: TLS defines the TLS configuration for ingestion + properties: + ca: + description: CA defines the name of a secret containing the + CA certificate + minLength: 1 + type: string + cert: + description: Cert defines the name of a secret containing + the TLS certificate and private key + minLength: 1 + type: string + enabled: + description: Enabled defines if TLS is enabled for ingestion + type: boolean + required: + - ca + - cert + - enabled + type: object + type: object + jaegerui: + description: JaegerUI defines the Jaeger UI configuration + properties: + enabled: + description: Enabled defines if the Jaeger UI should be enabled + type: boolean + ingress: + description: Ingress defines the ingress configuration for Jaeger + UI + properties: + enabled: + description: Enabled defines if an Ingress object should be + created for Jaeger UI + type: boolean + required: + - enabled + type: object + route: + description: Route defines the route configuration for Jaeger + UI + properties: + enabled: + description: Enabled defines if a Route object should be created + for Jaeger UI + type: boolean + required: + - enabled + type: object + required: + - enabled + type: object + management: + description: ManagementState defines whether this instance is managed + by the operator or self-managed + enum: + - Managed + - Unmanaged + type: string + observability: + description: Observability defines observability configuration for + the Tempo deployment + properties: + metrics: + description: Metrics defines the metrics configuration of the + Tempo deployment + properties: + prometheusRules: + description: ServiceMonitors defines the PrometheusRule configuration + properties: + enabled: + description: Enabled defines if the operator should create + PrometheusRules for this Tempo deployment + type: boolean + required: + - enabled + type: object + serviceMonitors: + description: ServiceMonitors defines the ServiceMonitor configuration + properties: + enabled: + description: Enabled defines if the operator should create + ServiceMonitors for this Tempo deployment + type: boolean + required: + - enabled + type: object + type: object + type: object + storage: + description: Storage defines the backend storage configuration + properties: + traces: + description: Traces defines the backend storage configuration + for traces + properties: + backend: + description: Backend defines the backend for storing traces + enum: + - memory + - pv + type: string + pv: + description: PV defines the Persistent Volume configuration + properties: + size: + anyOf: + - type: integer + - type: string + description: Size defines the size of the Persistent Volume + for storing the traces. Defaults to 10Gi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - size + type: object + wal: + description: WAL defines the write-ahead logging (WAL) configuration + properties: + size: + anyOf: + - type: integer + - type: string + description: Size defines the size of the Persistent Volume + for storing the WAL. Defaults to 10Gi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - size + type: object + required: + - backend + - pv + - wal + type: object + required: + - traces + type: object + type: object + status: + description: TempoMonolithicStatus defines the observed state of TempoMonolithic. + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/cmd/start/main.go b/cmd/start/main.go index 6613dde64..d9bb5b119 100644 --- a/cmd/start/main.go +++ b/cmd/start/main.go @@ -67,12 +67,25 @@ func start(c *cobra.Command, args []string) { os.Exit(1) } + if err = (&controllers.TempoMonolithicReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + CtrlConfig: ctrlConfig, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "TempoMonolithic") + os.Exit(1) + } + enableWebhooks := os.Getenv("ENABLE_WEBHOOKS") != "false" if enableWebhooks { if err = (&tempov1alpha1.TempoStack{}).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 { + setupLog.Error(err, "unable to create webhook", "webhook", "TempoMonolithic") + os.Exit(1) + } } //+kubebuilder:scaffold:builder @@ -130,7 +143,14 @@ func addDependencies(mgr ctrl.Manager, ctrlConfig configv1alpha1.ProjectConfig, Client: mgr.GetClient(), Scheme: mgr.GetScheme(), } - return reconciler.Reconcile(ctx, ctrlConfig) + + // log error but do not fail operator startup if operator reconcile fails + // operator reconcile is only used for creating ServiceMonitor and PrometheusRules of the operator itself + err := reconciler.Reconcile(ctx, ctrlConfig) + if err != nil { + ctrl.LoggerFrom(ctx).WithName("operator-reconcile").Error(err, "cannot reconcile operator") + } + return nil })) if err != nil { return fmt.Errorf("failed to setup operator reconciler: %w", err) diff --git a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml new file mode 100644 index 000000000..857af2d41 --- /dev/null +++ b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml @@ -0,0 +1,219 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: tempomonolithics.tempo.grafana.com +spec: + group: tempo.grafana.com + names: + kind: TempoMonolithic + listKind: TempoMonolithicList + plural: tempomonolithics + singular: tempomonolithic + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: TempoMonolithic is the Schema for the tempomonolithics API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TempoMonolithicSpec defines the desired state of TempoMonolithic. + properties: + extraConfig: + description: ExtraConfig defines any extra (overlay) configuration + for components + properties: + tempo: + description: Tempo defines any extra Tempo configuration, which + will be merged with the operator's generated Tempo configuration + x-kubernetes-preserve-unknown-fields: true + type: object + ingestion: + description: Ingestion defines the trace ingestion configuration + properties: + otlp: + description: OTLP defines the ingestion configuration for OTLP + properties: + grpc: + description: GRPC defines the OTLP/gRPC configuration + properties: + enabled: + description: Enabled defines if OTLP over gRPC is enabled + type: boolean + required: + - enabled + type: object + http: + description: HTTP defines the OTLP/HTTP configuration + properties: + enabled: + description: Enabled defines if OTLP over HTTP is enabled + type: boolean + required: + - enabled + type: object + type: object + tls: + description: TLS defines the TLS configuration for ingestion + properties: + ca: + description: CA defines the name of a secret containing the + CA certificate + minLength: 1 + type: string + cert: + description: Cert defines the name of a secret containing + the TLS certificate and private key + minLength: 1 + type: string + enabled: + description: Enabled defines if TLS is enabled for ingestion + type: boolean + required: + - ca + - cert + - enabled + type: object + type: object + jaegerui: + description: JaegerUI defines the Jaeger UI configuration + properties: + enabled: + description: Enabled defines if the Jaeger UI should be enabled + type: boolean + ingress: + description: Ingress defines the ingress configuration for Jaeger + UI + properties: + enabled: + description: Enabled defines if an Ingress object should be + created for Jaeger UI + type: boolean + required: + - enabled + type: object + route: + description: Route defines the route configuration for Jaeger + UI + properties: + enabled: + description: Enabled defines if a Route object should be created + for Jaeger UI + type: boolean + required: + - enabled + type: object + required: + - enabled + type: object + management: + description: ManagementState defines whether this instance is managed + by the operator or self-managed + enum: + - Managed + - Unmanaged + type: string + observability: + description: Observability defines observability configuration for + the Tempo deployment + properties: + metrics: + description: Metrics defines the metrics configuration of the + Tempo deployment + properties: + prometheusRules: + description: ServiceMonitors defines the PrometheusRule configuration + properties: + enabled: + description: Enabled defines if the operator should create + PrometheusRules for this Tempo deployment + type: boolean + required: + - enabled + type: object + serviceMonitors: + description: ServiceMonitors defines the ServiceMonitor configuration + properties: + enabled: + description: Enabled defines if the operator should create + ServiceMonitors for this Tempo deployment + type: boolean + required: + - enabled + type: object + type: object + type: object + storage: + description: Storage defines the backend storage configuration + properties: + traces: + description: Traces defines the backend storage configuration + for traces + properties: + backend: + description: Backend defines the backend for storing traces + enum: + - memory + - pv + type: string + pv: + description: PV defines the Persistent Volume configuration + properties: + size: + anyOf: + - type: integer + - type: string + description: Size defines the size of the Persistent Volume + for storing the traces. Defaults to 10Gi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - size + type: object + wal: + description: WAL defines the write-ahead logging (WAL) configuration + properties: + size: + anyOf: + - type: integer + - type: string + description: Size defines the size of the Persistent Volume + for storing the WAL. Defaults to 10Gi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - size + type: object + required: + - backend + - pv + - wal + type: object + required: + - traces + type: object + type: object + status: + description: TempoMonolithicStatus defines the observed state of TempoMonolithic. + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 04bf60698..2eee3e92c 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -3,6 +3,7 @@ # It should be run by config/default resources: - bases/tempo.grafana.com_tempostacks.yaml +- bases/tempo.grafana.com_tempomonolithics.yaml #- bases/config.tempo.grafana.com_projectconfigs.yaml #+kubebuilder:scaffold:crdkustomizeresource diff --git a/config/crd/patches/cainjection_in_tempomonolithics.yaml b/config/crd/patches/cainjection_in_tempomonolithics.yaml new file mode 100644 index 000000000..689bf4436 --- /dev/null +++ b/config/crd/patches/cainjection_in_tempomonolithics.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: tempomonolithics.tempo.grafana.com diff --git a/config/crd/patches/webhook_in_tempomonolithics.yaml b/config/crd/patches/webhook_in_tempomonolithics.yaml new file mode 100644 index 000000000..87d5b6053 --- /dev/null +++ b/config/crd/patches/webhook_in_tempomonolithics.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: tempomonolithics.tempo.grafana.com +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml b/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml index 9e51e750b..ab2ca3e9d 100644 --- a/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml +++ b/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml @@ -18,6 +18,11 @@ spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: TempoMonolithic is the Schema for the tempomonolithics API. + displayName: Tempo Monolithic + kind: TempoMonolithic + name: tempomonolithics.tempo.grafana.com + version: v1alpha1 - description: TempoStack is the spec for Tempo deployments. displayName: TempoStack kind: TempoStack diff --git a/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml b/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml index 9e51e750b..ab2ca3e9d 100644 --- a/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml +++ b/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml @@ -18,6 +18,11 @@ spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: TempoMonolithic is the Schema for the tempomonolithics API. + displayName: Tempo Monolithic + kind: TempoMonolithic + name: tempomonolithics.tempo.grafana.com + version: v1alpha1 - description: TempoStack is the spec for Tempo deployments. displayName: TempoStack kind: TempoStack diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e4ebc88bd..7c433e504 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -118,6 +118,32 @@ rules: - list - update - watch +- apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics/finalizers + verbs: + - update +- apiGroups: + - tempo.grafana.com + resources: + - tempomonolithics/status + verbs: + - get + - patch + - update - apiGroups: - tempo.grafana.com resources: diff --git a/config/samples/community/kustomization.yaml b/config/samples/community/kustomization.yaml index c7b35e7c5..71b5852c7 100644 --- a/config/samples/community/kustomization.yaml +++ b/config/samples/community/kustomization.yaml @@ -1,4 +1,5 @@ ## Append samples you want in your CSV to this file as resources ## resources: - tempo_v1alpha1_tempostack.yaml +- tempo_v1alpha1_tempomonolithic.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/community/tempo_v1alpha1_tempomonolithic.yaml b/config/samples/community/tempo_v1alpha1_tempomonolithic.yaml new file mode 100644 index 000000000..7530f6a54 --- /dev/null +++ b/config/samples/community/tempo_v1alpha1_tempomonolithic.yaml @@ -0,0 +1,8 @@ +apiVersion: tempo.grafana.com/v1alpha1 +kind: TempoMonolithic +metadata: + name: sample +spec: + storage: + traces: + backend: memory diff --git a/config/samples/openshift/kustomization.yaml b/config/samples/openshift/kustomization.yaml index c7b35e7c5..71b5852c7 100644 --- a/config/samples/openshift/kustomization.yaml +++ b/config/samples/openshift/kustomization.yaml @@ -1,4 +1,5 @@ ## Append samples you want in your CSV to this file as resources ## resources: - tempo_v1alpha1_tempostack.yaml +- tempo_v1alpha1_tempomonolithic.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/openshift/tempo_v1alpha1_tempomonolithic.yaml b/config/samples/openshift/tempo_v1alpha1_tempomonolithic.yaml new file mode 100644 index 000000000..7530f6a54 --- /dev/null +++ b/config/samples/openshift/tempo_v1alpha1_tempomonolithic.yaml @@ -0,0 +1,8 @@ +apiVersion: tempo.grafana.com/v1alpha1 +kind: TempoMonolithic +metadata: + name: sample +spec: + storage: + traces: + backend: memory diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index ac8ff9096..5ec8c2305 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -5,6 +5,26 @@ metadata: creationTimestamp: null name: mutating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-tempo-grafana-com-v1alpha1-tempomonolithic + failurePolicy: Fail + name: mtempomonolithic.kb.io + rules: + - apiGroups: + - tempo.grafana.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - tempomonolithics + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -32,6 +52,26 @@ metadata: creationTimestamp: null name: validating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-tempo-grafana-com-v1alpha1-tempomonolithic + failurePolicy: Fail + name: vtempomonolithic.kb.io + rules: + - apiGroups: + - tempo.grafana.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - tempomonolithics + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/controllers/tempo/common.go b/controllers/tempo/common.go new file mode 100644 index 000000000..d0ce86fa8 --- /dev/null +++ b/controllers/tempo/common.go @@ -0,0 +1,68 @@ +package controllers + +import ( + "context" + "errors" + "fmt" + + "github.com/go-logr/logr" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/grafana/tempo-operator/internal/manifests" +) + +func isNamespaceScoped(obj client.Object) bool { + switch obj.(type) { + case *rbacv1.ClusterRole, *rbacv1.ClusterRoleBinding: + return false + default: + return true + } +} + +// reconcileManagedObjects creates or updates all managed objects. +// If immutable fields are changed, the object will be deleted and re-created. +func reconcileManagedObjects(ctx context.Context, log logr.Logger, k8sclient client.Client, owner metav1.Object, scheme *runtime.Scheme, managedObjects []client.Object) error { + errs := []error{} + for _, obj := range managedObjects { + l := log.WithValues( + "objectName", obj.GetName(), + "objectKind", obj.GetObjectKind().GroupVersionKind(), + ) + + if isNamespaceScoped(obj) { + if err := ctrl.SetControllerReference(owner, obj, scheme); err != nil { + l.Error(err, "failed to set controller owner reference to resource") + errs = append(errs, err) + continue + } + } + + desired := obj.DeepCopyObject().(client.Object) + mutateFn := manifests.MutateFuncFor(obj, desired) + + op, err := ctrl.CreateOrUpdate(ctx, k8sclient, obj, mutateFn) + + var immutableErr *manifests.ImmutableErr + if err != nil && errors.As(err, &immutableErr) { + l.Error(err, "detected a change in an immutable field. The object will be deleted, and re-created on next reconcile", "obj", obj.GetName()) + err = k8sclient.Delete(ctx, desired) + } + if err != nil { + l.Error(err, "failed to configure resource") + errs = append(errs, err) + continue + } + + l.V(1).Info(fmt.Sprintf("resource has been %s", op)) + } + + if len(errs) > 0 { + return fmt.Errorf("failed to create objects for %s: %w", owner.GetName(), errors.Join(errs...)) + } + return nil +} diff --git a/controllers/tempo/tempomonolithic_controller.go b/controllers/tempo/tempomonolithic_controller.go new file mode 100644 index 000000000..dde031b05 --- /dev/null +++ b/controllers/tempo/tempomonolithic_controller.go @@ -0,0 +1,78 @@ +package controllers + +import ( + "context" + "fmt" + + appsv1 "k8s.io/api/apps/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + "github.com/grafana/tempo-operator/internal/manifests/monolithic" +) + +// TempoMonolithicReconciler reconciles a TempoMonolithic object. +type TempoMonolithicReconciler struct { + client.Client + Scheme *runtime.Scheme + CtrlConfig configv1alpha1.ProjectConfig +} + +//+kubebuilder:rbac:groups=tempo.grafana.com,resources=tempomonolithics,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=tempo.grafana.com,resources=tempomonolithics/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=tempo.grafana.com,resources=tempomonolithics/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +func (r *TempoMonolithicReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx).WithName("tempomonolithic-reconcile") + + log.V(1).Info("starting reconcile loop") + defer log.V(1).Info("finished reconcile loop") + + tempo := v1alpha1.TempoMonolithic{} + if err := r.Get(ctx, req.NamespacedName, &tempo); err != nil { + if !apierrors.IsNotFound(err) { + log.Error(err, "unable to fetch TempoMonolithic") + return ctrl.Result{}, fmt.Errorf("could not fetch tempo: %w", err) + } + + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. + return ctrl.Result{}, nil + } + + if tempo.Spec.Management == v1alpha1.ManagementStateUnmanaged { + log.Info("Skipping reconciliation for unmanaged TempoMonolithic resource", "name", req.String()) + return ctrl.Result{}, nil + } + + managedObjects, err := monolithic.BuildAll(monolithic.Options{ + CtrlConfig: r.CtrlConfig, + Tempo: tempo, + }) + if err != nil { + return ctrl.Result{}, fmt.Errorf("error building manifests: %w", err) + } + + err = reconcileManagedObjects(ctx, log, r.Client, &tempo, r.Scheme, managedObjects) + if err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *TempoMonolithicReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.TempoMonolithic{}). + Owns(&appsv1.StatefulSet{}). + Complete(r) +} diff --git a/controllers/tempo/tempostack_controller.go b/controllers/tempo/tempostack_controller.go index 3c3dd53df..4a2bb396f 100644 --- a/controllers/tempo/tempostack_controller.go +++ b/controllers/tempo/tempostack_controller.go @@ -72,7 +72,7 @@ func (r *TempoStackReconciler) Reconcile(ctx context.Context, req ctrl.Request) tempo := v1alpha1.TempoStack{} if err := r.Get(ctx, req.NamespacedName, &tempo); err != nil { if !apierrors.IsNotFound(err) { - log.Error(err, "unable to fetch TempoTempoStack") + log.Error(err, "unable to fetch TempoStack") return ctrl.Result{}, fmt.Errorf("could not fetch tempo: %w", err) } diff --git a/controllers/tempo/tempostack_create_or_update.go b/controllers/tempo/tempostack_create_or_update.go index 8b4799786..0d9ebdece 100644 --- a/controllers/tempo/tempostack_create_or_update.go +++ b/controllers/tempo/tempostack_create_or_update.go @@ -12,7 +12,6 @@ import ( monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" - rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation/field" @@ -75,15 +74,6 @@ func (r *TempoStackReconciler) getStorageConfig(ctx context.Context, tempo v1alp return params, nil } -func isNamespaceScoped(obj client.Object) bool { - switch obj.(type) { - case *rbacv1.ClusterRole, *rbacv1.ClusterRoleBinding: - return false - default: - return true - } -} - func (r *TempoStackReconciler) createOrUpdate(ctx context.Context, log logr.Logger, req ctrl.Request, tempo v1alpha1.TempoStack) error { storageConfig, err := r.getStorageConfig(ctx, tempo) if err != nil { diff --git a/docs/operator/api.md b/docs/operator/api.md index 89151191c..cab1608b2 100644 --- a/docs/operator/api.md +++ b/docs/operator/api.md @@ -1331,7 +1331,7 @@ RateLimitSpec

-(Appears on:TempoStackSpec) +(Appears on:TempoMonolithicSpec, TempoStackSpec)

@@ -1550,17 +1550,17 @@ using an in-process OpenPolicyAgent Rego authorizer.

-## OIDCSpec { #tempo-grafana-com-v1alpha1-OIDCSpec } +## MonolithicExtraConfigSpec { #tempo-grafana-com-v1alpha1-MonolithicExtraConfigSpec }

-(Appears on:AuthenticationSpec) +(Appears on:TempoMonolithicSpec)

-

OIDCSpec defines the oidc configuration spec for Tempo Gateway component.

+

MonolithicExtraConfigSpec defines extra configuration for this deployment.

@@ -1584,13 +1584,13 @@ using an in-process OpenPolicyAgent Rego authorizer.

-secret
+tempo
- + -TenantSecretSpec +k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON @@ -1600,45 +1600,53 @@ TenantSecretSpec -(Optional) - -

Secret defines the spec for the clientID, clientSecret and issuerCAPath for tenant’s authentication.

+

Tempo defines any extra Tempo configuration, which will be merged with the operator’s generated Tempo configuration

- + + - +## MonolithicIngestionOTLPProtocolsGRPCSpec { #tempo-grafana-com-v1alpha1-MonolithicIngestionOTLPProtocolsGRPCSpec } -issuerURL
+

- +(Appears on:MonolithicIngestionOTLPSpec) -string +

- +
- +

MonolithicIngestionOTLPProtocolsGRPCSpec defines the settings for OTLP ingestion over GRPC.

+ +
- + -(Optional) + -

IssuerURL defines the URL for issuer.

+ + + + + - + + + + - + +
FieldDescription
-redirectURL
+enabled
-string +bool @@ -1646,45 +1654,53 @@ string
-(Optional) - -

RedirectURL defines the URL for redirect.

+

Enabled defines if OTLP over gRPC is enabled

- +## MonolithicIngestionOTLPProtocolsHTTPSpec { #tempo-grafana-com-v1alpha1-MonolithicIngestionOTLPProtocolsHTTPSpec } -groupClaim
+

- +(Appears on:MonolithicIngestionOTLPSpec) -string +

- +
- +

MonolithicIngestionOTLPProtocolsHTTPSpec defines the settings for OTLP ingestion over HTTP.

+ +
- + -(Optional) + -

Group claim field from ID Token

+ + + + + - + + + + @@ -1702,17 +1716,17 @@ string
FieldDescription
-usernameClaim
+enabled
-string +bool @@ -1692,9 +1708,7 @@ string
-(Optional) - -

User claim field from ID Token

+

Enabled defines if OTLP over HTTP is enabled

-## ObjectStorageSecretSpec { #tempo-grafana-com-v1alpha1-ObjectStorageSecretSpec } +## MonolithicIngestionOTLPSpec { #tempo-grafana-com-v1alpha1-MonolithicIngestionOTLPSpec }

-(Appears on:ObjectStorageSpec) +(Appears on:MonolithicIngestionSpec)

-

ObjectStorageSecretSpec is a secret reference containing name only, no namespace.

+

MonolithicIngestionOTLPSpec defines the settings for OTLP ingestion.

@@ -1736,13 +1750,13 @@ string -type
+grpc
- + -ObjectStorageSecretType +MonolithicIngestionOTLPProtocolsGRPCSpec @@ -1752,7 +1766,7 @@ ObjectStorageSecretType -

Type of object storage that should be used

+

GRPC defines the OTLP/gRPC configuration

@@ -1761,11 +1775,15 @@ ObjectStorageSecretType -name
+http
-string + + +MonolithicIngestionOTLPProtocolsHTTPSpec + + @@ -1773,7 +1791,7 @@ string -

Name of a secret in the namespace configured for object storage secrets.

+

HTTP defines the OTLP/HTTP configuration

@@ -1781,19 +1799,17 @@ string -## ObjectStorageSecretType { #tempo-grafana-com-v1alpha1-ObjectStorageSecretType } - -(string alias) +## MonolithicIngestionSpec { #tempo-grafana-com-v1alpha1-MonolithicIngestionSpec }

-(Appears on:ObjectStorageSecretSpec) +(Appears on:TempoMonolithicSpec)

-

ObjectStorageSecretType defines the type of storage which can be used with the Tempo cluster.

+

MonolithicIngestionSpec defines the ingestion settings.

@@ -1803,7 +1819,7 @@ string -Value +Field Description @@ -1811,36 +1827,72 @@ string -

"azure"

+ + + + + + +otlp
+ + + + + +MonolithicIngestionOTLPSpec + + + + -

ObjectStorageSecretAzure when using Azure Storage for Tempo storage.

-

"gcs"

+ + +

OTLP defines the ingestion configuration for OTLP

-

ObjectStorageSecretGCS when using Google Cloud Storage for Tempo storage.

+ -

"s3"

+ + + + +tls
+ + + + + +MonolithicIngestionTLSSpec + + + + -

ObjectStorageSecretS3 when using S3 for Tempo storage.

- + + +

TLS defines the TLS configuration for ingestion

+ + + + + -## ObjectStorageSpec { #tempo-grafana-com-v1alpha1-ObjectStorageSpec } +## MonolithicIngestionTLSSpec { #tempo-grafana-com-v1alpha1-MonolithicIngestionTLSSpec }

-(Appears on:TempoStackSpec) +(Appears on:MonolithicIngestionSpec)

-

ObjectStorageSpec defines the requirements to access the object -storage bucket to persist traces by the ingester component.

+

MonolithicIngestionTLSSpec defines the TLS settings for ingestion.

@@ -1864,15 +1916,11 @@ storage bucket to persist traces by the ingester component.

-tls
+enabled
- - -ObjectStorageTLSSpec - - +bool @@ -1880,9 +1928,7 @@ ObjectStorageTLSSpec -(Optional) - -

TLS configuration for reaching the object storage endpoint.

+

Enabled defines if TLS is enabled for ingestion

@@ -1891,15 +1937,32 @@ ObjectStorageTLSSpec -secret
+ca
- +string -ObjectStorageSecretSpec + - + + + + +

CA defines the name of a secret containing the CA certificate

+ + + + + + + + +cert
+ + + +string @@ -1907,8 +1970,7 @@ ObjectStorageSecretSpec -

Secret for object storage authentication. -Name of a secret in the same namespace as the TempoStack custom resource.

+

Cert defines the name of a secret containing the TLS certificate and private key

@@ -1916,17 +1978,17 @@ Name of a secret in the same namespace as the TempoStack custom resource.

-## ObjectStorageTLSSpec { #tempo-grafana-com-v1alpha1-ObjectStorageTLSSpec } +## MonolithicJaegerUIIngressSpec { #tempo-grafana-com-v1alpha1-MonolithicJaegerUIIngressSpec }

-(Appears on:ObjectStorageSpec) +(Appears on:MonolithicJaegerUISpec)

-

ObjectStorageTLSSpec is the TLS configuration for reaching the object storage endpoint.

+

MonolithicJaegerUIIngressSpec defines the settings for the Jaeger UI ingress.

@@ -1950,11 +2012,11 @@ Name of a secret in the same namespace as the TempoStack custom resource.

-caName
+enabled
-string +bool @@ -1962,10 +2024,7 @@ string -(Optional) - -

CA is the name of a ConfigMap containing a ca.crt key with a CA certificate. -It needs to be in the same namespace as the TempoStack custom resource.

+

Enabled defines if an Ingress object should be created for Jaeger UI

@@ -1973,17 +2032,17 @@ It needs to be in the same namespace as the TempoStack custom resource.

-## ObservabilitySpec { #tempo-grafana-com-v1alpha1-ObservabilitySpec } +## MonolithicJaegerUIRouteSpec { #tempo-grafana-com-v1alpha1-MonolithicJaegerUIRouteSpec }

-(Appears on:TempoStackSpec) +(Appears on:MonolithicJaegerUISpec)

-

ObservabilitySpec defines how telemetry data gets handled.

+

MonolithicJaegerUIRouteSpec defines the settings for the Jaeger UI route.

@@ -2007,15 +2066,11 @@ It needs to be in the same namespace as the TempoStack custom resource.

-metrics
+enabled
- - -MetricsConfigSpec - - +bool @@ -2023,26 +2078,53 @@ MetricsConfigSpec -(Optional) - -

Metrics defines the metrics configuration for operands.

+

Enabled defines if a Route object should be created for Jaeger UI

+ + + +## MonolithicJaegerUISpec { #tempo-grafana-com-v1alpha1-MonolithicJaegerUISpec } + +

+ +(Appears on:TempoMonolithicSpec) + +

+ +
+ +

MonolithicJaegerUISpec defines the settings for the Jaeger UI.

+ +
+ + + + + + + + + + + + + + + + + @@ -2061,13 +2141,13 @@ TracingConfigSpec - -
FieldDescription
-tracing
+enabled
- - -TracingConfigSpec - - +bool @@ -2050,9 +2132,7 @@ TracingConfigSpec
-(Optional) - -

Tracing defines a config for operands.

+

Enabled defines if the Jaeger UI should be enabled

-grafana
+ingress
- + -GrafanaConfigSpec +MonolithicJaegerUIIngressSpec @@ -2077,86 +2157,50 @@ GrafanaConfigSpec
-(Optional) - -

Grafana defines the Grafana configuration for operands.

+

Ingress defines the ingress configuration for Jaeger UI

- -## PermissionType { #tempo-grafana-com-v1alpha1-PermissionType } - -(string alias) - -

- -(Appears on:RoleSpec) - -

- -
- -

PermissionType is a Tempo Gateway RBAC permission.

- -
- - - - - - + +route
- + - + - +MonolithicJaegerUIRouteSpec - + - + - - -
Value -Description

"read"

Read gives access to read data from a tenant.

-

"write"

Write gives access to write data to a tenant.

- -## PodStatusMap { #tempo-grafana-com-v1alpha1-PodStatusMap } - -(map[k8s.io/api/core/v1.PodPhase][]string alias) - -

- -(Appears on:ComponentStatus) + -

+

Route defines the route configuration for Jaeger UI

-
+ + -

PodStatusMap defines the type for mapping pod status to pod name.

- -
+ + -## QueryLimit { #tempo-grafana-com-v1alpha1-QueryLimit } +## MonolithicObservabilityMetricsPrometheusRulesSpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilityMetricsPrometheusRulesSpec }

-(Appears on:RateLimitSpec) +(Appears on:MonolithicObservabilityMetricsSpec)

-

QueryLimit defines query limits.

+

MonolithicObservabilityMetricsPrometheusRulesSpec defines the PrometheusRules settings.

@@ -2180,11 +2224,11 @@ GrafanaConfigSpec -maxBytesPerTagValues
+enabled
-int +bool @@ -2192,51 +2236,53 @@ int -(Optional) - -

MaxBytesPerTagValues defines the maximum size in bytes of a tag-values query.

+

Enabled defines if the operator should create PrometheusRules for this Tempo deployment

- + + - +## MonolithicObservabilityMetricsServiceMonitorsSpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilityMetricsServiceMonitorsSpec } -maxSearchBytesPerTrace
+

- +(Appears on:MonolithicObservabilityMetricsSpec) -int +

- +
- +

MonolithicObservabilityMetricsServiceMonitorsSpec defines the ServiceMonitor settings.

+ +
- + -(Optional) + -

DEPRECATED. MaxSearchBytesPerTrace defines the maximum size of search data for a single -trace in bytes. -default: 0 to disable.

+ + + + + - + + + + @@ -2255,17 +2298,17 @@ If this value is not set, then spec.search.maxDuration is used.

FieldDescription
-maxSearchDuration
+enabled
- - -Kubernetes meta/v1.Duration - - +bool @@ -2244,10 +2290,7 @@ Kubernetes meta/v1.Duration
-(Optional) - -

MaxSearchDuration defines the maximum allowed time range for a search. -If this value is not set, then spec.search.maxDuration is used.

+

Enabled defines if the operator should create ServiceMonitors for this Tempo deployment

-## RateLimitSpec { #tempo-grafana-com-v1alpha1-RateLimitSpec } +## MonolithicObservabilityMetricsSpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilityMetricsSpec }

-(Appears on:LimitSpec) +(Appears on:MonolithicObservabilitySpec)

-

RateLimitSpec defines rate limits for Ingestion and Query components.

+

MonolithicObservabilityMetricsSpec defines the metrics settings of the Tempo deployment.

@@ -2289,13 +2332,13 @@ If this value is not set, then spec.search.maxDuration is used.

-ingestion
+serviceMonitors
- + -IngestionLimitSpec +MonolithicObservabilityMetricsServiceMonitorsSpec @@ -2305,9 +2348,7 @@ IngestionLimitSpec -(Optional) - -

Ingestion is used to define ingestion rate limits.

+

ServiceMonitors defines the ServiceMonitor configuration

@@ -2316,13 +2357,13 @@ IngestionLimitSpec -query
+prometheusRules
- + -QueryLimit +MonolithicObservabilityMetricsPrometheusRulesSpec @@ -2332,9 +2373,7 @@ QueryLimit -(Optional) - -

Query is used to define query rate limits.

+

ServiceMonitors defines the PrometheusRule configuration

@@ -2342,17 +2381,17 @@ QueryLimit -## ReceiversTLSSpec { #tempo-grafana-com-v1alpha1-ReceiversTLSSpec } +## MonolithicObservabilitySpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilitySpec }

-(Appears on:TempoDistributorSpec) +(Appears on:TempoMonolithicSpec)

-

ReceiversTLSSpec is the TLS configuration for the receivers.

+

MonolithicObservabilitySpec defines the observability settings of the Tempo deployment.

@@ -2376,11 +2415,15 @@ QueryLimit -enabled
+metrics
-bool + + +MonolithicObservabilityMetricsSpec + + @@ -2388,62 +2431,57 @@ bool +

Metrics defines the metrics configuration of the Tempo deployment

+ - - - + + -caName
+## MonolithicStorageSpec { #tempo-grafana-com-v1alpha1-MonolithicStorageSpec } - +

-string +(Appears on:TempoMonolithicSpec) - +

- +
- +

MonolithicStorageSpec defines the storage for the Tempo deployment.

+ +
-

caName is the name of a ConfigMap containing a CA certificate. -It needs to be in the same namespace as the Tempo custom resource.

+ - - + - - + - -

certName is the name of a Secret containing a certificate and the private key -It needs to be in the same namespace as the Tempo custom resource.

+ - - + @@ -2462,17 +2497,19 @@ It needs to be in the same namespace as the Tempo custom resource.

- -certName
- - - -string - - +
FieldDescription +
-minVersion
+traces
-string + + +MonolithicTracesStorageSpec + + @@ -2451,10 +2489,7 @@ string
-(Optional) - -

minVersion is the name of a Secret containing a certificate and the private key -It needs to be in the same namespace as the Tempo custom resource.

+

Traces defines the backend storage configuration for traces

-## Resources { #tempo-grafana-com-v1alpha1-Resources } +## MonolithicTracesStorageBackend { #tempo-grafana-com-v1alpha1-MonolithicTracesStorageBackend } + +(string alias)

-(Appears on:TempoStackSpec) +(Appears on:MonolithicTracesStorageSpec)

-

Resources defines resources configuration.

+

MonolithicTracesStorageBackend defines the backend storage for traces.

@@ -2482,7 +2519,7 @@ It needs to be in the same namespace as the Tempo custom resource.

-Field +Value Description @@ -2490,52 +2527,30 @@ It needs to be in the same namespace as the Tempo custom resource.

- - - - - - -total
- - - - - -Kubernetes core/v1.ResourceRequirements - - - - +

"memory"

+

MonolithicTracesStorageBackendMemory specifies a in-memory storage backend.

- - -(Optional) - -

The total amount of resources for Tempo instance. -The operator autonomously splits resources between deployed Tempo components. -Only limits are supported, the operator calculates requests automatically. -See http://github.com/grafana/tempo/issues/1540.

+

"pv"

+

MonolithicTracesStorageBackendPersistentVolume specifies a Persistent Volume storage backend.

- - + -## RetentionConfig { #tempo-grafana-com-v1alpha1-RetentionConfig } +## MonolithicTracesStoragePersistentVolumeSpec { #tempo-grafana-com-v1alpha1-MonolithicTracesStoragePersistentVolumeSpec }

-(Appears on:RetentionSpec) +(Appears on:MonolithicTracesStorageSpec)

-

RetentionConfig defines how long data should be provided.

+

MonolithicTracesStoragePersistentVolumeSpec defines the Persistent Volume configuration.

@@ -2559,15 +2574,11 @@ See http://github.com/graf -traces
+size
-
- -Kubernetes meta/v1.Duration - - +k8s.io/apimachinery/pkg/api/resource.Quantity @@ -2575,11 +2586,7 @@ Kubernetes meta/v1.Duration -(Optional) - -

Traces defines retention period. Supported parameter suffixes are “s”, “m” and “h”. -example: 336h -default: value is 48h.

+

Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi.

@@ -2587,17 +2594,17 @@ default: value is 48h.

-## RetentionSpec { #tempo-grafana-com-v1alpha1-RetentionSpec } +## MonolithicTracesStorageSpec { #tempo-grafana-com-v1alpha1-MonolithicTracesStorageSpec }

-(Appears on:TempoStackSpec) +(Appears on:MonolithicStorageSpec)

-

RetentionSpec defines global and per tenant retention configurations.

+

MonolithicTracesStorageSpec defines the traces storage for the Tempo deployment.

@@ -2621,13 +2628,13 @@ default: value is 48h.

-perTenant
+backend
- + -map[string]github.com/grafana/tempo-operator/apis/tempo/v1alpha1.RetentionConfig +MonolithicTracesStorageBackend @@ -2637,9 +2644,7 @@ map[string]github.com/grafana/tempo-operator/apis/tempo/v1alpha1.RetentionConfig -(Optional) - -

PerTenant is used to configure retention per tenant.

+

Backend defines the backend for storing traces

@@ -2648,13 +2653,13 @@ map[string]github.com/grafana/tempo-operator/apis/tempo/v1alpha1.RetentionConfig -global
+wal
- + -RetentionConfig +MonolithicTracesStorageWALSpec @@ -2664,27 +2669,1435 @@ RetentionConfig -(Optional) - -

Global is used to configure global retention.

+

WAL defines the write-ahead logging (WAL) configuration

- - + + + + +pv
+ + + + + +MonolithicTracesStoragePersistentVolumeSpec + + + + + + + + + +

PV defines the Persistent Volume configuration

+ + + + + + + +## MonolithicTracesStorageWALSpec { #tempo-grafana-com-v1alpha1-MonolithicTracesStorageWALSpec } + +

+ +(Appears on:MonolithicTracesStorageSpec) + +

+ +
+ +

MonolithicTracesStorageWALSpec defines the write-ahead logging (WAL) configuration.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +size
+ + + +k8s.io/apimachinery/pkg/api/resource.Quantity + + + +
+ +

Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi.

+ +
+ +## OIDCSpec { #tempo-grafana-com-v1alpha1-OIDCSpec } + +

+ +(Appears on:AuthenticationSpec) + +

+ +
+ +

OIDCSpec defines the oidc configuration spec for Tempo Gateway component.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +secret
+ + + + + +TenantSecretSpec + + + + + +
+ +(Optional) + +

Secret defines the spec for the clientID, clientSecret and issuerCAPath for tenant’s authentication.

+ +
+ +issuerURL
+ + + +string + + + +
+ +(Optional) + +

IssuerURL defines the URL for issuer.

+ +
+ +redirectURL
+ + + +string + + + +
+ +(Optional) + +

RedirectURL defines the URL for redirect.

+ +
+ +groupClaim
+ + + +string + + + +
+ +(Optional) + +

Group claim field from ID Token

+ +
+ +usernameClaim
+ + + +string + + + +
+ +(Optional) + +

User claim field from ID Token

+ +
+ +## ObjectStorageSecretSpec { #tempo-grafana-com-v1alpha1-ObjectStorageSecretSpec } + +

+ +(Appears on:ObjectStorageSpec) + +

+ +
+ +

ObjectStorageSecretSpec is a secret reference containing name only, no namespace.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +type
+ + + + + +ObjectStorageSecretType + + + + + +
+ +

Type of object storage that should be used

+ +
+ +name
+ + + +string + + + +
+ +

Name of a secret in the namespace configured for object storage secrets.

+ +
+ +## ObjectStorageSecretType { #tempo-grafana-com-v1alpha1-ObjectStorageSecretType } + +(string alias) + +

+ +(Appears on:ObjectStorageSecretSpec) + +

+ +
+ +

ObjectStorageSecretType defines the type of storage which can be used with the Tempo cluster.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ValueDescription

"azure"

ObjectStorageSecretAzure when using Azure Storage for Tempo storage.

+

"gcs"

ObjectStorageSecretGCS when using Google Cloud Storage for Tempo storage.

+

"s3"

ObjectStorageSecretS3 when using S3 for Tempo storage.

+
+ +## ObjectStorageSpec { #tempo-grafana-com-v1alpha1-ObjectStorageSpec } + +

+ +(Appears on:TempoStackSpec) + +

+ +
+ +

ObjectStorageSpec defines the requirements to access the object +storage bucket to persist traces by the ingester component.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +tls
+ + + + + +ObjectStorageTLSSpec + + + + + +
+ +(Optional) + +

TLS configuration for reaching the object storage endpoint.

+ +
+ +secret
+ + + + + +ObjectStorageSecretSpec + + + + + +
+ +

Secret for object storage authentication. +Name of a secret in the same namespace as the TempoStack custom resource.

+ +
+ +## ObjectStorageTLSSpec { #tempo-grafana-com-v1alpha1-ObjectStorageTLSSpec } + +

+ +(Appears on:ObjectStorageSpec) + +

+ +
+ +

ObjectStorageTLSSpec is the TLS configuration for reaching the object storage endpoint.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +caName
+ + + +string + + + +
+ +(Optional) + +

CA is the name of a ConfigMap containing a ca.crt key with a CA certificate. +It needs to be in the same namespace as the TempoStack custom resource.

+ +
+ +## ObservabilitySpec { #tempo-grafana-com-v1alpha1-ObservabilitySpec } + +

+ +(Appears on:TempoStackSpec) + +

+ +
+ +

ObservabilitySpec defines how telemetry data gets handled.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +metrics
+ + + + + +MetricsConfigSpec + + + + + +
+ +(Optional) + +

Metrics defines the metrics configuration for operands.

+ +
+ +tracing
+ + + + + +TracingConfigSpec + + + + + +
+ +(Optional) + +

Tracing defines a config for operands.

+ +
+ +grafana
+ + + + + +GrafanaConfigSpec + + + + + +
+ +(Optional) + +

Grafana defines the Grafana configuration for operands.

+ +
+ +## PermissionType { #tempo-grafana-com-v1alpha1-PermissionType } + +(string alias) + +

+ +(Appears on:RoleSpec) + +

+ +
+ +

PermissionType is a Tempo Gateway RBAC permission.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
ValueDescription

"read"

Read gives access to read data from a tenant.

+

"write"

Write gives access to write data to a tenant.

+
+ +## PodStatusMap { #tempo-grafana-com-v1alpha1-PodStatusMap } + +(map[k8s.io/api/core/v1.PodPhase][]string alias) + +

+ +(Appears on:ComponentStatus) + +

+ +
+ +

PodStatusMap defines the type for mapping pod status to pod name.

+ +
+ +## QueryLimit { #tempo-grafana-com-v1alpha1-QueryLimit } + +

+ +(Appears on:RateLimitSpec) + +

+ +
+ +

QueryLimit defines query limits.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +maxBytesPerTagValues
+ + + +int + + + +
+ +(Optional) + +

MaxBytesPerTagValues defines the maximum size in bytes of a tag-values query.

+ +
+ +maxSearchBytesPerTrace
+ + + +int + + + +
+ +(Optional) + +

DEPRECATED. MaxSearchBytesPerTrace defines the maximum size of search data for a single +trace in bytes. +default: 0 to disable.

+ +
+ +maxSearchDuration
+ + + + + +Kubernetes meta/v1.Duration + + + + + +
+ +(Optional) + +

MaxSearchDuration defines the maximum allowed time range for a search. +If this value is not set, then spec.search.maxDuration is used.

+ +
+ +## RateLimitSpec { #tempo-grafana-com-v1alpha1-RateLimitSpec } + +

+ +(Appears on:LimitSpec) + +

+ +
+ +

RateLimitSpec defines rate limits for Ingestion and Query components.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +ingestion
+ + + + + +IngestionLimitSpec + + + + + +
+ +(Optional) + +

Ingestion is used to define ingestion rate limits.

+ +
+ +query
+ + + + + +QueryLimit + + + + + +
+ +(Optional) + +

Query is used to define query rate limits.

+ +
+ +## ReceiversTLSSpec { #tempo-grafana-com-v1alpha1-ReceiversTLSSpec } + +

+ +(Appears on:TempoDistributorSpec) + +

+ +
+ +

ReceiversTLSSpec is the TLS configuration for the receivers.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +enabled
+ + + +bool + + + +
+ +
+ +caName
+ + + +string + + + +
+ +

caName is the name of a ConfigMap containing a CA certificate. +It needs to be in the same namespace as the Tempo custom resource.

+ +
+ +certName
+ + + +string + + + +
+ +

certName is the name of a Secret containing a certificate and the private key +It needs to be in the same namespace as the Tempo custom resource.

+ +
+ +minVersion
+ + + +string + + + +
+ +(Optional) + +

minVersion is the name of a Secret containing a certificate and the private key +It needs to be in the same namespace as the Tempo custom resource.

+ +
+ +## Resources { #tempo-grafana-com-v1alpha1-Resources } + +

+ +(Appears on:TempoStackSpec) + +

+ +
+ +

Resources defines resources configuration.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +total
+ + + + + +Kubernetes core/v1.ResourceRequirements + + + + + +
+ +(Optional) + +

The total amount of resources for Tempo instance. +The operator autonomously splits resources between deployed Tempo components. +Only limits are supported, the operator calculates requests automatically. +See http://github.com/grafana/tempo/issues/1540.

+ +
+ +## RetentionConfig { #tempo-grafana-com-v1alpha1-RetentionConfig } + +

+ +(Appears on:RetentionSpec) + +

+ +
+ +

RetentionConfig defines how long data should be provided.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +traces
+ + + + + +Kubernetes meta/v1.Duration + + + + + +
+ +(Optional) + +

Traces defines retention period. Supported parameter suffixes are “s”, “m” and “h”. +example: 336h +default: value is 48h.

+ +
+ +## RetentionSpec { #tempo-grafana-com-v1alpha1-RetentionSpec } + +

+ +(Appears on:TempoStackSpec) + +

+ +
+ +

RetentionSpec defines global and per tenant retention configurations.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +perTenant
+ + + + + +map[string]github.com/grafana/tempo-operator/apis/tempo/v1alpha1.RetentionConfig + + + + + +
+ +(Optional) + +

PerTenant is used to configure retention per tenant.

+ +
+ +global
+ + + + + +RetentionConfig + + + + + +
+ +(Optional) + +

Global is used to configure global retention.

+ +
+ +## RoleBindingsSpec { #tempo-grafana-com-v1alpha1-RoleBindingsSpec } + +

+ +(Appears on:AuthorizationSpec) + +

+ +
+ +

RoleBindingsSpec binds a set of roles to a set of subjects.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +name
+ + + +string + + + +
+ +
+ +subjects
+ + + + + +[]Subject + + + + + +
+ +
+ +roles
+ + + +[]string + + + +
+ +
+ +## RoleSpec { #tempo-grafana-com-v1alpha1-RoleSpec } + +

+ +(Appears on:AuthorizationSpec) + +

+ +
+ +

RoleSpec describes a set of permissions to interact with a tenant.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +name
+ + + +string + + + +
+ +
+ +resources
+ + + +[]string + + + +
+ +
+ +tenants
+ + + +[]string + + -## RoleBindingsSpec { #tempo-grafana-com-v1alpha1-RoleBindingsSpec } +
+ +
+ +permissions
+ + + + + +[]PermissionType + + + + + +
+ +
+ +## RouteSpec { #tempo-grafana-com-v1alpha1-RouteSpec }

-(Appears on:AuthorizationSpec) +(Appears on:IngressSpec)

-

RoleBindingsSpec binds a set of roles to a set of subjects.

+

RouteSpec defines OpenShift Route specific options.

@@ -2708,11 +4121,71 @@ RetentionConfig -name
+termination
-string + + +TLSRouteTerminationType + + + + + + + + + +(Optional) + +

Termination specifies the termination type. By default “edge” is used.

+ + + + + + + +## SearchSpec { #tempo-grafana-com-v1alpha1-SearchSpec } + +

+ +(Appears on:TempoStackSpec) + +

+ +
+ +

SearchSpec specified the global search parameters.

+ +
+ + + + + + + + + + + + + + + + + + + + @@ -2727,13 +4204,13 @@ string @@ -2750,11 +4231,11 @@ string
FieldDescription
+ +defaultResultLimit
+ + + +int @@ -2720,6 +4193,10 @@ string
+(Optional) + +

Limit used for search requests if none is set by the caller (default: 20)

+
-subjects
+maxDuration
- + -[]Subject +Kubernetes meta/v1.Duration @@ -2743,6 +4220,10 @@ string
+(Optional) + +

The maximum allowed time range for a search, default: 0s which means unlimited.

+
-roles
+maxResultLimit
-[]string +int @@ -2762,23 +4243,29 @@ string
+(Optional) + +

The maximum allowed value of the limit parameter on search requests. If the search request limit parameter +exceeds the value configured here it will be set to the value configured here. +The default value of 0 disables this limit.

+
-## RoleSpec { #tempo-grafana-com-v1alpha1-RoleSpec } +## Subject { #tempo-grafana-com-v1alpha1-Subject }

-(Appears on:AuthorizationSpec) +(Appears on:RoleBindingsSpec)

-

RoleSpec describes a set of permissions to interact with a tenant.

+

Subject represents a subject that has been bound to a role.

@@ -2821,11 +4308,15 @@ string -resources
+kind
-[]string + + +SubjectKind + + @@ -2836,62 +4327,65 @@ string - - - + + -tenants
+## SubjectKind { #tempo-grafana-com-v1alpha1-SubjectKind } - +(string alias) -[]string +

- +(Appears on:Subject) - +

- +
- - +

SubjectKind is a kind of Tempo Gateway RBAC subject.

+ +
- + - -permissions
+ - + - + -[]PermissionType + - + - + + - + - - +
+
ValueDescription

"group"

Group represents a subject that is a group.

+

"user"

User represents a subject that is a user.

-## RouteSpec { #tempo-grafana-com-v1alpha1-RouteSpec } +## TLSRouteTerminationType { #tempo-grafana-com-v1alpha1-TLSRouteTerminationType } + +(string alias)

-(Appears on:IngressSpec) +(Appears on:RouteSpec)

-

RouteSpec defines OpenShift Route specific options.

+

TLSRouteTerminationType is used to indicate which TLS settings should be used.

@@ -2901,7 +4395,7 @@ string -Field +Value Description @@ -2909,49 +4403,51 @@ string - - - - - +

"edge"

-termination
+

TLSRouteTerminationTypeEdge indicates that encryption should be terminated +at the edge router.

+ - +

"insecure"

- +

TLSRouteTerminationTypeInsecure indicates that insecure connections are allowed.

+ -TLSRouteTerminationType +

"passthrough"

-
+

TLSRouteTerminationTypePassthrough indicates that the destination service is +responsible for decrypting traffic.

+ -
+

"reencrypt"

+

TLSRouteTerminationTypeReencrypt indicates that traffic will be decrypted on the edge +and re-encrypt using a new certificate.

- +

"passthrough"

-(Optional) + -

Termination specifies the termination type. By default “edge” is used.

+

"edge"

- - + - + -## SearchSpec { #tempo-grafana-com-v1alpha1-SearchSpec } +## TempoComponentSpec { #tempo-grafana-com-v1alpha1-TempoComponentSpec }

-(Appears on:TempoStackSpec) +(Appears on:TempoDistributorSpec, TempoGatewaySpec, TempoQueryFrontendSpec, TempoTemplateSpec)

-

SearchSpec specified the global search parameters.

+

TempoComponentSpec defines specific schedule settings for tempo components.

@@ -2975,11 +4471,11 @@ TLSRouteTerminationType -defaultResultLimit
+replicas
-int +int32 @@ -2989,7 +4485,7 @@ int (Optional) -

Limit used for search requests if none is set by the caller (default: 20)

+

Replicas represents the number of replicas to create for this component.

@@ -2998,15 +4494,11 @@ int -maxDuration
+nodeSelector
- - -Kubernetes meta/v1.Duration - - +map[string]string @@ -3016,7 +4508,7 @@ Kubernetes meta/v1.Duration (Optional) -

The maximum allowed time range for a search, default: 0s which means unlimited.

+

NodeSelector is the simplest recommended form of node selection constraint.

@@ -3025,11 +4517,15 @@ Kubernetes meta/v1.Duration -maxResultLimit
+tolerations
-int + + +[]Kubernetes core/v1.Toleration + + @@ -3039,9 +4535,7 @@ int (Optional) -

The maximum allowed value of the limit parameter on search requests. If the search request limit parameter -exceeds the value configured here it will be set to the value configured here. -The default value of 0 disables this limit.

+

Tolerations defines component specific pod tolerations.

@@ -3049,17 +4543,18 @@ The default value of 0 disables this limit.

-## Subject { #tempo-grafana-com-v1alpha1-Subject } +## TempoDistributorSpec { #tempo-grafana-com-v1alpha1-TempoDistributorSpec }

-(Appears on:RoleBindingsSpec) +(Appears on:TempoTemplateSpec)

-

Subject represents a subject that has been bound to a role.

+

TempoDistributorSpec defines the template of all requirements to configure +scheduling of Tempo distributor component to be deployed.

@@ -3083,11 +4578,15 @@ The default value of 0 disables this limit.

-name
+component
-string + + +TempoComponentSpec + + @@ -3095,6 +4594,13 @@ string +(Optional) + +

TempoComponentSpec is embedded to extend this definition with further options.

+ +

Currently, there is no way to inline this field. +See: https://github.com/golang/go/issues/6213

+ @@ -3102,13 +4608,13 @@ string -kind
+tls
- + -SubjectKind +ReceiversTLSSpec @@ -3118,25 +4624,27 @@ SubjectKind +(Optional) + +

TLS defines TLS configuration for distributor receivers

+ -## SubjectKind { #tempo-grafana-com-v1alpha1-SubjectKind } - -(string alias) +## TempoGatewaySpec { #tempo-grafana-com-v1alpha1-TempoGatewaySpec }

-(Appears on:Subject) +(Appears on:TempoTemplateSpec)

-

SubjectKind is a kind of Tempo Gateway RBAC subject.

+

TempoGatewaySpec extends TempoComponentSpec with gateway parameters.

@@ -3146,7 +4654,7 @@ SubjectKind -Value +Field Description @@ -3154,94 +4662,92 @@ SubjectKind -

"group"

+ -

Group represents a subject that is a group.

- + -

"user"

+ -

User represents a subject that is a user.

- +component
- - + -## TLSRouteTerminationType { #tempo-grafana-com-v1alpha1-TLSRouteTerminationType } + -(string alias) +TempoComponentSpec -

+ -(Appears on:RouteSpec) + -

+ -
+ -

TLSRouteTerminationType is used to indicate which TLS settings should be used.

- -
+(Optional) - +

TempoComponentSpec is embedded to extend this definition with further options.

+ +

Currently there is no way to inline this field. +See: https://github.com/golang/go/issues/6213

- + + - + +enabled
- + - +bool - + - - + + - + - + +ingress
- + - + - +IngressSpec - + - + - -
Value -Description

"edge"

TLSRouteTerminationTypeEdge indicates that encryption should be terminated -at the edge router.

"insecure"

-

TLSRouteTerminationTypeInsecure indicates that insecure connections are allowed.

"passthrough"

TLSRouteTerminationTypePassthrough indicates that the destination service is -responsible for decrypting traffic.

-
-

"reencrypt"

TLSRouteTerminationTypeReencrypt indicates that traffic will be decrypted on the edge -and re-encrypt using a new certificate.

-

"passthrough"

"edge"

+ -## TempoComponentSpec { #tempo-grafana-com-v1alpha1-TempoComponentSpec } + -

+(Optional) -(Appears on:TempoDistributorSpec, TempoGatewaySpec, TempoQueryFrontendSpec, TempoTemplateSpec) +

Ingress defines gateway Ingress options.

-

+ + + + + + +## TempoMonolithic { #tempo-grafana-com-v1alpha1-TempoMonolithic }
-

TempoComponentSpec defines specific schedule settings for tempo components.

+

TempoMonolithic is the Schema for the tempomonolithics API.

@@ -3265,11 +4771,15 @@ and re-encrypt using a new certificate.

-replicas
+metadata
-int32 + + +Kubernetes meta/v1.ObjectMeta + + @@ -3277,9 +4787,9 @@ int32 -(Optional) +Refer to the Kubernetes API documentation for the fields of the -

Replicas represents the number of replicas to create for this component.

+metadata field. @@ -3288,11 +4798,15 @@ int32 -nodeSelector
+spec
-map[string]string + + +TempoMonolithicSpec + + @@ -3300,10 +4814,6 @@ map[string]string -(Optional) - -

NodeSelector is the simplest recommended form of node selection constraint.

- @@ -3311,13 +4821,13 @@ map[string]string -tolerations
+status
- + -[]Kubernetes core/v1.Toleration +TempoMonolithicStatus @@ -3327,28 +4837,23 @@ map[string]string -(Optional) - -

Tolerations defines component specific pod tolerations.

- -## TempoDistributorSpec { #tempo-grafana-com-v1alpha1-TempoDistributorSpec } +## TempoMonolithicSpec { #tempo-grafana-com-v1alpha1-TempoMonolithicSpec }

-(Appears on:TempoTemplateSpec) +(Appears on:TempoMonolithic)

-

TempoDistributorSpec defines the template of all requirements to configure -scheduling of Tempo distributor component to be deployed.

+

TempoMonolithicSpec defines the desired state of TempoMonolithic.

@@ -3372,13 +4877,13 @@ scheduling of Tempo distributor component to be deployed.

-component
+storage
- + -TempoComponentSpec +MonolithicStorageSpec @@ -3388,12 +4893,7 @@ TempoComponentSpec -(Optional) - -

TempoComponentSpec is embedded to extend this definition with further options.

- -

Currently, there is no way to inline this field. -See: https://github.com/golang/go/issues/6213

+

Storage defines the backend storage configuration

@@ -3402,13 +4902,13 @@ See: https://github.com/golan -tls
+ingestion
-
+ -ReceiversTLSSpec +MonolithicIngestionSpec @@ -3418,57 +4918,47 @@ ReceiversTLSSpec -(Optional) - -

TLS defines TLS configuration for distributor receivers

+

Ingestion defines the trace ingestion configuration

- - - -## TempoGatewaySpec { #tempo-grafana-com-v1alpha1-TempoGatewaySpec } + -

+ -(Appears on:TempoTemplateSpec) +jaegerui
-

+ - +MonolithicJaegerUISpec - + - + - + - + +

JaegerUI defines the Jaeger UI configuration

+ - - - - @@ -3492,11 +4977,15 @@ See: https://github.com/golan @@ -3511,13 +5002,13 @@ bool @@ -3537,6 +5026,20 @@ IngressSpec
Field -Description
-component
+management
- + -TempoComponentSpec +ManagementStateType @@ -3478,12 +4968,7 @@ TempoComponentSpec
-(Optional) - -

TempoComponentSpec is embedded to extend this definition with further options.

- -

Currently there is no way to inline this field. -See: https://github.com/golang/go/issues/6213

+

ManagementState defines whether this instance is managed by the operator or self-managed

-enabled
+observability
-bool + + +MonolithicObservabilitySpec + + @@ -3504,6 +4993,8 @@ bool
+

Observability defines observability configuration for the Tempo deployment

+
-ingress
+extraConfig
- + -IngressSpec +MonolithicExtraConfigSpec @@ -3527,9 +5018,7 @@ IngressSpec
-(Optional) - -

Ingress defines gateway Ingress options.

+

ExtraConfig defines any extra (overlay) configuration for components

+## TempoMonolithicStatus { #tempo-grafana-com-v1alpha1-TempoMonolithicStatus } + +

+ +(Appears on:TempoMonolithic) + +

+ +
+ +

TempoMonolithicStatus defines the observed state of TempoMonolithic.

+ +
+ ## TempoQueryFrontendSpec { #tempo-grafana-com-v1alpha1-TempoQueryFrontendSpec }

diff --git a/docs/spec/tempomonolithic.yaml b/docs/spec/tempomonolithic.yaml new file mode 100644 index 000000000..6a53a8c77 --- /dev/null +++ b/docs/spec/tempomonolithic.yaml @@ -0,0 +1,39 @@ +apiVersion: tempo.grafana.com/v1alpha1 # APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources +kind: TempoMonolithic # Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds +metadata: + name: example +spec: # TempoMonolithicSpec defines the desired state of TempoMonolithic. + extraConfig: # ExtraConfig defines any extra (overlay) configuration for components + tempo: {} # Tempo defines any extra Tempo configuration, which will be merged with the operator's generated Tempo configuration + ingestion: # Ingestion defines the trace ingestion configuration + otlp: # OTLP defines the ingestion configuration for OTLP + grpc: # GRPC defines the OTLP/gRPC configuration + enabled: false # Enabled defines if the OTLP over gRPC is enabled + http: # HTTP defines the OTLP/HTTP configuration + enabled: false # Enabled defines if the OTLP over HTTP is enabled + tls: # TLS defines the TLS configuration for ingestion + ca: "" # CA defines the name of a secret containing the CA certificate + cert: "" # Cert defines the name of a secret containing the TLS certificate and private key + enabled: false # Enabled defines if TLS is enabled for ingestion + jaegerui: # JaegerUI defines the Jaeger UI configuration + enabled: false # Enabled defines if the Jaeger UI should be enabled + ingress: # Ingress defines the ingress configuration for Jaeger UI + enabled: false # Enabled defines if an Ingress object should be created for Jaeger UI + route: # Route defines the route configuration for Jaeger UI + enabled: false # Enabled defines if a Route object should be created for Jaeger UI + management: "" # ManagementState defines whether this instance is managed by the operator or self-managed + observability: # Observability defines observability configuration for the Tempo deployment + metrics: # Metrics defines the metrics configuration of the Tempo deployment + prometheusRules: # ServiceMonitors defines the PrometheusRule configuration + enabled: false # Enabled defines if the operator should create PrometheusRules for this Tempo deployment + serviceMonitors: # ServiceMonitors defines the ServiceMonitor configuration + enabled: false # Enabled defines if the operator should create ServiceMonitors for this Tempo deployment + storage: # Storage defines the backend storage configuration + traces: # Traces defines the backend storage configuration for traces + backend: "" # Backend defines the backend for storing traces + pv: # PV defines the Persistent Volume configuration + size: 1Gi # Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. + secret: "" # Secret defines name of a secret containing the credentials for accessing the specified backend storage + wal: # WAL defines the write-ahead logging (WAL) configuration + size: 1Gi # Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. +status: # TempoMonolithicStatus defines the observed state of TempoMonolithic. diff --git a/internal/manifests/manifestutils/constants.go b/internal/manifests/manifestutils/constants.go index 90c7e5d80..0231d694d 100644 --- a/internal/manifests/manifestutils/constants.go +++ b/internal/manifests/manifestutils/constants.go @@ -37,6 +37,21 @@ const ( // PortGRPCServer declares the port number of the tempo gRPC port. PortGRPCServer = 9095 + // JaegerUIPortName declares the name of the Jaeger UI HTTP port. + JaegerUIPortName = "jaeger-ui" + // PortJaegerUI declares the port number of the Jaeger UI HTTP port. + PortJaegerUI = 16686 + + // JaegerGRPCQuery declares the name of the Jaeger UI gPRC port. + JaegerGRPCQuery = "jaeger-gprc" + // PortJaegerGRPCQuery declares the port number of the Jaeger UI gPRC port. + PortJaegerGRPCQuery = 16685 + + // JaegerMetricsPortName declares the name of the Jaeger UI metrics port. + JaegerMetricsPortName = "jaeger-metrics" + // PortJaegerMetrics declares the port number of the Jaeger UI metrics port. + PortJaegerMetrics = 16687 + // OtlpGrpcPortName declares the name of the OpenTelemetry Collector gRPC receiver port. OtlpGrpcPortName = "otlp-grpc" // PortOtlpGrpcServer declares the port number of the OpenTelemetry Collector gRPC receiver port. diff --git a/internal/manifests/monolithic/build.go b/internal/manifests/monolithic/build.go new file mode 100644 index 000000000..b65037d32 --- /dev/null +++ b/internal/manifests/monolithic/build.go @@ -0,0 +1,28 @@ +package monolithic + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// BuildAll generates all manifests. +func BuildAll(opts Options) ([]client.Object, error) { + var manifests []client.Object + + configMap, configChecksum, err := BuildConfigMap(opts) + if err != nil { + return nil, err + } + manifests = append(manifests, configMap) + opts.ConfigChecksum = configChecksum + + statefulSet, err := BuildTempoStatefulset(opts) + if err != nil { + return nil, err + } + manifests = append(manifests, statefulSet) + + services := BuildServices(opts) + manifests = append(manifests, services...) + + return manifests, nil +} diff --git a/internal/manifests/monolithic/config.go b/internal/manifests/monolithic/config.go new file mode 100644 index 000000000..53158903d --- /dev/null +++ b/internal/manifests/monolithic/config.go @@ -0,0 +1,162 @@ +package monolithic + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + + "github.com/imdario/mergo" + "gopkg.in/yaml.v2" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + "github.com/grafana/tempo-operator/internal/manifests/manifestutils" + "github.com/grafana/tempo-operator/internal/manifests/naming" +) + +type tempoConfig struct { + Server struct { + HttpListenPort int `yaml:"http_listen_port"` + } `yaml:"server"` + + Storage struct { + Trace struct { + Backend string `yaml:"backend"` + WAL struct { + Path string `yaml:"path"` + } `yaml:"wal"` + Local struct { + Path string `yaml:"path"` + } `yaml:"local"` + } `yaml:"trace"` + } `yaml:"storage"` + + Distributor struct { + Receivers struct { + OTLP struct { + Protocols struct { + GRPC *struct{} `yaml:"grpc,omitempty"` + HTTP *struct{} `yaml:"http,omitempty"` + } `yaml:"protocols"` + } `yaml:"otlp"` + } `yaml:"receivers"` + } `yaml:"distributor"` + + UsageReport struct { + ReportingEnabled bool `yaml:"reporting_enabled"` + } `yaml:"usage_report"` +} + +type tempoQueryConfig struct { + Backend string `yaml:"backend"` + TenantHeaderKey string `yaml:"tenant_header_key"` +} + +// BuildConfigMap creates the Tempo ConfigMap for a monolithic deployment. +func BuildConfigMap(opts Options) (*corev1.ConfigMap, string, error) { + tempo := opts.Tempo + labels := ComponentLabels("config", tempo.Name) + + tempoConfig, err := buildTempoConfig(opts) + if err != nil { + return nil, "", err + } + + configMap := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Name("", tempo.Name), + Namespace: tempo.Namespace, + Labels: labels, + }, + Data: map[string]string{ + "tempo.yaml": string(tempoConfig), + }, + } + + h := sha256.Sum256(tempoConfig) + checksum := fmt.Sprintf("%x", h) + + if tempo.Spec.JaegerUI != nil && tempo.Spec.JaegerUI.Enabled { + tempoQueryConfig, err := buildTempoQueryConfig() + if err != nil { + return nil, "", err + } + configMap.Data["tempo-query.yaml"] = string(tempoQueryConfig) + } + + return configMap, checksum, nil +} + +func buildTempoConfig(opts Options) ([]byte, error) { + tempo := opts.Tempo + + config := tempoConfig{} + config.Server.HttpListenPort = manifestutils.PortHTTPServer + + config.Storage.Trace.WAL.Path = "/var/tempo/wal" + switch tempo.Spec.Storage.Traces.Backend { + case v1alpha1.MonolithicTracesStorageBackendMemory, + v1alpha1.MonolithicTracesStorageBackendPersistentVolume: + config.Storage.Trace.Backend = "local" + config.Storage.Trace.Local.Path = "/var/tempo/blocks" + + default: + return nil, fmt.Errorf("invalid storage backend: '%s'", tempo.Spec.Storage.Traces.Backend) + } + + if tempo.Spec.Ingestion != nil && tempo.Spec.Ingestion.OTLP != nil { + if tempo.Spec.Ingestion.OTLP.GRPC != nil && tempo.Spec.Ingestion.OTLP.GRPC.Enabled { + config.Distributor.Receivers.OTLP.Protocols.GRPC = &struct{}{} + } + if tempo.Spec.Ingestion.OTLP.HTTP != nil && tempo.Spec.Ingestion.OTLP.HTTP.Enabled { + config.Distributor.Receivers.OTLP.Protocols.HTTP = &struct{}{} + } + } + + if tempo.Spec.ExtraConfig == nil || len(tempo.Spec.ExtraConfig.Tempo.Raw) == 0 { + return yaml.Marshal(config) + } else { + return overlayJson(config, tempo.Spec.ExtraConfig.Tempo.Raw) + } +} + +func overlayJson(config tempoConfig, overlay []byte) ([]byte, error) { + // mergo.Merge requires that both variables have the same type + generatedCfg := make(map[string]interface{}) + overlayCfg := make(map[string]interface{}) + + // Convert tempoConfig{} to map[string]interface{} + generatedYaml, err := yaml.Marshal(config) + if err != nil { + return nil, err + } + if err := yaml.Unmarshal(generatedYaml, &generatedCfg); err != nil { + return nil, err + } + + // Unmarshal overlay of type []byte to map[string]interface{} + if err := json.Unmarshal(overlay, &overlayCfg); err != nil { + return nil, err + } + + // Override generated config with extra config + err = mergo.Merge(&generatedCfg, overlayCfg, mergo.WithOverride) + if err != nil { + return nil, err + } + + return yaml.Marshal(generatedCfg) +} + +func buildTempoQueryConfig() ([]byte, error) { + config := tempoQueryConfig{} + config.Backend = fmt.Sprintf("127.0.0.1:%d", manifestutils.PortHTTPServer) + config.TenantHeaderKey = manifestutils.TenantHeader + + return yaml.Marshal(&config) +} diff --git a/internal/manifests/monolithic/labels.go b/internal/manifests/monolithic/labels.go new file mode 100644 index 000000000..cd191fa88 --- /dev/null +++ b/internal/manifests/monolithic/labels.go @@ -0,0 +1,19 @@ +package monolithic + +import "k8s.io/apimachinery/pkg/labels" + +// ComponentLabels is a list of all commonLabels including the app.kubernetes.io/component: label. +func ComponentLabels(component, instanceName string) labels.Set { + return labels.Merge(CommonLabels(instanceName), map[string]string{ + "app.kubernetes.io/component": component, + }) +} + +// CommonLabels returns common labels for each TempoMonolithic object created by the operator. +func CommonLabels(instanceName string) map[string]string { + return map[string]string{ + "app.kubernetes.io/name": "tempo-monolithic", + "app.kubernetes.io/instance": instanceName, + "app.kubernetes.io/managed-by": "tempo-operator", + } +} diff --git a/internal/manifests/monolithic/options.go b/internal/manifests/monolithic/options.go new file mode 100644 index 000000000..add1e6441 --- /dev/null +++ b/internal/manifests/monolithic/options.go @@ -0,0 +1,13 @@ +package monolithic + +import ( + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" +) + +// Options defines calculated options required to generate all manifests. +type Options struct { + CtrlConfig configv1alpha1.ProjectConfig + Tempo v1alpha1.TempoMonolithic + ConfigChecksum string +} diff --git a/internal/manifests/monolithic/service.go b/internal/manifests/monolithic/service.go new file mode 100644 index 000000000..52bb6bdc7 --- /dev/null +++ b/internal/manifests/monolithic/service.go @@ -0,0 +1,126 @@ +package monolithic + +import ( + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/grafana/tempo-operator/internal/manifests/manifestutils" + "github.com/grafana/tempo-operator/internal/manifests/naming" +) + +// BuildServices creates all services for a monolithic deployment. +func BuildServices(opts Options) []client.Object { + services := []client.Object{ + buildTempoApiService(opts), + buildTempoIngestService(opts), + } + + if opts.Tempo.Spec.JaegerUI != nil && opts.Tempo.Spec.JaegerUI.Enabled { + services = append(services, buildJaegerUIService(opts)) + } + + return services +} + +func buildTempoApiService(opts Options) *corev1.Service { + labels := ComponentLabels("tempo", opts.Tempo.Name) + return &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Name("api", opts.Tempo.Name), + Namespace: opts.Tempo.Namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: manifestutils.HttpPortName, + Protocol: corev1.ProtocolTCP, + Port: manifestutils.PortHTTPServer, + TargetPort: intstr.FromString(manifestutils.HttpPortName), + }, + }, + Selector: labels, + }, + } +} + +func buildTempoIngestService(opts Options) *corev1.Service { + tempo := opts.Tempo + labels := ComponentLabels("tempo", tempo.Name) + + ports := []corev1.ServicePort{} + // TODO: point to gateway + if tempo.Spec.Ingestion != nil && tempo.Spec.Ingestion.OTLP != nil { + if tempo.Spec.Ingestion.OTLP.GRPC != nil && tempo.Spec.Ingestion.OTLP.GRPC.Enabled { + ports = append(ports, corev1.ServicePort{ + Name: manifestutils.OtlpGrpcPortName, + Protocol: corev1.ProtocolTCP, + Port: manifestutils.PortOtlpGrpcServer, + TargetPort: intstr.FromString(manifestutils.OtlpGrpcPortName), + }) + } + if tempo.Spec.Ingestion.OTLP.HTTP != nil && tempo.Spec.Ingestion.OTLP.HTTP.Enabled { + ports = append(ports, corev1.ServicePort{ + Name: manifestutils.PortOtlpHttpName, + Protocol: corev1.ProtocolTCP, + Port: manifestutils.PortOtlpHttp, + TargetPort: intstr.FromString(manifestutils.PortOtlpHttpName), + }) + } + } + + return &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Name("ingest", opts.Tempo.Name), + Namespace: opts.Tempo.Namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: ports, + Selector: labels, + }, + } +} + +func buildJaegerUIService(opts Options) *corev1.Service { + labels := ComponentLabels("tempo", opts.Tempo.Name) + return &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Name("jaegerui", opts.Tempo.Name), + Namespace: opts.Tempo.Namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: manifestutils.JaegerGRPCQuery, + Port: manifestutils.PortJaegerGRPCQuery, + TargetPort: intstr.FromString(manifestutils.JaegerGRPCQuery), + }, + { + Name: manifestutils.JaegerUIPortName, + Port: manifestutils.PortJaegerUI, + TargetPort: intstr.FromString(manifestutils.JaegerUIPortName), + }, + { + Name: manifestutils.JaegerMetricsPortName, + Port: manifestutils.PortJaegerMetrics, + TargetPort: intstr.FromString(manifestutils.JaegerMetricsPortName), + }, + }, + Selector: labels, + }, + } +} diff --git a/internal/manifests/monolithic/statefulset.go b/internal/manifests/monolithic/statefulset.go new file mode 100644 index 000000000..760265748 --- /dev/null +++ b/internal/manifests/monolithic/statefulset.go @@ -0,0 +1,252 @@ +package monolithic + +import ( + "errors" + "fmt" + + "github.com/operator-framework/operator-lib/proxy" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + "github.com/grafana/tempo-operator/internal/manifests/manifestutils" + "github.com/grafana/tempo-operator/internal/manifests/naming" +) + +const ( + walVolumeName = "tempo-wal" + blocksVolumeName = "tempo-blocks" +) + +// BuildTempoStatefulset creates the Tempo statefulset for a monolithic deployment. +func BuildTempoStatefulset(opts Options) (*v1.StatefulSet, error) { + tempo := opts.Tempo + labels := ComponentLabels("tempo", tempo.Name) + annotations := manifestutils.CommonAnnotations(opts.ConfigChecksum) + + ss := &v1.StatefulSet{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Name("", tempo.Name), + Namespace: tempo.Namespace, + Labels: labels, + }, + Spec: v1.StatefulSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: labels, + }, + + // Changes to a StatefulSet are not propagated to pods in a broken state (e.g. CrashLoopBackOff) + // See https://github.com/kubernetes/kubernetes/issues/67250 + // + // This is a workaround for the above issue. + // This setting is also in the tempo-distributed helm chart: https://github.com/grafana/helm-charts/blob/0fdf2e1900733eb104ac734f5fb0a89dc950d2c2/charts/tempo-distributed/templates/ingester/statefulset-ingester.yaml#L21 + PodManagementPolicy: v1.ParallelPodManagement, + + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: annotations, + }, + Spec: corev1.PodSpec{ + Affinity: manifestutils.DefaultAffinity(labels), + Containers: []corev1.Container{ + { + Name: "tempo", + Image: opts.CtrlConfig.DefaultImages.Tempo, + Env: proxy.ReadProxyVarsFromEnv(), + Args: []string{ + "-config.file=/conf/tempo.yaml", + "-mem-ballast-size-mbs=1024", + "-log.level=info", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: manifestutils.ConfigVolumeName, + MountPath: "/conf", + ReadOnly: true, + }, + { + Name: walVolumeName, + MountPath: "/var/tempo/wal", + }, + }, + Ports: buildTempoPorts(opts), + ReadinessProbe: manifestutils.TempoReadinessProbe(false), + SecurityContext: manifestutils.TempoContainerSecurityContext(), + }, + }, + Volumes: []corev1.Volume{ + { + Name: manifestutils.ConfigVolumeName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: naming.Name("", tempo.Name), + }, + }, + }, + }, + }, + }, + }, + }, + } + + err := configureStorage(opts, ss) + if err != nil { + return nil, err + } + + if tempo.Spec.JaegerUI != nil && tempo.Spec.JaegerUI.Enabled { + configureJaegerUI(opts, ss) + } + + return ss, nil +} + +func buildTempoPorts(opts Options) []corev1.ContainerPort { + tempo := opts.Tempo + ports := []corev1.ContainerPort{ + { + Name: manifestutils.HttpPortName, + ContainerPort: manifestutils.PortHTTPServer, + Protocol: corev1.ProtocolTCP, + }, + } + + if tempo.Spec.Ingestion != nil && tempo.Spec.Ingestion.OTLP != nil { + if tempo.Spec.Ingestion.OTLP.GRPC != nil && tempo.Spec.Ingestion.OTLP.GRPC.Enabled { + ports = append(ports, corev1.ContainerPort{ + Name: manifestutils.OtlpGrpcPortName, + ContainerPort: manifestutils.PortOtlpGrpcServer, + Protocol: corev1.ProtocolTCP, + }) + } + if tempo.Spec.Ingestion.OTLP.HTTP != nil && tempo.Spec.Ingestion.OTLP.HTTP.Enabled { + ports = append(ports, corev1.ContainerPort{ + Name: manifestutils.PortOtlpHttpName, + ContainerPort: manifestutils.PortOtlpHttp, + Protocol: corev1.ProtocolTCP, + }) + } + } + + return ports +} + +func configureStorage(opts Options, sts *v1.StatefulSet) error { + tempo := opts.Tempo + switch tempo.Spec.Storage.Traces.Backend { + case v1alpha1.MonolithicTracesStorageBackendMemory: + sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, corev1.Volume{ + Name: walVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumMemory, + }, + }, + }) + + sts.Spec.Template.Spec.Containers[0].VolumeMounts = append(sts.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ + Name: blocksVolumeName, + MountPath: "/var/tempo/blocks", + }) + sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, corev1.Volume{ + Name: blocksVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumMemory, + }, + }, + }) + + case v1alpha1.MonolithicTracesStorageBackendPersistentVolume: + if tempo.Spec.Storage.Traces.WAL == nil { + return errors.New("please configure .spec.storage.traces.wal") + } + sts.Spec.VolumeClaimTemplates = append(sts.Spec.VolumeClaimTemplates, corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: walVolumeName, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: tempo.Spec.Storage.Traces.WAL.Size, + }, + }, + VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem), + }, + }) + + if tempo.Spec.Storage.Traces.PV == nil { + return errors.New("please configure .spec.storage.traces.pv") + } + sts.Spec.Template.Spec.Containers[0].VolumeMounts = append(sts.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ + Name: blocksVolumeName, + MountPath: "/var/tempo/blocks", + }) + sts.Spec.VolumeClaimTemplates = append(sts.Spec.VolumeClaimTemplates, corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: blocksVolumeName, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: tempo.Spec.Storage.Traces.PV.Size, + }, + }, + VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem), + }, + }) + + default: + return fmt.Errorf("invalid storage backend: '%s'", tempo.Spec.Storage.Traces.Backend) + } + return nil +} + +func configureJaegerUI(opts Options, sts *v1.StatefulSet) { + tempoQuery := corev1.Container{ + Name: "tempo-query", + Image: opts.CtrlConfig.DefaultImages.TempoQuery, + Env: proxy.ReadProxyVarsFromEnv(), + Args: []string{ + "--query.base-path=/", + "--grpc-storage-plugin.configuration-file=/conf/tempo-query.yaml", + "--query.bearer-token-propagation=true", + }, + Ports: []corev1.ContainerPort{ + { + Name: manifestutils.JaegerGRPCQuery, + ContainerPort: manifestutils.PortJaegerGRPCQuery, + Protocol: corev1.ProtocolTCP, + }, + { + Name: manifestutils.JaegerUIPortName, + ContainerPort: manifestutils.PortJaegerUI, + Protocol: corev1.ProtocolTCP, + }, + { + Name: manifestutils.JaegerMetricsPortName, + ContainerPort: manifestutils.PortJaegerMetrics, + Protocol: corev1.ProtocolTCP, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: manifestutils.ConfigVolumeName, + MountPath: "/conf", + ReadOnly: true, + }, + }, + } + + sts.Spec.Template.Spec.Containers = append(sts.Spec.Template.Spec.Containers, tempoQuery) +} diff --git a/internal/manifests/mutate.go b/internal/manifests/mutate.go index 6c2f989e5..691554e50 100644 --- a/internal/manifests/mutate.go +++ b/internal/manifests/mutate.go @@ -1,6 +1,7 @@ package manifests import ( + "fmt" "reflect" "github.com/ViaQ/logerr/v2/kverrors" @@ -12,10 +13,22 @@ import ( corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" rbacv1 "k8s.io/api/rbac/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) +// ImmutableErr occurs if an immutable field should be changed. +type ImmutableErr struct { + field string + existing interface{} + desired interface{} +} + +func (m *ImmutableErr) Error() string { + return fmt.Sprintf("update to immutable field %s is forbidden", m.field) +} + // MutateFuncFor returns a mutate function based on the // existing resource's concrete type. It supports currently // only the following types or else panics: @@ -127,6 +140,7 @@ func MutateFuncFor(existing, desired client.Object) controllerutil.MutateFn { } } +// Override non-empty dst attributes with non-empty src attributes values. func mergeWithOverride(dst, src interface{}) error { err := mergo.Merge(dst, src, mergo.WithOverride) if err != nil { @@ -231,12 +245,32 @@ func mutateDeployment(existing, desired *appsv1.Deployment) error { return nil } +func statefulSetVolumeClaimTemplatesChanged(existing, desired *appsv1.StatefulSet) bool { + if len(desired.Spec.VolumeClaimTemplates) != len(existing.Spec.VolumeClaimTemplates) { + return true + } + for i := range desired.Spec.VolumeClaimTemplates { + if desired.Spec.VolumeClaimTemplates[i].Name != existing.Spec.VolumeClaimTemplates[i].Name || + !apiequality.Semantic.DeepEqual(desired.Spec.VolumeClaimTemplates[i].Annotations, existing.Spec.VolumeClaimTemplates[i].Annotations) || + !apiequality.Semantic.DeepEqual(desired.Spec.VolumeClaimTemplates[i].Spec, existing.Spec.VolumeClaimTemplates[i].Spec) { + return true + } + } + return false +} + func mutateStatefulSet(existing, desired *appsv1.StatefulSet) error { - // StatefulSet selector is immutable so we set this value only if - // a new object is going to be created - if existing.CreationTimestamp.IsZero() { - existing.Spec.Selector = desired.Spec.Selector + // list of mutable fields: https://github.com/kubernetes/kubernetes/blob/b1cf91b300a82bd05fdd7b115559e5b83680d768/pkg/apis/apps/validation/validation.go#L184 + if !existing.CreationTimestamp.IsZero() { + if !apiequality.Semantic.DeepEqual(desired.Spec.Selector, existing.Spec.Selector) { + return &ImmutableErr{".spec.selector", existing.Spec.Selector, desired.Spec.Selector} + } + if statefulSetVolumeClaimTemplatesChanged(existing, desired) { + return &ImmutableErr{".spec.volumeClaimTemplates", existing.Spec.VolumeClaimTemplates, desired.Spec.VolumeClaimTemplates} + } } + + existing.Spec.Selector = desired.Spec.Selector existing.Spec.PodManagementPolicy = desired.Spec.PodManagementPolicy existing.Spec.Replicas = desired.Spec.Replicas for i := range existing.Spec.VolumeClaimTemplates { diff --git a/internal/manifests/queryfrontend/query_frontend.go b/internal/manifests/queryfrontend/query_frontend.go index b5df0c92c..f76fe66f7 100644 --- a/internal/manifests/queryfrontend/query_frontend.go +++ b/internal/manifests/queryfrontend/query_frontend.go @@ -23,15 +23,8 @@ import ( ) const ( - grpclbPortName = "grpclb" - jaegerMetricsPortName = "jaeger-metrics" - jaegerGRPCQuery = "jaeger-gprc" - jaegerUIPortName = "jaeger-ui" - portGRPCLBServer = 9096 - portJaegerGRPCQuery = 16685 - portJaegerUI = 16686 - portJaegerMetrics = 16687 - + grpclbPortName = "grpclb" + portGRPCLBServer = 9096 thanosQuerierOpenShiftMonitoring = "https://thanos-querier.openshift-monitoring.svc.cluster.local:9091" ) @@ -205,18 +198,18 @@ func deployment(params manifestutils.Params) (*appsv1.Deployment, error) { }, Ports: []corev1.ContainerPort{ { - Name: jaegerGRPCQuery, - ContainerPort: portJaegerGRPCQuery, + Name: manifestutils.JaegerGRPCQuery, + ContainerPort: manifestutils.PortJaegerGRPCQuery, Protocol: corev1.ProtocolTCP, }, { - Name: jaegerUIPortName, - ContainerPort: portJaegerUI, + Name: manifestutils.JaegerUIPortName, + ContainerPort: manifestutils.PortJaegerUI, Protocol: corev1.ProtocolTCP, }, { - Name: jaegerMetricsPortName, - ContainerPort: portJaegerMetrics, + Name: manifestutils.JaegerMetricsPortName, + ContainerPort: manifestutils.PortJaegerMetrics, Protocol: corev1.ProtocolTCP, }, }, @@ -414,19 +407,19 @@ func services(tempo v1alpha1.TempoStack) []*corev1.Service { if tempo.Spec.Template.QueryFrontend.JaegerQuery.Enabled { jaegerPorts := []corev1.ServicePort{ { - Name: jaegerGRPCQuery, - Port: portJaegerGRPCQuery, - TargetPort: intstr.FromString(jaegerGRPCQuery), + Name: manifestutils.JaegerGRPCQuery, + Port: manifestutils.PortJaegerGRPCQuery, + TargetPort: intstr.FromString(manifestutils.JaegerGRPCQuery), }, { - Name: jaegerUIPortName, - Port: portJaegerUI, - TargetPort: intstr.FromString(jaegerUIPortName), + Name: manifestutils.JaegerUIPortName, + Port: manifestutils.PortJaegerUI, + TargetPort: intstr.FromString(manifestutils.JaegerUIPortName), }, { - Name: jaegerMetricsPortName, - Port: portJaegerMetrics, - TargetPort: intstr.FromString(jaegerMetricsPortName), + Name: manifestutils.JaegerMetricsPortName, + Port: manifestutils.PortJaegerMetrics, + TargetPort: intstr.FromString(manifestutils.JaegerMetricsPortName), }, } @@ -457,7 +450,7 @@ func ingress(tempo v1alpha1.TempoStack) *networkingv1.Ingress { Service: &networkingv1.IngressServiceBackend{ Name: queryFrontendName, Port: networkingv1.ServiceBackendPort{ - Name: jaegerUIPortName, + Name: manifestutils.JaegerUIPortName, }, }, } @@ -519,7 +512,7 @@ func route(tempo v1alpha1.TempoStack) (*routev1.Route, error) { Name: queryFrontendName, }, Port: &routev1.RoutePort{ - TargetPort: intstr.FromString(jaegerUIPortName), + TargetPort: intstr.FromString(manifestutils.JaegerUIPortName), }, TLS: tlsCfg, }, diff --git a/internal/manifests/queryfrontend/query_frontend_test.go b/internal/manifests/queryfrontend/query_frontend_test.go index 3cd128e73..91aad0d76 100644 --- a/internal/manifests/queryfrontend/query_frontend_test.go +++ b/internal/manifests/queryfrontend/query_frontend_test.go @@ -26,19 +26,19 @@ import ( func getJaegerServicePorts() []corev1.ServicePort { jaegerServicePorts := []corev1.ServicePort{ { - Name: jaegerGRPCQuery, - Port: portJaegerGRPCQuery, - TargetPort: intstr.FromString(jaegerGRPCQuery), + Name: manifestutils.JaegerGRPCQuery, + Port: manifestutils.PortJaegerGRPCQuery, + TargetPort: intstr.FromString(manifestutils.JaegerGRPCQuery), }, { - Name: jaegerUIPortName, - Port: portJaegerUI, - TargetPort: intstr.FromString(jaegerUIPortName), + Name: manifestutils.JaegerUIPortName, + Port: manifestutils.PortJaegerUI, + TargetPort: intstr.FromString(manifestutils.JaegerUIPortName), }, { - Name: jaegerMetricsPortName, - Port: portJaegerMetrics, - TargetPort: intstr.FromString(jaegerMetricsPortName), + Name: manifestutils.JaegerMetricsPortName, + Port: manifestutils.PortJaegerMetrics, + TargetPort: intstr.FromString(manifestutils.JaegerMetricsPortName), }, } return jaegerServicePorts @@ -226,18 +226,18 @@ func getExpectedDeployment(withJaeger bool) *v1.Deployment { }, Ports: []corev1.ContainerPort{ { - Name: jaegerGRPCQuery, - ContainerPort: portJaegerGRPCQuery, + Name: manifestutils.JaegerGRPCQuery, + ContainerPort: manifestutils.PortJaegerGRPCQuery, Protocol: corev1.ProtocolTCP, }, { - Name: jaegerUIPortName, - ContainerPort: portJaegerUI, + Name: manifestutils.JaegerUIPortName, + ContainerPort: manifestutils.PortJaegerUI, Protocol: corev1.ProtocolTCP, }, { - Name: jaegerMetricsPortName, - ContainerPort: portJaegerMetrics, + Name: manifestutils.JaegerMetricsPortName, + ContainerPort: manifestutils.PortJaegerMetrics, Protocol: corev1.ProtocolTCP, }, }, @@ -432,7 +432,7 @@ func TestQueryFrontendJaegerIngress(t *testing.T) { Service: &networkingv1.IngressServiceBackend{ Name: naming.Name(manifestutils.QueryFrontendComponentName, "test"), Port: networkingv1.ServiceBackendPort{ - Name: jaegerUIPortName, + Name: manifestutils.JaegerUIPortName, }, }, }, @@ -483,7 +483,7 @@ func TestQueryFrontendJaegerRoute(t *testing.T) { Name: naming.Name(manifestutils.QueryFrontendComponentName, "test"), }, Port: &routev1.RoutePort{ - TargetPort: intstr.FromString(jaegerUIPortName), + TargetPort: intstr.FromString(manifestutils.JaegerUIPortName), }, TLS: &routev1.TLSConfig{ Termination: routev1.TLSTerminationEdge, From 51a2b26f8d805f4209d36143336f93e3af8f4973 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 21 Dec 2023 14:32:24 +0100 Subject: [PATCH 02/36] Add changelog Signed-off-by: Andreas Gerstmayr --- .chloggen/monolithic_mode.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 .chloggen/monolithic_mode.yaml diff --git a/.chloggen/monolithic_mode.yaml b/.chloggen/monolithic_mode.yaml new file mode 100755 index 000000000..cd27b900e --- /dev/null +++ b/.chloggen/monolithic_mode.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. operator, github action) +component: operator + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Support monolithic deployment mode + +# One or more tracking issues related to the change +issues: [710] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: From 41dbed1533384547071e981938860ff36a266144 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 21 Dec 2023 15:25:13 +0100 Subject: [PATCH 03/36] update mutate_test.go Signed-off-by: Andreas Gerstmayr --- internal/manifests/mutate_test.go | 88 ++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/internal/manifests/mutate_test.go b/internal/manifests/mutate_test.go index 85ef87ce4..1f7b431f6 100644 --- a/internal/manifests/mutate_test.go +++ b/internal/manifests/mutate_test.go @@ -642,9 +642,10 @@ func TestGeMutateFunc_MutateStatefulSetSpec(t *testing.T) { one := int32(1) two := int32(2) type test struct { + name string got *appsv1.StatefulSet want *appsv1.StatefulSet - name string + err error } table := []test{ { @@ -710,7 +711,69 @@ func TestGeMutateFunc_MutateStatefulSetSpec(t *testing.T) { }, }, { - name: "update spec without selector", + name: "update mutable field .spec.template", + got: &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.Now()}, + Spec: appsv1.StatefulSetSpec{ + PodManagementPolicy: appsv1.ParallelPodManagement, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "test": "test", + }, + }, + Replicas: &one, + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "test"}, + }, + }, + }, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + { + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + }, + }, + }, + }, + }, + want: &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.Now()}, + Spec: appsv1.StatefulSetSpec{ + PodManagementPolicy: appsv1.OrderedReadyPodManagement, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "test": "test", + }, + }, + Replicas: &two, + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test", + Args: []string{"--do-nothing"}, + }, + }, + }, + }, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + { + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + }, + }, + }, + }, + }, + }, + { + name: "update immutable field .spec.volumeClaimTemplates", got: &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.Now()}, Spec: appsv1.StatefulSetSpec{ @@ -746,7 +809,6 @@ func TestGeMutateFunc_MutateStatefulSetSpec(t *testing.T) { Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ "test": "test", - "and": "another", }, }, Replicas: &two, @@ -772,6 +834,7 @@ func TestGeMutateFunc_MutateStatefulSetSpec(t *testing.T) { }, }, }, + err: &manifests.ImmutableErr{}, }, } for _, tst := range table { @@ -780,19 +843,18 @@ func TestGeMutateFunc_MutateStatefulSetSpec(t *testing.T) { t.Parallel() f := manifests.MutateFuncFor(tst.got, tst.want) err := f() - require.NoError(t, err) - // Ensure conditional mutation applied - if tst.got.CreationTimestamp.IsZero() { - require.Equal(t, tst.got.Spec.Selector, tst.want.Spec.Selector) + if tst.err != nil { + require.ErrorAs(t, err, &tst.err) } else { - require.NotEqual(t, tst.got.Spec.Selector, tst.want.Spec.Selector) - } + require.NoError(t, err) - // Ensure partial mutation applied - require.Equal(t, tst.got.Spec.Replicas, tst.want.Spec.Replicas) - require.Equal(t, tst.got.Spec.Template, tst.want.Spec.Template) - require.Equal(t, tst.got.Spec.VolumeClaimTemplates, tst.got.Spec.VolumeClaimTemplates) + // Ensure partial mutation applied + require.Equal(t, tst.got.Spec.Selector, tst.want.Spec.Selector) + require.Equal(t, tst.got.Spec.Replicas, tst.want.Spec.Replicas) + require.Equal(t, tst.got.Spec.Template, tst.want.Spec.Template) + require.Equal(t, tst.got.Spec.VolumeClaimTemplates, tst.want.Spec.VolumeClaimTemplates) + } }) } } From 8aaa8b53f916e1e29a7044c51c81b037934b883a Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 21 Dec 2023 18:45:20 +0100 Subject: [PATCH 04/36] add webhook tests Signed-off-by: Andreas Gerstmayr --- apis/tempo/v1alpha1/tempomonolithic_types.go | 6 +- .../tempo/v1alpha1/tempomonolithic_webhook.go | 2 +- .../v1alpha1/tempomonolithic_webhook_test.go | 121 ++++++++++++++++++ apis/tempo/v1alpha1/zz_generated.deepcopy.go | 8 +- .../tempo/tempomonolithic_controller.go | 5 + 5 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 apis/tempo/v1alpha1/tempomonolithic_webhook_test.go diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index ee535f8df..5a80f00de 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -62,7 +62,7 @@ type MonolithicTracesStorageSpec struct { // PV defines the Persistent Volume configuration // // +kubebuilder:validation:Required - PV *MonolithicTracesStoragePersistentVolumeSpec `json:"pv"` + PV *MonolithicTracesStoragePVSpec `json:"pv"` } // MonolithicTracesStorageBackend defines the backend storage for traces. @@ -85,8 +85,8 @@ type MonolithicTracesStorageWALSpec struct { Size resource.Quantity `json:"size"` } -// MonolithicTracesStoragePersistentVolumeSpec defines the Persistent Volume configuration. -type MonolithicTracesStoragePersistentVolumeSpec struct { +// MonolithicTracesStoragePVSpec defines the Persistent Volume configuration. +type MonolithicTracesStoragePVSpec struct { // Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. // // +kubebuilder:validation:Required diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook.go b/apis/tempo/v1alpha1/tempomonolithic_webhook.go index ff7d71b5d..683f1002d 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook.go +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook.go @@ -34,7 +34,7 @@ func (r *TempoMonolithic) Default() { } if r.Spec.Storage.Traces.Backend == MonolithicTracesStorageBackendPersistentVolume && r.Spec.Storage.Traces.PV == nil { - r.Spec.Storage.Traces.PV = &MonolithicTracesStoragePersistentVolumeSpec{ + r.Spec.Storage.Traces.PV = &MonolithicTracesStoragePVSpec{ Size: tenGBQuantity, } } diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go b/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go new file mode 100644 index 000000000..f0f4a7668 --- /dev/null +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go @@ -0,0 +1,121 @@ +package v1alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMonolithicDefault(t *testing.T) { + tests := []struct { + name string + input *TempoMonolithic + expected *TempoMonolithic + }{ + { + name: "empty spec, set memory backend and enable OTLP/gRPC", + input: &TempoMonolithic{ + Spec: TempoMonolithicSpec{}, + }, + expected: &TempoMonolithic{ + Spec: TempoMonolithicSpec{ + Storage: MonolithicStorageSpec{ + Traces: MonolithicTracesStorageSpec{ + Backend: "memory", + }, + }, + Ingestion: &MonolithicIngestionSpec{ + OTLP: &MonolithicIngestionOTLPSpec{ + GRPC: &MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, + }, + }, + }, + }, + }, + { + name: "set default values for PV", + input: &TempoMonolithic{ + Spec: TempoMonolithicSpec{ + Storage: MonolithicStorageSpec{ + Traces: MonolithicTracesStorageSpec{ + Backend: "pv", + }, + }, + }, + }, + expected: &TempoMonolithic{ + Spec: TempoMonolithicSpec{ + Storage: MonolithicStorageSpec{ + Traces: MonolithicTracesStorageSpec{ + Backend: "pv", + WAL: &MonolithicTracesStorageWALSpec{ + Size: tenGBQuantity, + }, + PV: &MonolithicTracesStoragePVSpec{ + Size: tenGBQuantity, + }, + }, + }, + Ingestion: &MonolithicIngestionSpec{ + OTLP: &MonolithicIngestionOTLPSpec{ + GRPC: &MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, + }, + }, + }, + }, + }, + { + name: "do not change already set values", + input: &TempoMonolithic{ + Spec: TempoMonolithicSpec{ + Storage: MonolithicStorageSpec{ + Traces: MonolithicTracesStorageSpec{ + Backend: "s3", + WAL: &MonolithicTracesStorageWALSpec{ + Size: tenGBQuantity, + }, + }, + }, + Ingestion: &MonolithicIngestionSpec{ + OTLP: &MonolithicIngestionOTLPSpec{ + // HTTP is already set, GRPC should not be enabled by webhook + HTTP: &MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, + }, + }, + }, + }, + expected: &TempoMonolithic{ + Spec: TempoMonolithicSpec{ + Storage: MonolithicStorageSpec{ + Traces: MonolithicTracesStorageSpec{ + Backend: "s3", + WAL: &MonolithicTracesStorageWALSpec{ + Size: tenGBQuantity, + }, + }, + }, + Ingestion: &MonolithicIngestionSpec{ + OTLP: &MonolithicIngestionOTLPSpec{ + HTTP: &MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.input.Default() + assert.Equal(t, test.expected, test.input) + }) + } +} diff --git a/apis/tempo/v1alpha1/zz_generated.deepcopy.go b/apis/tempo/v1alpha1/zz_generated.deepcopy.go index 7829eda3a..47a5aeb26 100644 --- a/apis/tempo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/tempo/v1alpha1/zz_generated.deepcopy.go @@ -624,17 +624,17 @@ func (in *MonolithicStorageSpec) DeepCopy() *MonolithicStorageSpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MonolithicTracesStoragePersistentVolumeSpec) DeepCopyInto(out *MonolithicTracesStoragePersistentVolumeSpec) { +func (in *MonolithicTracesStoragePVSpec) DeepCopyInto(out *MonolithicTracesStoragePVSpec) { *out = *in out.Size = in.Size.DeepCopy() } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicTracesStoragePersistentVolumeSpec. -func (in *MonolithicTracesStoragePersistentVolumeSpec) DeepCopy() *MonolithicTracesStoragePersistentVolumeSpec { +func (in *MonolithicTracesStoragePVSpec) DeepCopy() *MonolithicTracesStoragePVSpec { if in == nil { return nil } - out := new(MonolithicTracesStoragePersistentVolumeSpec) + out := new(MonolithicTracesStoragePVSpec) in.DeepCopyInto(out) return out } @@ -649,7 +649,7 @@ func (in *MonolithicTracesStorageSpec) DeepCopyInto(out *MonolithicTracesStorage } if in.PV != nil { in, out := &in.PV, &out.PV - *out = new(MonolithicTracesStoragePersistentVolumeSpec) + *out = new(MonolithicTracesStoragePVSpec) (*in).DeepCopyInto(*out) } } diff --git a/controllers/tempo/tempomonolithic_controller.go b/controllers/tempo/tempomonolithic_controller.go index dde031b05..ccf663bdc 100644 --- a/controllers/tempo/tempomonolithic_controller.go +++ b/controllers/tempo/tempomonolithic_controller.go @@ -5,6 +5,8 @@ import ( "fmt" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" @@ -73,6 +75,9 @@ func (r *TempoMonolithicReconciler) Reconcile(ctx context.Context, req ctrl.Requ func (r *TempoMonolithicReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.TempoMonolithic{}). + Owns(&corev1.ConfigMap{}). + Owns(&corev1.Service{}). Owns(&appsv1.StatefulSet{}). + Owns(&networkingv1.Ingress{}). Complete(r) } From 442004c459ac2251a9804725d0fc1c617544bba6 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 21 Dec 2023 18:50:24 +0100 Subject: [PATCH 05/36] add generated files Signed-off-by: Andreas Gerstmayr --- apis/tempo/v1alpha1/zz_generated.deepcopy.go | 2 +- docs/operator/api.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apis/tempo/v1alpha1/zz_generated.deepcopy.go b/apis/tempo/v1alpha1/zz_generated.deepcopy.go index 47a5aeb26..48cbc8993 100644 --- a/apis/tempo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/tempo/v1alpha1/zz_generated.deepcopy.go @@ -629,7 +629,7 @@ func (in *MonolithicTracesStoragePVSpec) DeepCopyInto(out *MonolithicTracesStora out.Size = in.Size.DeepCopy() } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicTracesStoragePersistentVolumeSpec. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicTracesStoragePVSpec. func (in *MonolithicTracesStoragePVSpec) DeepCopy() *MonolithicTracesStoragePVSpec { if in == nil { return nil diff --git a/docs/operator/api.md b/docs/operator/api.md index cab1608b2..4fd16c30c 100644 --- a/docs/operator/api.md +++ b/docs/operator/api.md @@ -2540,7 +2540,7 @@ MonolithicTracesStorageSpec -## MonolithicTracesStoragePersistentVolumeSpec { #tempo-grafana-com-v1alpha1-MonolithicTracesStoragePersistentVolumeSpec } +## MonolithicTracesStoragePVSpec { #tempo-grafana-com-v1alpha1-MonolithicTracesStoragePVSpec }

@@ -2550,7 +2550,7 @@ MonolithicTracesStorageSpec

-

MonolithicTracesStoragePersistentVolumeSpec defines the Persistent Volume configuration.

+

MonolithicTracesStoragePVSpec defines the Persistent Volume configuration.

@@ -2682,9 +2682,9 @@ MonolithicTracesStorageWALSpec - + -MonolithicTracesStoragePersistentVolumeSpec +MonolithicTracesStoragePVSpec From 7d3ce55f61a1a9bbed49600ca800ec1995f6fb45 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 21 Dec 2023 20:05:01 +0100 Subject: [PATCH 06/36] support pruning unmanaged objects Signed-off-by: Andreas Gerstmayr --- controllers/tempo/common.go | 43 ++++++++-- .../tempo/tempomonolithic_controller.go | 85 ++++++++++++++++++- .../tempo/tempostack_controller_test.go | 2 +- internal/manifests/monolithic/config.go | 5 +- internal/manifests/monolithic/service.go | 11 ++- internal/manifests/monolithic/statefulset.go | 17 ++-- 6 files changed, 142 insertions(+), 21 deletions(-) diff --git a/controllers/tempo/common.go b/controllers/tempo/common.go index d0ce86fa8..801ae73c2 100644 --- a/controllers/tempo/common.go +++ b/controllers/tempo/common.go @@ -9,6 +9,7 @@ import ( rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -26,7 +27,18 @@ func isNamespaceScoped(obj client.Object) bool { // reconcileManagedObjects creates or updates all managed objects. // If immutable fields are changed, the object will be deleted and re-created. -func reconcileManagedObjects(ctx context.Context, log logr.Logger, k8sclient client.Client, owner metav1.Object, scheme *runtime.Scheme, managedObjects []client.Object) error { +func reconcileManagedObjects( + ctx context.Context, + log logr.Logger, + k8sclient client.Client, + owner metav1.Object, + scheme *runtime.Scheme, + managedObjects []client.Object, + ownedObjects map[types.UID]client.Object, +) error { + pruneObjects := ownedObjects + + // Create or update all objects managed by the operator errs := []error{} for _, obj := range managedObjects { l := log.WithValues( @@ -44,7 +56,6 @@ func reconcileManagedObjects(ctx context.Context, log logr.Logger, k8sclient cli desired := obj.DeepCopyObject().(client.Object) mutateFn := manifests.MutateFuncFor(obj, desired) - op, err := ctrl.CreateOrUpdate(ctx, k8sclient, obj, mutateFn) var immutableErr *manifests.ImmutableErr @@ -52,17 +63,39 @@ func reconcileManagedObjects(ctx context.Context, log logr.Logger, k8sclient cli l.Error(err, "detected a change in an immutable field. The object will be deleted, and re-created on next reconcile", "obj", obj.GetName()) err = k8sclient.Delete(ctx, desired) } + if err != nil { l.Error(err, "failed to configure resource") errs = append(errs, err) - continue + } else { + l.V(1).Info(fmt.Sprintf("resource has been %s", op)) } - l.V(1).Info(fmt.Sprintf("resource has been %s", op)) + // This object is still managed by the operator, remove it from the list of objects to prune + delete(pruneObjects, obj.GetUID()) } - if len(errs) > 0 { return fmt.Errorf("failed to create objects for %s: %w", owner.GetName(), errors.Join(errs...)) } + + // Prune owned objects in the cluster which are not managed anymore + pruneErrs := []error{} + for _, obj := range pruneObjects { + l := log.WithValues( + "objectName", obj.GetName(), + "objectKind", obj.GetObjectKind(), + ) + + l.Info("pruning unmanaged resource") + err := k8sclient.Delete(ctx, obj) + if err != nil { + l.Error(err, "failed to delete resource") + pruneErrs = append(pruneErrs, err) + } + } + if len(pruneErrs) > 0 { + return fmt.Errorf("failed to prune objects for %s: %w", owner.GetName(), errors.Join(pruneErrs...)) + } + return nil } diff --git a/controllers/tempo/tempomonolithic_controller.go b/controllers/tempo/tempomonolithic_controller.go index ccf663bdc..a67fd7d9e 100644 --- a/controllers/tempo/tempomonolithic_controller.go +++ b/controllers/tempo/tempomonolithic_controller.go @@ -8,14 +8,19 @@ import ( corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" + grafanav1 "github.com/grafana-operator/grafana-operator/v5/api/v1beta1" configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" "github.com/grafana/tempo-operator/internal/manifests/monolithic" + routev1 "github.com/openshift/api/route/v1" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" ) // TempoMonolithicReconciler reconciles a TempoMonolithic object. @@ -63,7 +68,12 @@ func (r *TempoMonolithicReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, fmt.Errorf("error building manifests: %w", err) } - err = reconcileManagedObjects(ctx, log, r.Client, &tempo, r.Scheme, managedObjects) + ownedObjects, err := r.getOwnedObjects(ctx, tempo) + if err != nil { + return ctrl.Result{}, err + } + + err = reconcileManagedObjects(ctx, log, r.Client, &tempo, r.Scheme, managedObjects, ownedObjects) if err != nil { return ctrl.Result{}, err } @@ -71,6 +81,79 @@ func (r *TempoMonolithicReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, nil } +func (r *TempoMonolithicReconciler) getOwnedObjects(ctx context.Context, tempo v1alpha1.TempoMonolithic) (map[types.UID]client.Object, error) { + ownedObjects := map[types.UID]client.Object{} + listOps := &client.ListOptions{ + Namespace: tempo.GetNamespace(), + LabelSelector: labels.SelectorFromSet(monolithic.CommonLabels(tempo.Name)), + } + + // Add all resources where the operator can conditionally create an object. + // For example, Ingress and Route can be enabled or disabled in the CR. + + serviceList := &corev1.ServiceList{} + err := r.List(ctx, serviceList, listOps) + if err != nil { + return nil, fmt.Errorf("error listing services: %w", err) + } + for i := range serviceList.Items { + ownedObjects[serviceList.Items[i].GetUID()] = &serviceList.Items[i] + } + + ingressList := &networkingv1.IngressList{} + err = r.List(ctx, ingressList, listOps) + if err != nil { + return nil, fmt.Errorf("error listing ingress: %w", err) + } + for i := range ingressList.Items { + ownedObjects[ingressList.Items[i].GetUID()] = &ingressList.Items[i] + } + + if r.CtrlConfig.Gates.PrometheusOperator { + servicemonitorList := &monitoringv1.ServiceMonitorList{} + err := r.List(ctx, servicemonitorList, listOps) + if err != nil { + return nil, fmt.Errorf("error listing service monitors: %w", err) + } + for i := range servicemonitorList.Items { + ownedObjects[servicemonitorList.Items[i].GetUID()] = servicemonitorList.Items[i] + } + + prometheusRulesList := &monitoringv1.PrometheusRuleList{} + err = r.List(ctx, prometheusRulesList, listOps) + if err != nil { + return nil, fmt.Errorf("error listing prometheus rules: %w", err) + } + for i := range prometheusRulesList.Items { + ownedObjects[prometheusRulesList.Items[i].GetUID()] = prometheusRulesList.Items[i] + } + } + + if r.CtrlConfig.Gates.OpenShift.OpenShiftRoute { + routesList := &routev1.RouteList{} + err := r.List(ctx, routesList, listOps) + if err != nil { + return nil, fmt.Errorf("error listing routes: %w", err) + } + for i := range routesList.Items { + ownedObjects[routesList.Items[i].GetUID()] = &routesList.Items[i] + } + } + + if r.CtrlConfig.Gates.GrafanaOperator { + datasourceList := &grafanav1.GrafanaDatasourceList{} + err := r.List(ctx, datasourceList, listOps) + if err != nil { + return nil, fmt.Errorf("error listing datasources: %w", err) + } + for i := range datasourceList.Items { + ownedObjects[datasourceList.Items[i].GetUID()] = &datasourceList.Items[i] + } + } + + return ownedObjects, nil +} + // SetupWithManager sets up the controller with the Manager. func (r *TempoMonolithicReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/controllers/tempo/tempostack_controller_test.go b/controllers/tempo/tempostack_controller_test.go index f9c8b98d4..823da603e 100644 --- a/controllers/tempo/tempostack_controller_test.go +++ b/controllers/tempo/tempostack_controller_test.go @@ -517,7 +517,7 @@ func TestStorageCustomCA(t *testing.T) { { Type: string(v1alpha1.ConditionReady), Status: "True", - LastTransitionTime: updatedTempo2.Status.Conditions[0].LastTransitionTime, + LastTransitionTime: updatedTempo3.Status.Conditions[0].LastTransitionTime, Reason: string(v1alpha1.ReasonReady), Message: "All components are operational", }, diff --git a/internal/manifests/monolithic/config.go b/internal/manifests/monolithic/config.go index 53158903d..dec159dfb 100644 --- a/internal/manifests/monolithic/config.go +++ b/internal/manifests/monolithic/config.go @@ -7,7 +7,7 @@ import ( "github.com/imdario/mergo" "gopkg.in/yaml.v2" - v1 "k8s.io/api/apps/v1" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -66,7 +66,8 @@ func BuildConfigMap(opts Options) (*corev1.ConfigMap, string, error) { configMap := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ - APIVersion: v1.SchemeGroupVersion.String(), + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "ConfigMap", }, ObjectMeta: metav1.ObjectMeta{ Name: naming.Name("", tempo.Name), diff --git a/internal/manifests/monolithic/service.go b/internal/manifests/monolithic/service.go index 52bb6bdc7..e9a2d25c6 100644 --- a/internal/manifests/monolithic/service.go +++ b/internal/manifests/monolithic/service.go @@ -1,7 +1,7 @@ package monolithic import ( - v1 "k8s.io/api/apps/v1" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -29,7 +29,8 @@ func buildTempoApiService(opts Options) *corev1.Service { labels := ComponentLabels("tempo", opts.Tempo.Name) return &corev1.Service{ TypeMeta: metav1.TypeMeta{ - APIVersion: v1.SchemeGroupVersion.String(), + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Service", }, ObjectMeta: metav1.ObjectMeta{ Name: naming.Name("api", opts.Tempo.Name), @@ -77,7 +78,8 @@ func buildTempoIngestService(opts Options) *corev1.Service { return &corev1.Service{ TypeMeta: metav1.TypeMeta{ - APIVersion: v1.SchemeGroupVersion.String(), + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Service", }, ObjectMeta: metav1.ObjectMeta{ Name: naming.Name("ingest", opts.Tempo.Name), @@ -95,7 +97,8 @@ func buildJaegerUIService(opts Options) *corev1.Service { labels := ComponentLabels("tempo", opts.Tempo.Name) return &corev1.Service{ TypeMeta: metav1.TypeMeta{ - APIVersion: v1.SchemeGroupVersion.String(), + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Service", }, ObjectMeta: metav1.ObjectMeta{ Name: naming.Name("jaegerui", opts.Tempo.Name), diff --git a/internal/manifests/monolithic/statefulset.go b/internal/manifests/monolithic/statefulset.go index 760265748..76036e0a3 100644 --- a/internal/manifests/monolithic/statefulset.go +++ b/internal/manifests/monolithic/statefulset.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/operator-framework/operator-lib/proxy" - v1 "k8s.io/api/apps/v1" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" @@ -21,21 +21,22 @@ const ( ) // BuildTempoStatefulset creates the Tempo statefulset for a monolithic deployment. -func BuildTempoStatefulset(opts Options) (*v1.StatefulSet, error) { +func BuildTempoStatefulset(opts Options) (*appsv1.StatefulSet, error) { tempo := opts.Tempo labels := ComponentLabels("tempo", tempo.Name) annotations := manifestutils.CommonAnnotations(opts.ConfigChecksum) - ss := &v1.StatefulSet{ + ss := &appsv1.StatefulSet{ TypeMeta: metav1.TypeMeta{ - APIVersion: v1.SchemeGroupVersion.String(), + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "StatefulSet", }, ObjectMeta: metav1.ObjectMeta{ Name: naming.Name("", tempo.Name), Namespace: tempo.Namespace, Labels: labels, }, - Spec: v1.StatefulSetSpec{ + Spec: appsv1.StatefulSetSpec{ Selector: &metav1.LabelSelector{ MatchLabels: labels, }, @@ -45,7 +46,7 @@ func BuildTempoStatefulset(opts Options) (*v1.StatefulSet, error) { // // This is a workaround for the above issue. // This setting is also in the tempo-distributed helm chart: https://github.com/grafana/helm-charts/blob/0fdf2e1900733eb104ac734f5fb0a89dc950d2c2/charts/tempo-distributed/templates/ingester/statefulset-ingester.yaml#L21 - PodManagementPolicy: v1.ParallelPodManagement, + PodManagementPolicy: appsv1.ParallelPodManagement, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ @@ -139,7 +140,7 @@ func buildTempoPorts(opts Options) []corev1.ContainerPort { return ports } -func configureStorage(opts Options, sts *v1.StatefulSet) error { +func configureStorage(opts Options, sts *appsv1.StatefulSet) error { tempo := opts.Tempo switch tempo.Spec.Storage.Traces.Backend { case v1alpha1.MonolithicTracesStorageBackendMemory: @@ -212,7 +213,7 @@ func configureStorage(opts Options, sts *v1.StatefulSet) error { return nil } -func configureJaegerUI(opts Options, sts *v1.StatefulSet) { +func configureJaegerUI(opts Options, sts *appsv1.StatefulSet) { tempoQuery := corev1.Container{ Name: "tempo-query", Image: opts.CtrlConfig.DefaultImages.TempoQuery, From 84cba0cb8b902a15116c0c24801398f0630a480d Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Fri, 22 Dec 2023 15:03:16 +0100 Subject: [PATCH 07/36] use common reconcile/prune function Signed-off-by: Andreas Gerstmayr --- .../tempo/tempostack_create_or_update.go | 71 +++---------------- internal/manifests/alerts/prometheus.go | 3 +- internal/manifests/config/configmap.go | 5 +- internal/manifests/gateway/openshift.go | 1 + 4 files changed, 16 insertions(+), 64 deletions(-) diff --git a/controllers/tempo/tempostack_create_or_update.go b/controllers/tempo/tempostack_create_or_update.go index 0d9ebdece..eb62d2598 100644 --- a/controllers/tempo/tempostack_create_or_update.go +++ b/controllers/tempo/tempostack_create_or_update.go @@ -2,7 +2,6 @@ package controllers import ( "context" - "errors" "fmt" "strings" @@ -114,15 +113,6 @@ func (r *TempoStackReconciler) createOrUpdate(ctx context.Context, log logr.Logg } - // Collect all objects owned by the operator, to be able to prune objects - // which exist in the cluster but are not managed by the operator anymore. - // For example, when the Jaeger Query Ingress is enabled and later disabled, - // the Ingress object should be removed from the cluster. - pruneObjects, err := r.findObjectsOwnedByTempoOperator(ctx, tempo) - if err != nil { - return err - } - var tenantSecrets []*manifestutils.GatewayTenantOIDCSecret if tempo.Spec.Tenants != nil && tempo.Spec.Tenants.Mode == v1alpha1.ModeStatic { tenantSecrets, err = gateway.GetOIDCTenantSecrets(ctx, r.Client, tempo) @@ -153,59 +143,18 @@ func (r *TempoStackReconciler) createOrUpdate(ctx context.Context, log logr.Logg return fmt.Errorf("error building manifests: %w", err) } - errs := []error{} - for _, obj := range managedObjects { - l := log.WithValues( - "object_name", obj.GetName(), - "object_kind", obj.GetObjectKind(), - ) - - if isNamespaceScoped(obj) { - obj.SetNamespace(req.Namespace) - if err := ctrl.SetControllerReference(&tempo, obj, r.Scheme); err != nil { - l.Error(err, "failed to set controller owner reference to resource") - errs = append(errs, err) - continue - } - } - - desired := obj.DeepCopyObject().(client.Object) - mutateFn := manifests.MutateFuncFor(obj, desired) - - op, err := ctrl.CreateOrUpdate(ctx, r.Client, obj, mutateFn) - if err != nil { - l.Error(err, "failed to configure resource") - errs = append(errs, err) - continue - } - - l.V(1).Info(fmt.Sprintf("resource has been %s", op)) - - // This object is still managed by the operator, remove it from the list of objects to prune - delete(pruneObjects, obj.GetUID()) - } - - if len(errs) > 0 { - return fmt.Errorf("failed to create objects for TempoStack %s: %w", req.NamespacedName, errors.Join(errs...)) + // Collect all objects owned by the operator, to be able to prune objects + // which exist in the cluster but are not managed by the operator anymore. + // For example, when the Jaeger Query Ingress is enabled and later disabled, + // the Ingress object should be removed from the cluster. + ownedObjects, err := r.findObjectsOwnedByTempoOperator(ctx, tempo) + if err != nil { + return err } - // Prune owned objects in the cluster which are not managed anymore. - pruneErrs := []error{} - for _, obj := range pruneObjects { - l := log.WithValues( - "object_name", obj.GetName(), - "object_kind", obj.GetObjectKind(), - ) - l.Info("pruning unmanaged resource") - - err = r.Delete(ctx, obj) - if err != nil { - l.Error(err, "failed to delete resource") - pruneErrs = append(pruneErrs, err) - } - } - if len(pruneErrs) > 0 { - return fmt.Errorf("failed to prune objects of TempoStack %s: %w", req.NamespacedName, errors.Join(pruneErrs...)) + err = reconcileManagedObjects(ctx, log, r.Client, &tempo, r.Scheme, managedObjects, ownedObjects) + if err != nil { + return err } return nil diff --git a/internal/manifests/alerts/prometheus.go b/internal/manifests/alerts/prometheus.go index 34e1fc28d..86a16f3b7 100644 --- a/internal/manifests/alerts/prometheus.go +++ b/internal/manifests/alerts/prometheus.go @@ -44,7 +44,8 @@ func newPrometheusRule(stackName, namespace string) (*monitoringv1.PrometheusRul }, ObjectMeta: metav1.ObjectMeta{ - Name: naming.PrometheusRuleName(stackName), + Name: naming.PrometheusRuleName(stackName), + Namespace: namespace, Labels: map[string]string{ "openshift.io/prometheus-rule-evaluation-scope": "leaf-prometheus", }, diff --git a/internal/manifests/config/configmap.go b/internal/manifests/config/configmap.go index 06fc0cf3a..09940501a 100644 --- a/internal/manifests/config/configmap.go +++ b/internal/manifests/config/configmap.go @@ -37,8 +37,9 @@ func BuildConfigMap(params manifestutils.Params) (*corev1.ConfigMap, string, err labels := manifestutils.ComponentLabels("config", tempo.Name) configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: naming.Name("", tempo.Name), - Labels: labels, + Name: naming.Name("", tempo.Name), + Namespace: tempo.Namespace, + Labels: labels, }, Data: map[string]string{ "tempo.yaml": string(config), diff --git a/internal/manifests/gateway/openshift.go b/internal/manifests/gateway/openshift.go index c85f18857..1ff5b9092 100644 --- a/internal/manifests/gateway/openshift.go +++ b/internal/manifests/gateway/openshift.go @@ -126,6 +126,7 @@ func configMapCABundle(tempo v1alpha1.TempoStack) *corev1.ConfigMap { return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: naming.Name("gateway-cabundle", tempo.Name), + Namespace: tempo.Namespace, Labels: manifestutils.ComponentLabels(manifestutils.GatewayComponentName, tempo.Name), Annotations: map[string]string{"service.beta.openshift.io/inject-cabundle": "true"}, }, From cd797bce46d43cbe093a840109a9994e4022135e Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Fri, 22 Dec 2023 15:29:45 +0100 Subject: [PATCH 08/36] fix linter Signed-off-by: Andreas Gerstmayr --- controllers/tempo/tempomonolithic_controller.go | 6 +++--- controllers/tempo/tempostack_controller.go | 2 +- controllers/tempo/tempostack_controller_test.go | 3 +-- controllers/tempo/tempostack_create_or_update.go | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/controllers/tempo/tempomonolithic_controller.go b/controllers/tempo/tempomonolithic_controller.go index a67fd7d9e..69d9b72fe 100644 --- a/controllers/tempo/tempomonolithic_controller.go +++ b/controllers/tempo/tempomonolithic_controller.go @@ -4,6 +4,9 @@ import ( "context" "fmt" + grafanav1 "github.com/grafana-operator/grafana-operator/v5/api/v1beta1" + routev1 "github.com/openshift/api/route/v1" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" @@ -15,12 +18,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - grafanav1 "github.com/grafana-operator/grafana-operator/v5/api/v1beta1" configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" "github.com/grafana/tempo-operator/internal/manifests/monolithic" - routev1 "github.com/openshift/api/route/v1" - monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" ) // TempoMonolithicReconciler reconciles a TempoMonolithic object. diff --git a/controllers/tempo/tempostack_controller.go b/controllers/tempo/tempostack_controller.go index 4a2bb396f..6f0bd87ab 100644 --- a/controllers/tempo/tempostack_controller.go +++ b/controllers/tempo/tempostack_controller.go @@ -113,7 +113,7 @@ func (r *TempoStackReconciler) Reconcile(ctx context.Context, req ctrl.Request) } } - err := r.createOrUpdate(ctx, log, req, tempo) + err := r.createOrUpdate(ctx, log, tempo) if err != nil { return r.handleReconcileStatus(ctx, log, tempo, err) } diff --git a/controllers/tempo/tempostack_controller_test.go b/controllers/tempo/tempostack_controller_test.go index 823da603e..e8d7138cf 100644 --- a/controllers/tempo/tempostack_controller_test.go +++ b/controllers/tempo/tempostack_controller_test.go @@ -888,8 +888,7 @@ func TestReconcileManifestsValidateModes(t *testing.T) { err := k8sClient.Update(context.Background(), tempo) require.NoError(t, err) reconciler := TempoStackReconciler{Client: k8sClient, Scheme: testScheme} - req := ctrl.Request{NamespacedName: nsn} - err = reconciler.createOrUpdate(context.Background(), logr.Discard(), req, *tempo) + err = reconciler.createOrUpdate(context.Background(), logr.Discard(), *tempo) tc.validate(t, err) }) } diff --git a/controllers/tempo/tempostack_create_or_update.go b/controllers/tempo/tempostack_create_or_update.go index eb62d2598..c2d912187 100644 --- a/controllers/tempo/tempostack_create_or_update.go +++ b/controllers/tempo/tempostack_create_or_update.go @@ -14,7 +14,6 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation/field" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" @@ -73,7 +72,7 @@ func (r *TempoStackReconciler) getStorageConfig(ctx context.Context, tempo v1alp return params, nil } -func (r *TempoStackReconciler) createOrUpdate(ctx context.Context, log logr.Logger, req ctrl.Request, tempo v1alpha1.TempoStack) error { +func (r *TempoStackReconciler) createOrUpdate(ctx context.Context, log logr.Logger, tempo v1alpha1.TempoStack) error { storageConfig, err := r.getStorageConfig(ctx, tempo) if err != nil { return &status.ConfigurationError{ From 355a79e93949c6b2f78ed0286b04dece5a424e98 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Mon, 8 Jan 2024 19:51:42 +0100 Subject: [PATCH 09/36] add sts tests Signed-off-by: Andreas Gerstmayr --- apis/tempo/v1alpha1/tempomonolithic_types.go | 12 +- .../tempo-operator.clusterserviceversion.yaml | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 2 - .../tempo-operator.clusterserviceversion.yaml | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 2 - .../tempo.grafana.com_tempomonolithics.yaml | 2 - .../manifests/monolithic/statefulset_test.go | 336 ++++++++++++++++++ 7 files changed, 344 insertions(+), 14 deletions(-) create mode 100644 internal/manifests/monolithic/statefulset_test.go diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index 5a80f00de..8215d6d1f 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -11,7 +11,7 @@ type TempoMonolithicSpec struct { // Storage defines the backend storage configuration // // +kubebuilder:validation:Optional - Storage MonolithicStorageSpec `json:"storage"` + Storage MonolithicStorageSpec `json:"storage,omitempty"` // Ingestion defines the trace ingestion configuration // @@ -25,7 +25,7 @@ type TempoMonolithicSpec struct { // ManagementState defines whether this instance is managed by the operator or self-managed // - // +kubebuilder:validation:Required + // +kubebuilder:validation:Optional Management ManagementStateType `json:"management,omitempty"` // Observability defines observability configuration for the Tempo deployment @@ -56,13 +56,13 @@ type MonolithicTracesStorageSpec struct { // WAL defines the write-ahead logging (WAL) configuration // - // +kubebuilder:validation:Required - WAL *MonolithicTracesStorageWALSpec `json:"wal"` + // +kubebuilder:validation:Optional + WAL *MonolithicTracesStorageWALSpec `json:"wal,omitempty"` // PV defines the Persistent Volume configuration // - // +kubebuilder:validation:Required - PV *MonolithicTracesStoragePVSpec `json:"pv"` + // +kubebuilder:validation:Optional + PV *MonolithicTracesStoragePVSpec `json:"pv,omitempty"` } // MonolithicTracesStorageBackend defines the backend storage for traces. diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index 26fcd0a89..6e020dd7e 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2023-12-21T12:56:45Z" + createdAt: "2024-01-08T18:44:24Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml index 04502402a..a725edd00 100644 --- a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -205,8 +205,6 @@ spec: type: object required: - backend - - pv - - wal type: object required: - traces diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index 8951a1ee8..b07d5fc64 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2023-12-21T12:56:44Z" + createdAt: "2024-01-08T18:44:22Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml index 04502402a..a725edd00 100644 --- a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -205,8 +205,6 @@ spec: type: object required: - backend - - pv - - wal type: object required: - traces diff --git a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml index 857af2d41..9dc254f55 100644 --- a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml +++ b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml @@ -202,8 +202,6 @@ spec: type: object required: - backend - - pv - - wal type: object required: - traces diff --git a/internal/manifests/monolithic/statefulset_test.go b/internal/manifests/monolithic/statefulset_test.go new file mode 100644 index 000000000..45ad0c27a --- /dev/null +++ b/internal/manifests/monolithic/statefulset_test.go @@ -0,0 +1,336 @@ +package monolithic + +import ( + "testing" + + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + "github.com/grafana/tempo-operator/internal/manifests/manifestutils" + "github.com/operator-framework/operator-lib/proxy" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +var ( + oneGBQuantity = resource.MustParse("1Gi") + tenGBQuantity = resource.MustParse("10Gi") +) + +func TestStatefulsetMemoryStorage(t *testing.T) { + opts := Options{ + CtrlConfig: configv1alpha1.ProjectConfig{ + DefaultImages: configv1alpha1.ImagesSpec{ + Tempo: "docker.io/grafana/tempo:x.y.z", + }, + }, + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{ + Storage: v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "memory", + }, + }, + Ingestion: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, + }, + }, + }, + }, + } + sts, err := BuildTempoStatefulset(opts) + require.NoError(t, err) + + labels := ComponentLabels("tempo", "sample") + annotations := manifestutils.CommonAnnotations("") + require.Equal(t, &appsv1.StatefulSet{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "StatefulSet", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tempo-sample", + Namespace: "default", + Labels: labels, + }, + Spec: appsv1.StatefulSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: labels, + }, + PodManagementPolicy: appsv1.ParallelPodManagement, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: annotations, + }, + Spec: corev1.PodSpec{ + Affinity: manifestutils.DefaultAffinity(labels), + Containers: []corev1.Container{ + { + Name: "tempo", + Image: "docker.io/grafana/tempo:x.y.z", + Env: proxy.ReadProxyVarsFromEnv(), + Args: []string{ + "-config.file=/conf/tempo.yaml", + "-mem-ballast-size-mbs=1024", + "-log.level=info", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "tempo-conf", + MountPath: "/conf", + ReadOnly: true, + }, + { + Name: "tempo-wal", + MountPath: "/var/tempo/wal", + }, + { + Name: "tempo-blocks", + MountPath: "/var/tempo/blocks", + }, + }, + Ports: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: 3200, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "otlp-grpc", + ContainerPort: 4317, + Protocol: corev1.ProtocolTCP, + }, + }, + ReadinessProbe: manifestutils.TempoReadinessProbe(false), + SecurityContext: manifestutils.TempoContainerSecurityContext(), + }, + }, + Volumes: []corev1.Volume{ + { + Name: "tempo-conf", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tempo-sample", + }, + }, + }, + }, + { + Name: "tempo-wal", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumMemory, + }, + }, + }, + { + Name: "tempo-blocks", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumMemory, + }, + }, + }, + }, + }, + }, + }, + }, sts) +} + +func TestStatefulsetPVStorage(t *testing.T) { + opts := Options{ + CtrlConfig: configv1alpha1.ProjectConfig{ + DefaultImages: configv1alpha1.ImagesSpec{ + Tempo: "docker.io/grafana/tempo:x.y.z", + }, + }, + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{ + Storage: v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "pv", + WAL: &v1alpha1.MonolithicTracesStorageWALSpec{ + Size: oneGBQuantity, + }, + PV: &v1alpha1.MonolithicTracesStoragePVSpec{ + Size: tenGBQuantity, + }, + }, + }, + }, + }, + } + sts, err := BuildTempoStatefulset(opts) + require.NoError(t, err) + + require.Equal(t, []corev1.VolumeMount{ + { + Name: "tempo-conf", + MountPath: "/conf", + ReadOnly: true, + }, + { + Name: "tempo-wal", + MountPath: "/var/tempo/wal", + }, + { + Name: "tempo-blocks", + MountPath: "/var/tempo/blocks", + }, + }, sts.Spec.Template.Spec.Containers[0].VolumeMounts) + + require.Equal(t, []corev1.Volume{ + { + Name: "tempo-conf", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tempo-sample", + }, + }, + }, + }, + }, sts.Spec.Template.Spec.Volumes) + + require.Equal(t, []corev1.PersistentVolumeClaim{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "tempo-wal", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: oneGBQuantity, + }, + }, + VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "tempo-blocks", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: tenGBQuantity, + }, + }, + VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem), + }, + }, + }, sts.Spec.VolumeClaimTemplates) +} + +func TestStatefulsetPorts(t *testing.T) { + opts := Options{ + CtrlConfig: configv1alpha1.ProjectConfig{ + DefaultImages: configv1alpha1.ImagesSpec{ + Tempo: "docker.io/grafana/tempo:x.y.z", + }, + }, + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{ + Storage: v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "memory", + }, + }, + }, + }, + } + + tests := []struct { + name string + input *v1alpha1.MonolithicIngestionSpec + expected []corev1.ContainerPort + }{ + { + name: "no ingestion ports", + input: nil, + expected: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: 3200, + Protocol: corev1.ProtocolTCP, + }, + }, + }, + { + name: "OTLP/gRPC", + input: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, + }, + }, + expected: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: 3200, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "otlp-grpc", + ContainerPort: 4317, + Protocol: corev1.ProtocolTCP, + }, + }, + }, + { + name: "OTLP/HTTP", + input: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + HTTP: &v1alpha1.MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, + }, + }, + expected: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: 3200, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "otlp-http", + ContainerPort: 4318, + Protocol: corev1.ProtocolTCP, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + opts.Tempo.Spec.Ingestion = test.input + sts, err := BuildTempoStatefulset(opts) + require.NoError(t, err) + require.Equal(t, test.expected, sts.Spec.Template.Spec.Containers[0].Ports) + }) + } +} From 82fef56ccb477d0e6da4d4f763335148a733f5b5 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Mon, 8 Jan 2024 20:05:13 +0100 Subject: [PATCH 10/36] add service tests Signed-off-by: Andreas Gerstmayr --- internal/manifests/manifestutils/constants.go | 2 +- internal/manifests/monolithic/service_test.go | 177 ++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 internal/manifests/monolithic/service_test.go diff --git a/internal/manifests/manifestutils/constants.go b/internal/manifests/manifestutils/constants.go index 0231d694d..481d915eb 100644 --- a/internal/manifests/manifestutils/constants.go +++ b/internal/manifests/manifestutils/constants.go @@ -43,7 +43,7 @@ const ( PortJaegerUI = 16686 // JaegerGRPCQuery declares the name of the Jaeger UI gPRC port. - JaegerGRPCQuery = "jaeger-gprc" + JaegerGRPCQuery = "jaeger-grpc" // PortJaegerGRPCQuery declares the port number of the Jaeger UI gPRC port. PortJaegerGRPCQuery = 16685 diff --git a/internal/manifests/monolithic/service_test.go b/internal/manifests/monolithic/service_test.go new file mode 100644 index 000000000..644a2f12a --- /dev/null +++ b/internal/manifests/monolithic/service_test.go @@ -0,0 +1,177 @@ +package monolithic + +import ( + "testing" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestBuildTempoApiService(t *testing.T) { + opts := Options{ + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + }, + } + + svc := buildTempoApiService(opts) + + labels := ComponentLabels("tempo", "sample") + require.Equal(t, &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tempo-sample-api", + Namespace: "default", + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Protocol: corev1.ProtocolTCP, + Port: 3200, + TargetPort: intstr.FromString("http"), + }, + }, + Selector: labels, + }, + }, svc) +} + +func TestBuildTempoIngestService(t *testing.T) { + opts := Options{ + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{}, + }, + } + + tests := []struct { + name string + input *v1alpha1.MonolithicIngestionSpec + expected []corev1.ServicePort + }{ + { + name: "no ingestion ports", + input: nil, + expected: []corev1.ServicePort{}, + }, + { + name: "OTLP/gRPC", + input: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, + }, + }, + expected: []corev1.ServicePort{ + { + Name: "otlp-grpc", + Protocol: corev1.ProtocolTCP, + Port: 4317, + TargetPort: intstr.FromString("otlp-grpc"), + }, + }, + }, + { + name: "OTLP/HTTP", + input: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + HTTP: &v1alpha1.MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, + }, + }, + expected: []corev1.ServicePort{ + { + Name: "otlp-http", + Protocol: corev1.ProtocolTCP, + Port: 4318, + TargetPort: intstr.FromString("otlp-http"), + }, + }, + }, + } + + labels := ComponentLabels("tempo", "sample") + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + opts.Tempo.Spec.Ingestion = test.input + svc := buildTempoIngestService(opts) + require.Equal(t, &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tempo-sample-ingest", + Namespace: "default", + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: test.expected, + Selector: labels, + }, + }, svc) + }) + } +} + +func TestBuildJaegerUIService(t *testing.T) { + opts := Options{ + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + }, + } + + svc := buildJaegerUIService(opts) + + labels := ComponentLabels("tempo", "sample") + require.Equal(t, &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tempo-sample-jaegerui", + Namespace: "default", + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "jaeger-grpc", + Port: 16685, + TargetPort: intstr.FromString("jaeger-grpc"), + }, + { + Name: "jaeger-ui", + Port: 16686, + TargetPort: intstr.FromString("jaeger-ui"), + }, + { + Name: "jaeger-metrics", + Port: 16687, + TargetPort: intstr.FromString("jaeger-metrics"), + }, + }, + Selector: labels, + }, + }, svc) +} From 0f490ddbdc1bbf366dc2e697c1d301f56fabfb96 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Tue, 9 Jan 2024 17:51:37 +0100 Subject: [PATCH 11/36] add configmap tests Signed-off-by: Andreas Gerstmayr --- .../monolithic/{config.go => configmap.go} | 0 .../manifests/monolithic/configmap_test.go | 141 ++++++++++++++++++ 2 files changed, 141 insertions(+) rename internal/manifests/monolithic/{config.go => configmap.go} (100%) create mode 100644 internal/manifests/monolithic/configmap_test.go diff --git a/internal/manifests/monolithic/config.go b/internal/manifests/monolithic/configmap.go similarity index 100% rename from internal/manifests/monolithic/config.go rename to internal/manifests/monolithic/configmap.go diff --git a/internal/manifests/monolithic/configmap_test.go b/internal/manifests/monolithic/configmap_test.go new file mode 100644 index 000000000..a7a2fa71a --- /dev/null +++ b/internal/manifests/monolithic/configmap_test.go @@ -0,0 +1,141 @@ +package monolithic + +import ( + "crypto/sha256" + "fmt" + "testing" + + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestBuildConfigMap(t *testing.T) { + opts := Options{ + CtrlConfig: configv1alpha1.ProjectConfig{ + DefaultImages: configv1alpha1.ImagesSpec{ + Tempo: "docker.io/grafana/tempo:x.y.z", + }, + }, + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{ + Storage: v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "memory", + }, + }, + Ingestion: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, + }, + }, + }, + }, + } + + cm, checksum, err := BuildConfigMap(opts) + require.NoError(t, err) + require.NotNil(t, cm.Data) + require.NotNil(t, cm.Data["tempo.yaml"]) + require.Equal(t, fmt.Sprintf("%x", sha256.Sum256([]byte(cm.Data["tempo.yaml"]))), checksum) +} + +func TestBuildConfig(t *testing.T) { + opts := Options{ + CtrlConfig: configv1alpha1.ProjectConfig{ + DefaultImages: configv1alpha1.ImagesSpec{ + Tempo: "docker.io/grafana/tempo:x.y.z", + }, + }, + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{}, + }, + } + + tests := []struct { + name string + storage v1alpha1.MonolithicStorageSpec + ingestion *v1alpha1.MonolithicIngestionSpec + expected string + }{ + { + name: "memory storage", + storage: v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "memory", + }, + }, + expected: ` +server: + http_listen_port: 3200 +storage: + trace: + backend: local + wal: + path: /var/tempo/wal + local: + path: /var/tempo/blocks +distributor: + receivers: + otlp: + protocols: {} +usage_report: + reporting_enabled: false +`, + }, + { + name: "PV storage with OTLP/gRPC and OTLP/HTTP", + storage: v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "pv", + }, + }, + ingestion: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, + }, + }, + expected: ` +server: + http_listen_port: 3200 +storage: + trace: + backend: local + wal: + path: /var/tempo/wal + local: + path: /var/tempo/blocks +distributor: + receivers: + otlp: + protocols: + grpc: {} +usage_report: + reporting_enabled: false +`, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + opts.Tempo.Spec.Storage = test.storage + opts.Tempo.Spec.Ingestion = test.ingestion + cfg, err := buildTempoConfig(opts) + require.NoError(t, err) + require.YAMLEq(t, test.expected, string(cfg)) + }) + } +} From c84f6bd4c9ec2265a67c90ddc26c191b8d44ea6e Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 10 Jan 2024 20:09:03 +0100 Subject: [PATCH 12/36] use a single service, because the monolithic deployment will use a single pod Signed-off-by: Andreas Gerstmayr --- internal/manifests/monolithic/build.go | 4 +- internal/manifests/monolithic/service.go | 108 +++-------- internal/manifests/monolithic/service_test.go | 172 ++++++++---------- 3 files changed, 103 insertions(+), 181 deletions(-) diff --git a/internal/manifests/monolithic/build.go b/internal/manifests/monolithic/build.go index b65037d32..df13c49d5 100644 --- a/internal/manifests/monolithic/build.go +++ b/internal/manifests/monolithic/build.go @@ -21,8 +21,8 @@ func BuildAll(opts Options) ([]client.Object, error) { } manifests = append(manifests, statefulSet) - services := BuildServices(opts) - manifests = append(manifests, services...) + service := BuildTempoService(opts) + manifests = append(manifests, service) return manifests, nil } diff --git a/internal/manifests/monolithic/service.go b/internal/manifests/monolithic/service.go index e9a2d25c6..c41bec7ed 100644 --- a/internal/manifests/monolithic/service.go +++ b/internal/manifests/monolithic/service.go @@ -5,57 +5,24 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/grafana/tempo-operator/internal/manifests/manifestutils" "github.com/grafana/tempo-operator/internal/manifests/naming" ) -// BuildServices creates all services for a monolithic deployment. -func BuildServices(opts Options) []client.Object { - services := []client.Object{ - buildTempoApiService(opts), - buildTempoIngestService(opts), - } - - if opts.Tempo.Spec.JaegerUI != nil && opts.Tempo.Spec.JaegerUI.Enabled { - services = append(services, buildJaegerUIService(opts)) - } - - return services -} - -func buildTempoApiService(opts Options) *corev1.Service { - labels := ComponentLabels("tempo", opts.Tempo.Name) - return &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - APIVersion: appsv1.SchemeGroupVersion.String(), - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: naming.Name("api", opts.Tempo.Name), - Namespace: opts.Tempo.Namespace, - Labels: labels, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: manifestutils.HttpPortName, - Protocol: corev1.ProtocolTCP, - Port: manifestutils.PortHTTPServer, - TargetPort: intstr.FromString(manifestutils.HttpPortName), - }, - }, - Selector: labels, +// BuildTempoService creates the service for a monolithic deployment. +func BuildTempoService(opts Options) *corev1.Service { + tempo := opts.Tempo + labels := CommonLabels(opts.Tempo.Name) + ports := []corev1.ServicePort{ + { + Name: manifestutils.HttpPortName, + Protocol: corev1.ProtocolTCP, + Port: manifestutils.PortHTTPServer, + TargetPort: intstr.FromString(manifestutils.HttpPortName), }, } -} -func buildTempoIngestService(opts Options) *corev1.Service { - tempo := opts.Tempo - labels := ComponentLabels("tempo", tempo.Name) - - ports := []corev1.ServicePort{} // TODO: point to gateway if tempo.Spec.Ingestion != nil && tempo.Spec.Ingestion.OTLP != nil { if tempo.Spec.Ingestion.OTLP.GRPC != nil && tempo.Spec.Ingestion.OTLP.GRPC.Enabled { @@ -76,53 +43,38 @@ func buildTempoIngestService(opts Options) *corev1.Service { } } - return &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - APIVersion: appsv1.SchemeGroupVersion.String(), - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: naming.Name("ingest", opts.Tempo.Name), - Namespace: opts.Tempo.Namespace, - Labels: labels, - }, - Spec: corev1.ServiceSpec{ - Ports: ports, - Selector: labels, - }, + if opts.Tempo.Spec.JaegerUI != nil && opts.Tempo.Spec.JaegerUI.Enabled { + ports = append(ports, []corev1.ServicePort{ + { + Name: manifestutils.JaegerGRPCQuery, + Port: manifestutils.PortJaegerGRPCQuery, + TargetPort: intstr.FromString(manifestutils.JaegerGRPCQuery), + }, + { + Name: manifestutils.JaegerUIPortName, + Port: manifestutils.PortJaegerUI, + TargetPort: intstr.FromString(manifestutils.JaegerUIPortName), + }, + { + Name: manifestutils.JaegerMetricsPortName, + Port: manifestutils.PortJaegerMetrics, + TargetPort: intstr.FromString(manifestutils.JaegerMetricsPortName), + }, + }...) } -} -func buildJaegerUIService(opts Options) *corev1.Service { - labels := ComponentLabels("tempo", opts.Tempo.Name) return &corev1.Service{ TypeMeta: metav1.TypeMeta{ APIVersion: appsv1.SchemeGroupVersion.String(), Kind: "Service", }, ObjectMeta: metav1.ObjectMeta{ - Name: naming.Name("jaegerui", opts.Tempo.Name), + Name: naming.Name("", opts.Tempo.Name), Namespace: opts.Tempo.Namespace, Labels: labels, }, Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: manifestutils.JaegerGRPCQuery, - Port: manifestutils.PortJaegerGRPCQuery, - TargetPort: intstr.FromString(manifestutils.JaegerGRPCQuery), - }, - { - Name: manifestutils.JaegerUIPortName, - Port: manifestutils.PortJaegerUI, - TargetPort: intstr.FromString(manifestutils.JaegerUIPortName), - }, - { - Name: manifestutils.JaegerMetricsPortName, - Port: manifestutils.PortJaegerMetrics, - TargetPort: intstr.FromString(manifestutils.JaegerMetricsPortName), - }, - }, + Ports: ports, Selector: labels, }, } diff --git a/internal/manifests/monolithic/service_test.go b/internal/manifests/monolithic/service_test.go index 644a2f12a..6ad5d30f4 100644 --- a/internal/manifests/monolithic/service_test.go +++ b/internal/manifests/monolithic/service_test.go @@ -10,7 +10,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -func TestBuildTempoApiService(t *testing.T) { +func TestBuildTempoService(t *testing.T) { opts := Options{ Tempo: v1alpha1.TempoMonolithic{ ObjectMeta: metav1.ObjectMeta{ @@ -20,21 +20,15 @@ func TestBuildTempoApiService(t *testing.T) { }, } - svc := buildTempoApiService(opts) - - labels := ComponentLabels("tempo", "sample") - require.Equal(t, &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "apps/v1", - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tempo-sample-api", - Namespace: "default", - Labels: labels, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ + tests := []struct { + name string + input v1alpha1.TempoMonolithicSpec + expected []corev1.ServicePort + }{ + { + name: "no ingestion ports, no jaeger ui", + input: v1alpha1.TempoMonolithicSpec{}, + expected: []corev1.ServicePort{ { Name: "http", Protocol: corev1.ProtocolTCP, @@ -42,42 +36,25 @@ func TestBuildTempoApiService(t *testing.T) { TargetPort: intstr.FromString("http"), }, }, - Selector: labels, - }, - }, svc) -} - -func TestBuildTempoIngestService(t *testing.T) { - opts := Options{ - Tempo: v1alpha1.TempoMonolithic{ - ObjectMeta: metav1.ObjectMeta{ - Name: "sample", - Namespace: "default", - }, - Spec: v1alpha1.TempoMonolithicSpec{}, - }, - } - - tests := []struct { - name string - input *v1alpha1.MonolithicIngestionSpec - expected []corev1.ServicePort - }{ - { - name: "no ingestion ports", - input: nil, - expected: []corev1.ServicePort{}, }, { - name: "OTLP/gRPC", - input: &v1alpha1.MonolithicIngestionSpec{ - OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ - GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ - Enabled: true, + name: "ingest OTLP/gRPC", + input: v1alpha1.TempoMonolithicSpec{ + Ingestion: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, }, }, }, expected: []corev1.ServicePort{ + { + Name: "http", + Protocol: corev1.ProtocolTCP, + Port: 3200, + TargetPort: intstr.FromString("http"), + }, { Name: "otlp-grpc", Protocol: corev1.ProtocolTCP, @@ -87,15 +64,23 @@ func TestBuildTempoIngestService(t *testing.T) { }, }, { - name: "OTLP/HTTP", - input: &v1alpha1.MonolithicIngestionSpec{ - OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ - HTTP: &v1alpha1.MonolithicIngestionOTLPProtocolsHTTPSpec{ - Enabled: true, + name: "ingest OTLP/HTTP", + input: v1alpha1.TempoMonolithicSpec{ + Ingestion: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + HTTP: &v1alpha1.MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, }, }, }, expected: []corev1.ServicePort{ + { + Name: "http", + Protocol: corev1.ProtocolTCP, + Port: 3200, + TargetPort: intstr.FromString("http"), + }, { Name: "otlp-http", Protocol: corev1.ProtocolTCP, @@ -104,20 +89,51 @@ func TestBuildTempoIngestService(t *testing.T) { }, }, }, + { + name: "enable JaegerUI", + input: v1alpha1.TempoMonolithicSpec{ + JaegerUI: &v1alpha1.MonolithicJaegerUISpec{ + Enabled: true, + }, + }, + expected: []corev1.ServicePort{ + { + Name: "http", + Protocol: corev1.ProtocolTCP, + Port: 3200, + TargetPort: intstr.FromString("http"), + }, + { + Name: "jaeger-grpc", + Port: 16685, + TargetPort: intstr.FromString("jaeger-grpc"), + }, + { + Name: "jaeger-ui", + Port: 16686, + TargetPort: intstr.FromString("jaeger-ui"), + }, + { + Name: "jaeger-metrics", + Port: 16687, + TargetPort: intstr.FromString("jaeger-metrics"), + }, + }, + }, } - labels := ComponentLabels("tempo", "sample") + labels := CommonLabels("sample") for _, test := range tests { t.Run(test.name, func(t *testing.T) { - opts.Tempo.Spec.Ingestion = test.input - svc := buildTempoIngestService(opts) + opts.Tempo.Spec = test.input + svc := BuildTempoService(opts) require.Equal(t, &corev1.Service{ TypeMeta: metav1.TypeMeta{ APIVersion: "apps/v1", Kind: "Service", }, ObjectMeta: metav1.ObjectMeta{ - Name: "tempo-sample-ingest", + Name: "tempo-sample", Namespace: "default", Labels: labels, }, @@ -129,49 +145,3 @@ func TestBuildTempoIngestService(t *testing.T) { }) } } - -func TestBuildJaegerUIService(t *testing.T) { - opts := Options{ - Tempo: v1alpha1.TempoMonolithic{ - ObjectMeta: metav1.ObjectMeta{ - Name: "sample", - Namespace: "default", - }, - }, - } - - svc := buildJaegerUIService(opts) - - labels := ComponentLabels("tempo", "sample") - require.Equal(t, &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "apps/v1", - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tempo-sample-jaegerui", - Namespace: "default", - Labels: labels, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "jaeger-grpc", - Port: 16685, - TargetPort: intstr.FromString("jaeger-grpc"), - }, - { - Name: "jaeger-ui", - Port: 16686, - TargetPort: intstr.FromString("jaeger-ui"), - }, - { - Name: "jaeger-metrics", - Port: 16687, - TargetPort: intstr.FromString("jaeger-metrics"), - }, - }, - Selector: labels, - }, - }, svc) -} From bd54f6b508239f8fb94d83c2b43944403156d660 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 10 Jan 2024 20:11:57 +0100 Subject: [PATCH 13/36] drop component from labels Signed-off-by: Andreas Gerstmayr --- controllers/tempo/tempomonolithic_controller.go | 2 +- internal/manifests/monolithic/configmap.go | 2 +- internal/manifests/monolithic/labels.go | 13 ++----------- internal/manifests/monolithic/service.go | 2 +- internal/manifests/monolithic/service_test.go | 2 +- internal/manifests/monolithic/statefulset.go | 2 +- internal/manifests/monolithic/statefulset_test.go | 2 +- 7 files changed, 8 insertions(+), 17 deletions(-) diff --git a/controllers/tempo/tempomonolithic_controller.go b/controllers/tempo/tempomonolithic_controller.go index 69d9b72fe..a5b6f8a59 100644 --- a/controllers/tempo/tempomonolithic_controller.go +++ b/controllers/tempo/tempomonolithic_controller.go @@ -85,7 +85,7 @@ func (r *TempoMonolithicReconciler) getOwnedObjects(ctx context.Context, tempo v ownedObjects := map[types.UID]client.Object{} listOps := &client.ListOptions{ Namespace: tempo.GetNamespace(), - LabelSelector: labels.SelectorFromSet(monolithic.CommonLabels(tempo.Name)), + LabelSelector: labels.SelectorFromSet(monolithic.Labels(tempo.Name)), } // Add all resources where the operator can conditionally create an object. diff --git a/internal/manifests/monolithic/configmap.go b/internal/manifests/monolithic/configmap.go index dec159dfb..da1035202 100644 --- a/internal/manifests/monolithic/configmap.go +++ b/internal/manifests/monolithic/configmap.go @@ -57,7 +57,7 @@ type tempoQueryConfig struct { // BuildConfigMap creates the Tempo ConfigMap for a monolithic deployment. func BuildConfigMap(opts Options) (*corev1.ConfigMap, string, error) { tempo := opts.Tempo - labels := ComponentLabels("config", tempo.Name) + labels := Labels(tempo.Name) tempoConfig, err := buildTempoConfig(opts) if err != nil { diff --git a/internal/manifests/monolithic/labels.go b/internal/manifests/monolithic/labels.go index cd191fa88..9469ba3ef 100644 --- a/internal/manifests/monolithic/labels.go +++ b/internal/manifests/monolithic/labels.go @@ -1,16 +1,7 @@ package monolithic -import "k8s.io/apimachinery/pkg/labels" - -// ComponentLabels is a list of all commonLabels including the app.kubernetes.io/component: label. -func ComponentLabels(component, instanceName string) labels.Set { - return labels.Merge(CommonLabels(instanceName), map[string]string{ - "app.kubernetes.io/component": component, - }) -} - -// CommonLabels returns common labels for each TempoMonolithic object created by the operator. -func CommonLabels(instanceName string) map[string]string { +// Labels returns common labels for each TempoMonolithic object created by the operator. +func Labels(instanceName string) map[string]string { return map[string]string{ "app.kubernetes.io/name": "tempo-monolithic", "app.kubernetes.io/instance": instanceName, diff --git a/internal/manifests/monolithic/service.go b/internal/manifests/monolithic/service.go index c41bec7ed..4a3a2d202 100644 --- a/internal/manifests/monolithic/service.go +++ b/internal/manifests/monolithic/service.go @@ -13,7 +13,7 @@ import ( // BuildTempoService creates the service for a monolithic deployment. func BuildTempoService(opts Options) *corev1.Service { tempo := opts.Tempo - labels := CommonLabels(opts.Tempo.Name) + labels := Labels(opts.Tempo.Name) ports := []corev1.ServicePort{ { Name: manifestutils.HttpPortName, diff --git a/internal/manifests/monolithic/service_test.go b/internal/manifests/monolithic/service_test.go index 6ad5d30f4..fdfb76589 100644 --- a/internal/manifests/monolithic/service_test.go +++ b/internal/manifests/monolithic/service_test.go @@ -122,7 +122,7 @@ func TestBuildTempoService(t *testing.T) { }, } - labels := CommonLabels("sample") + labels := Labels("sample") for _, test := range tests { t.Run(test.name, func(t *testing.T) { opts.Tempo.Spec = test.input diff --git a/internal/manifests/monolithic/statefulset.go b/internal/manifests/monolithic/statefulset.go index 76036e0a3..2f3113a81 100644 --- a/internal/manifests/monolithic/statefulset.go +++ b/internal/manifests/monolithic/statefulset.go @@ -23,7 +23,7 @@ const ( // BuildTempoStatefulset creates the Tempo statefulset for a monolithic deployment. func BuildTempoStatefulset(opts Options) (*appsv1.StatefulSet, error) { tempo := opts.Tempo - labels := ComponentLabels("tempo", tempo.Name) + labels := Labels(tempo.Name) annotations := manifestutils.CommonAnnotations(opts.ConfigChecksum) ss := &appsv1.StatefulSet{ diff --git a/internal/manifests/monolithic/statefulset_test.go b/internal/manifests/monolithic/statefulset_test.go index 45ad0c27a..dc4ab81f1 100644 --- a/internal/manifests/monolithic/statefulset_test.go +++ b/internal/manifests/monolithic/statefulset_test.go @@ -51,7 +51,7 @@ func TestStatefulsetMemoryStorage(t *testing.T) { sts, err := BuildTempoStatefulset(opts) require.NoError(t, err) - labels := ComponentLabels("tempo", "sample") + labels := Labels("sample") annotations := manifestutils.CommonAnnotations("") require.Equal(t, &appsv1.StatefulSet{ TypeMeta: metav1.TypeMeta{ From 8c66093f8005ab53ee0b3414862d4f29a3e71ccc Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 10 Jan 2024 20:23:05 +0100 Subject: [PATCH 14/36] fix configmap Signed-off-by: Andreas Gerstmayr --- internal/manifests/monolithic/configmap.go | 18 ++++++++++-------- .../manifests/monolithic/configmap_test.go | 10 +++++----- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/internal/manifests/monolithic/configmap.go b/internal/manifests/monolithic/configmap.go index da1035202..6f44380cf 100644 --- a/internal/manifests/monolithic/configmap.go +++ b/internal/manifests/monolithic/configmap.go @@ -37,12 +37,12 @@ type tempoConfig struct { Receivers struct { OTLP struct { Protocols struct { - GRPC *struct{} `yaml:"grpc,omitempty"` - HTTP *struct{} `yaml:"http,omitempty"` - } `yaml:"protocols"` - } `yaml:"otlp"` - } `yaml:"receivers"` - } `yaml:"distributor"` + GRPC *interface{} `yaml:"grpc,omitempty"` + HTTP *interface{} `yaml:"http,omitempty"` + } `yaml:"protocols,omitempty"` + } `yaml:"otlp,omitempty"` + } `yaml:"receivers,omitempty"` + } `yaml:"distributor,omitempty"` UsageReport struct { ReportingEnabled bool `yaml:"reporting_enabled"` @@ -112,10 +112,12 @@ func buildTempoConfig(opts Options) ([]byte, error) { if tempo.Spec.Ingestion != nil && tempo.Spec.Ingestion.OTLP != nil { if tempo.Spec.Ingestion.OTLP.GRPC != nil && tempo.Spec.Ingestion.OTLP.GRPC.Enabled { - config.Distributor.Receivers.OTLP.Protocols.GRPC = &struct{}{} + var i interface{} + config.Distributor.Receivers.OTLP.Protocols.GRPC = &i } if tempo.Spec.Ingestion.OTLP.HTTP != nil && tempo.Spec.Ingestion.OTLP.HTTP.Enabled { - config.Distributor.Receivers.OTLP.Protocols.HTTP = &struct{}{} + var i interface{} + config.Distributor.Receivers.OTLP.Protocols.HTTP = &i } } diff --git a/internal/manifests/monolithic/configmap_test.go b/internal/manifests/monolithic/configmap_test.go index a7a2fa71a..28115af73 100644 --- a/internal/manifests/monolithic/configmap_test.go +++ b/internal/manifests/monolithic/configmap_test.go @@ -86,10 +86,6 @@ storage: path: /var/tempo/wal local: path: /var/tempo/blocks -distributor: - receivers: - otlp: - protocols: {} usage_report: reporting_enabled: false `, @@ -106,6 +102,9 @@ usage_report: GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ Enabled: true, }, + HTTP: &v1alpha1.MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, }, }, expected: ` @@ -122,7 +121,8 @@ distributor: receivers: otlp: protocols: - grpc: {} + grpc: + http: usage_report: reporting_enabled: false `, From 5874547286f70948fbf06deaf841f4d705568805 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 10 Jan 2024 20:26:47 +0100 Subject: [PATCH 15/36] TestBuildAll() Signed-off-by: Andreas Gerstmayr --- internal/manifests/monolithic/build_test.go | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 internal/manifests/monolithic/build_test.go diff --git a/internal/manifests/monolithic/build_test.go b/internal/manifests/monolithic/build_test.go new file mode 100644 index 000000000..943f2a961 --- /dev/null +++ b/internal/manifests/monolithic/build_test.go @@ -0,0 +1,31 @@ +package monolithic + +import ( + "testing" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestBuildAll(t *testing.T) { + opts := Options{ + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{ + Storage: v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "memory", + }, + }, + }, + }, + } + + objects, err := BuildAll(opts) + require.NoError(t, err) + require.Len(t, objects, 3) +} From 3b86d3afe337a2a6a7961aac741edd1e7e17108d Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 10 Jan 2024 20:44:04 +0100 Subject: [PATCH 16/36] add e2e test Signed-off-by: Andreas Gerstmayr --- tests/e2e/monolithic-smoketest/01-assert.yaml | 56 +++++++++++++++++++ .../01-install-tempo.yaml | 7 +++ tests/e2e/monolithic-smoketest/03-assert.yaml | 8 +++ .../03-generate-traces.yaml | 17 ++++++ tests/e2e/monolithic-smoketest/04-assert.yaml | 8 +++ .../04-verify-traces-jaeger.yaml | 36 ++++++++++++ tests/e2e/monolithic-smoketest/05-assert.yaml | 8 +++ .../05-verify-traces-grafana.yaml | 46 +++++++++++++++ 8 files changed, 186 insertions(+) create mode 100644 tests/e2e/monolithic-smoketest/01-assert.yaml create mode 100644 tests/e2e/monolithic-smoketest/01-install-tempo.yaml create mode 100644 tests/e2e/monolithic-smoketest/03-assert.yaml create mode 100644 tests/e2e/monolithic-smoketest/03-generate-traces.yaml create mode 100644 tests/e2e/monolithic-smoketest/04-assert.yaml create mode 100644 tests/e2e/monolithic-smoketest/04-verify-traces-jaeger.yaml create mode 100644 tests/e2e/monolithic-smoketest/05-assert.yaml create mode 100644 tests/e2e/monolithic-smoketest/05-verify-traces-grafana.yaml diff --git a/tests/e2e/monolithic-smoketest/01-assert.yaml b/tests/e2e/monolithic-smoketest/01-assert.yaml new file mode 100644 index 000000000..2c50d42e4 --- /dev/null +++ b/tests/e2e/monolithic-smoketest/01-assert.yaml @@ -0,0 +1,56 @@ +apiVersion: tempo.grafana.com/v1alpha1 +kind: TempoMonolithic +metadata: + name: simplest +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: tempo-simplest + labels: + app.kubernetes.io/instance: simplest + app.kubernetes.io/managed-by: tempo-operator + app.kubernetes.io/name: tempo-monolithic +spec: + selector: + matchLabels: + app.kubernetes.io/instance: simplest + app.kubernetes.io/managed-by: tempo-operator + app.kubernetes.io/name: tempo-monolithic +status: + readyReplicas: 1 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: simplest + app.kubernetes.io/managed-by: tempo-operator + app.kubernetes.io/name: tempo-monolithic + name: tempo-simplest +spec: + ports: + - name: http + port: 3200 + protocol: TCP + targetPort: http + - name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: otlp-grpc + - name: jaeger-grpc + port: 16685 + protocol: TCP + targetPort: jaeger-grpc + - name: jaeger-ui + port: 16686 + protocol: TCP + targetPort: jaeger-ui + - name: jaeger-metrics + port: 16687 + protocol: TCP + targetPort: jaeger-metrics + selector: + app.kubernetes.io/instance: simplest + app.kubernetes.io/managed-by: tempo-operator + app.kubernetes.io/name: tempo-monolithic diff --git a/tests/e2e/monolithic-smoketest/01-install-tempo.yaml b/tests/e2e/monolithic-smoketest/01-install-tempo.yaml new file mode 100644 index 000000000..7a3a5f84d --- /dev/null +++ b/tests/e2e/monolithic-smoketest/01-install-tempo.yaml @@ -0,0 +1,7 @@ +apiVersion: tempo.grafana.com/v1alpha1 +kind: TempoMonolithic +metadata: + name: simplest +spec: + jaegerui: + enabled: true diff --git a/tests/e2e/monolithic-smoketest/03-assert.yaml b/tests/e2e/monolithic-smoketest/03-assert.yaml new file mode 100644 index 000000000..3f7323066 --- /dev/null +++ b/tests/e2e/monolithic-smoketest/03-assert.yaml @@ -0,0 +1,8 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces +status: + conditions: + - status: "True" + type: Complete diff --git a/tests/e2e/monolithic-smoketest/03-generate-traces.yaml b/tests/e2e/monolithic-smoketest/03-generate-traces.yaml new file mode 100644 index 000000000..87e2fbc76 --- /dev/null +++ b/tests/e2e/monolithic-smoketest/03-generate-traces.yaml @@ -0,0 +1,17 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.75.0 + args: + - traces + - --otlp-endpoint=tempo-simplest:4317 + - --otlp-insecure + - --traces=10 + restartPolicy: Never + backoffLimit: 4 diff --git a/tests/e2e/monolithic-smoketest/04-assert.yaml b/tests/e2e/monolithic-smoketest/04-assert.yaml new file mode 100644 index 000000000..ab9e98db3 --- /dev/null +++ b/tests/e2e/monolithic-smoketest/04-assert.yaml @@ -0,0 +1,8 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-jaeger +status: + conditions: + - status: "True" + type: Complete diff --git a/tests/e2e/monolithic-smoketest/04-verify-traces-jaeger.yaml b/tests/e2e/monolithic-smoketest/04-verify-traces-jaeger.yaml new file mode 100644 index 000000000..8c1127424 --- /dev/null +++ b/tests/e2e/monolithic-smoketest/04-verify-traces-jaeger.yaml @@ -0,0 +1,36 @@ +# Simulate Jaeger Query API requests. +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-jaeger +spec: + template: + spec: + containers: + - name: verify-traces-jaeger + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + # The query frontend must be accessible via HTTP (no mTLS) to enable connections from Grafana + curl \ + -v -G \ + http://tempo-simplest:3200/api/search \ + --data-urlencode "q={}" \ + | tee /tmp/tempo.out + num_traces=$(jq ".traces | length" /tmp/tempo.out) + if [[ "$num_traces" -ne 10 ]]; then + echo && echo "The Tempo API returned $num_traces instead of 10 traces." + exit 1 + fi + + curl -v -G http://tempo-simplest:16686/api/traces --data-urlencode "service=telemetrygen" | tee /tmp/jaeger.out + num_traces=$(jq ".data | length" /tmp/jaeger.out) + if [[ "$num_traces" -ne 10 ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 10 traces." + exit 1 + fi + restartPolicy: Never diff --git a/tests/e2e/monolithic-smoketest/05-assert.yaml b/tests/e2e/monolithic-smoketest/05-assert.yaml new file mode 100644 index 000000000..7eec01bfb --- /dev/null +++ b/tests/e2e/monolithic-smoketest/05-assert.yaml @@ -0,0 +1,8 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-grafana +status: + conditions: + - status: "True" + type: Complete diff --git a/tests/e2e/monolithic-smoketest/05-verify-traces-grafana.yaml b/tests/e2e/monolithic-smoketest/05-verify-traces-grafana.yaml new file mode 100644 index 000000000..82c56c300 --- /dev/null +++ b/tests/e2e/monolithic-smoketest/05-verify-traces-grafana.yaml @@ -0,0 +1,46 @@ +# Simulate Grafana Dashboard API requests. +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-grafana +spec: + template: + spec: + containers: + - name: verify-traces-grafana + image: registry.gitlab.com/gitlab-ci-utils/curl-jq:1.1.0 + command: + - /bin/bash + - -eux + - -c + args: + - | + # Get the current Unix timestamp for "end" time, which is the current time + end_time=$(date -u +%s) + + # Calculate "start" time by subtracting 24 hours (86400 seconds) from the "end" time + start_time=$((end_time - 86400)) + + # The query frontend must be accessible via HTTP (no mTLS) to enable connections from Grafana + + # Run the curl command and capture the HTTP status code and output in a file + response_file=$(mktemp) + http_status=$(curl -s -o "$response_file" -w "%{http_code}" "http://tempo-simplest:3200/api/search?tags=%20service.name%3D%22telemetrygen%22%20name%3D%22okey-dokey%22&limit=20&start=$start_time&end=$end_time") + + # Check the HTTP status code to detect API call failures + if [[ "$http_status" -ne 200 ]]; then + echo "API call failed with HTTP status code $http_status." + exit 1 + fi + + # Parse the JSON output from the file and check if the "traces" array is empty + output=$(cat "$response_file" | jq .) + + if [[ "$(echo "$output" | jq -r '.traces | length')" -eq 0 ]]; then + echo "The Tempo API returned 0 Traces." + exit 1 + else + echo "Traces found." + exit 0 + fi + restartPolicy: Never From 3faae98265bde66815feaf8c3193924cbe1ece9e Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 11 Jan 2024 13:44:51 +0100 Subject: [PATCH 17/36] drop defaulter webhook for TempoMonolithic Signed-off-by: Andreas Gerstmayr --- Makefile | 1 + apis/tempo/v1alpha1/tempomonolithic_types.go | 2 +- .../tempo/v1alpha1/tempomonolithic_webhook.go | 8 +++---- .../v1alpha1/tempomonolithic_webhook_test.go | 10 ++++----- apis/tempo/v1alpha1/zz_generated.deepcopy.go | 6 ++++- .../tempo-operator.clusterserviceversion.yaml | 22 +------------------ .../tempo-operator.clusterserviceversion.yaml | 22 +------------------ config/webhook/manifests.yaml | 20 ----------------- .../tempo/tempomonolithic_controller.go | 3 +++ internal/manifests/monolithic/build_test.go | 2 +- .../manifests/monolithic/configmap_test.go | 8 +++---- .../manifests/monolithic/statefulset_test.go | 6 ++--- 12 files changed, 29 insertions(+), 81 deletions(-) diff --git a/Makefile b/Makefile index d91fb91d9..073a3beb3 100644 --- a/Makefile +++ b/Makefile @@ -129,6 +129,7 @@ build: generate fmt ## Build manager binary. run: manifests generate fmt ## Run a controller from your host. # Disabled webhooks only affects local runs, not the build or in-cluster deployments. @echo -e "\033[33mWebhooks are disabled! Use the normal deployment method to enable full operator functionality.\033[0m" + kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io tempo-operator-validating-webhook-configuration ENABLE_WEBHOOKS=false go run ./main.go start .PHONY: docker-build diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index 8215d6d1f..30d1791c0 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -11,7 +11,7 @@ type TempoMonolithicSpec struct { // Storage defines the backend storage configuration // // +kubebuilder:validation:Optional - Storage MonolithicStorageSpec `json:"storage,omitempty"` + Storage *MonolithicStorageSpec `json:"storage,omitempty"` // Ingestion defines the trace ingestion configuration // diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook.go b/apis/tempo/v1alpha1/tempomonolithic_webhook.go index 683f1002d..18411e2f8 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook.go +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook.go @@ -14,15 +14,15 @@ func (r *TempoMonolithic) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -//+kubebuilder:webhook:path=/mutate-tempo-grafana-com-v1alpha1-tempomonolithic,mutating=true,failurePolicy=fail,sideEffects=None,groups=tempo.grafana.com,resources=tempomonolithics,verbs=create;update,versions=v1alpha1,name=mtempomonolithic.kb.io,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &TempoMonolithic{} - // Default implements webhook.Defaulter so a webhook will be registered for the type. func (r *TempoMonolithic) Default() { log := ctrl.Log.WithName("tempomonolithic-webhook") log.V(1).Info("running defaulter webhook", "name", r.Name) + if r.Spec.Storage == nil { + r.Spec.Storage = &MonolithicStorageSpec{} + } + if r.Spec.Storage.Traces.Backend == "" { r.Spec.Storage.Traces.Backend = MonolithicTracesStorageBackendMemory } diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go b/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go index f0f4a7668..49ae4d72e 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go @@ -19,7 +19,7 @@ func TestMonolithicDefault(t *testing.T) { }, expected: &TempoMonolithic{ Spec: TempoMonolithicSpec{ - Storage: MonolithicStorageSpec{ + Storage: &MonolithicStorageSpec{ Traces: MonolithicTracesStorageSpec{ Backend: "memory", }, @@ -38,7 +38,7 @@ func TestMonolithicDefault(t *testing.T) { name: "set default values for PV", input: &TempoMonolithic{ Spec: TempoMonolithicSpec{ - Storage: MonolithicStorageSpec{ + Storage: &MonolithicStorageSpec{ Traces: MonolithicTracesStorageSpec{ Backend: "pv", }, @@ -47,7 +47,7 @@ func TestMonolithicDefault(t *testing.T) { }, expected: &TempoMonolithic{ Spec: TempoMonolithicSpec{ - Storage: MonolithicStorageSpec{ + Storage: &MonolithicStorageSpec{ Traces: MonolithicTracesStorageSpec{ Backend: "pv", WAL: &MonolithicTracesStorageWALSpec{ @@ -72,7 +72,7 @@ func TestMonolithicDefault(t *testing.T) { name: "do not change already set values", input: &TempoMonolithic{ Spec: TempoMonolithicSpec{ - Storage: MonolithicStorageSpec{ + Storage: &MonolithicStorageSpec{ Traces: MonolithicTracesStorageSpec{ Backend: "s3", WAL: &MonolithicTracesStorageWALSpec{ @@ -92,7 +92,7 @@ func TestMonolithicDefault(t *testing.T) { }, expected: &TempoMonolithic{ Spec: TempoMonolithicSpec{ - Storage: MonolithicStorageSpec{ + Storage: &MonolithicStorageSpec{ Traces: MonolithicTracesStorageSpec{ Backend: "s3", WAL: &MonolithicTracesStorageWALSpec{ diff --git a/apis/tempo/v1alpha1/zz_generated.deepcopy.go b/apis/tempo/v1alpha1/zz_generated.deepcopy.go index 48cbc8993..5807ebdbf 100644 --- a/apis/tempo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/tempo/v1alpha1/zz_generated.deepcopy.go @@ -1147,7 +1147,11 @@ func (in *TempoMonolithicList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TempoMonolithicSpec) DeepCopyInto(out *TempoMonolithicSpec) { *out = *in - in.Storage.DeepCopyInto(&out.Storage) + if in.Storage != nil { + in, out := &in.Storage, &out.Storage + *out = new(MonolithicStorageSpec) + (*in).DeepCopyInto(*out) + } if in.Ingestion != nil { in, out := &in.Ingestion, &out.Ingestion *out = new(MonolithicIngestionSpec) diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index 6e020dd7e..ee830dea9 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2024-01-08T18:44:24Z" + createdAt: "2024-01-10T20:43:08Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -1010,26 +1010,6 @@ spec: name: tempo-gateway-opa version: 0.6.0 webhookdefinitions: - - admissionReviewVersions: - - v1 - containerPort: 443 - deploymentName: tempo-operator-controller - failurePolicy: Fail - generateName: mtempomonolithic.kb.io - rules: - - apiGroups: - - tempo.grafana.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - tempomonolithics - sideEffects: None - targetPort: 9443 - type: MutatingAdmissionWebhook - webhookPath: /mutate-tempo-grafana-com-v1alpha1-tempomonolithic - admissionReviewVersions: - v1 containerPort: 443 diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index b07d5fc64..13e58e248 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2024-01-08T18:44:22Z" + createdAt: "2024-01-10T20:43:07Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -1021,26 +1021,6 @@ spec: name: tempo-gateway-opa version: 0.6.0 webhookdefinitions: - - admissionReviewVersions: - - v1 - containerPort: 443 - deploymentName: tempo-operator-controller - failurePolicy: Fail - generateName: mtempomonolithic.kb.io - rules: - - apiGroups: - - tempo.grafana.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - tempomonolithics - sideEffects: None - targetPort: 9443 - type: MutatingAdmissionWebhook - webhookPath: /mutate-tempo-grafana-com-v1alpha1-tempomonolithic - admissionReviewVersions: - v1 containerPort: 443 diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 5ec8c2305..b51b201fb 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -5,26 +5,6 @@ metadata: creationTimestamp: null name: mutating-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-tempo-grafana-com-v1alpha1-tempomonolithic - failurePolicy: Fail - name: mtempomonolithic.kb.io - rules: - - apiGroups: - - tempo.grafana.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - tempomonolithics - sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/controllers/tempo/tempomonolithic_controller.go b/controllers/tempo/tempomonolithic_controller.go index a5b6f8a59..9f2240c5a 100644 --- a/controllers/tempo/tempomonolithic_controller.go +++ b/controllers/tempo/tempomonolithic_controller.go @@ -55,6 +55,9 @@ func (r *TempoMonolithicReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, nil } + // apply defaults + tempo.Default() + if tempo.Spec.Management == v1alpha1.ManagementStateUnmanaged { log.Info("Skipping reconciliation for unmanaged TempoMonolithic resource", "name", req.String()) return ctrl.Result{}, nil diff --git a/internal/manifests/monolithic/build_test.go b/internal/manifests/monolithic/build_test.go index 943f2a961..43844425a 100644 --- a/internal/manifests/monolithic/build_test.go +++ b/internal/manifests/monolithic/build_test.go @@ -16,7 +16,7 @@ func TestBuildAll(t *testing.T) { Namespace: "default", }, Spec: v1alpha1.TempoMonolithicSpec{ - Storage: v1alpha1.MonolithicStorageSpec{ + Storage: &v1alpha1.MonolithicStorageSpec{ Traces: v1alpha1.MonolithicTracesStorageSpec{ Backend: "memory", }, diff --git a/internal/manifests/monolithic/configmap_test.go b/internal/manifests/monolithic/configmap_test.go index 28115af73..ffc81771e 100644 --- a/internal/manifests/monolithic/configmap_test.go +++ b/internal/manifests/monolithic/configmap_test.go @@ -24,7 +24,7 @@ func TestBuildConfigMap(t *testing.T) { Namespace: "default", }, Spec: v1alpha1.TempoMonolithicSpec{ - Storage: v1alpha1.MonolithicStorageSpec{ + Storage: &v1alpha1.MonolithicStorageSpec{ Traces: v1alpha1.MonolithicTracesStorageSpec{ Backend: "memory", }, @@ -65,13 +65,13 @@ func TestBuildConfig(t *testing.T) { tests := []struct { name string - storage v1alpha1.MonolithicStorageSpec + storage *v1alpha1.MonolithicStorageSpec ingestion *v1alpha1.MonolithicIngestionSpec expected string }{ { name: "memory storage", - storage: v1alpha1.MonolithicStorageSpec{ + storage: &v1alpha1.MonolithicStorageSpec{ Traces: v1alpha1.MonolithicTracesStorageSpec{ Backend: "memory", }, @@ -92,7 +92,7 @@ usage_report: }, { name: "PV storage with OTLP/gRPC and OTLP/HTTP", - storage: v1alpha1.MonolithicStorageSpec{ + storage: &v1alpha1.MonolithicStorageSpec{ Traces: v1alpha1.MonolithicTracesStorageSpec{ Backend: "pv", }, diff --git a/internal/manifests/monolithic/statefulset_test.go b/internal/manifests/monolithic/statefulset_test.go index dc4ab81f1..897129903 100644 --- a/internal/manifests/monolithic/statefulset_test.go +++ b/internal/manifests/monolithic/statefulset_test.go @@ -33,7 +33,7 @@ func TestStatefulsetMemoryStorage(t *testing.T) { Namespace: "default", }, Spec: v1alpha1.TempoMonolithicSpec{ - Storage: v1alpha1.MonolithicStorageSpec{ + Storage: &v1alpha1.MonolithicStorageSpec{ Traces: v1alpha1.MonolithicTracesStorageSpec{ Backend: "memory", }, @@ -163,7 +163,7 @@ func TestStatefulsetPVStorage(t *testing.T) { Namespace: "default", }, Spec: v1alpha1.TempoMonolithicSpec{ - Storage: v1alpha1.MonolithicStorageSpec{ + Storage: &v1alpha1.MonolithicStorageSpec{ Traces: v1alpha1.MonolithicTracesStorageSpec{ Backend: "pv", WAL: &v1alpha1.MonolithicTracesStorageWALSpec{ @@ -254,7 +254,7 @@ func TestStatefulsetPorts(t *testing.T) { Namespace: "default", }, Spec: v1alpha1.TempoMonolithicSpec{ - Storage: v1alpha1.MonolithicStorageSpec{ + Storage: &v1alpha1.MonolithicStorageSpec{ Traces: v1alpha1.MonolithicTracesStorageSpec{ Backend: "memory", }, From a027746bd035d84fbccdcdc859cf2e233b85fb99 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 11 Jan 2024 15:01:32 +0100 Subject: [PATCH 18/36] delete operator namespace and webhooks in make run Signed-off-by: Andreas Gerstmayr --- Makefile | 7 ++++++- env | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 env diff --git a/Makefile b/Makefile index 073a3beb3..4cb4ebc94 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +include env +export $(shell sed 's/=.*//' env) + # Current Operator version VERSION_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION_PKG ?= github.com/grafana/tempo-operator/internal/version @@ -129,7 +132,9 @@ build: generate fmt ## Build manager binary. run: manifests generate fmt ## Run a controller from your host. # Disabled webhooks only affects local runs, not the build or in-cluster deployments. @echo -e "\033[33mWebhooks are disabled! Use the normal deployment method to enable full operator functionality.\033[0m" - kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io tempo-operator-validating-webhook-configuration + -kubectl delete ns $(OPERATOR_NAMESPACE) + -kubectl delete mutatingwebhookconfigurations.admissionregistration.k8s.io tempo-operator-mutating-webhook-configuration + -kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io tempo-operator-validating-webhook-configuration ENABLE_WEBHOOKS=false go run ./main.go start .PHONY: docker-build diff --git a/env b/env new file mode 100644 index 000000000..94de74570 --- /dev/null +++ b/env @@ -0,0 +1,4 @@ +RELATED_IMAGE_TEMPO=docker.io/grafana/tempo:2.3.0 +RELATED_IMAGE_TEMPO_QUERY=docker.io/grafana/tempo-query:2.3.0 +RELATED_IMAGE_TEMPO_GATEWAY=quay.io/observatorium/api:main-2023-11-20-81f8fdf +RELATED_IMAGE_TEMPO_GATEWAY_OPA=quay.io/observatorium/opa-openshift:main-2023-10-13-13d8960 From 19d6e07e9763158522ec45060400a2c1764d074a Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 11 Jan 2024 17:36:26 +0100 Subject: [PATCH 19/36] change the way to include .env file Signed-off-by: Andreas Gerstmayr --- env => .env | 0 Makefile | 5 +---- 2 files changed, 1 insertion(+), 4 deletions(-) rename env => .env (100%) mode change 100644 => 100755 diff --git a/env b/.env old mode 100644 new mode 100755 similarity index 100% rename from env rename to .env diff --git a/Makefile b/Makefile index 87e6633c5..8ead9162f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,3 @@ -include env -export $(shell sed 's/=.*//' env) - # Current Operator version VERSION_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION_PKG ?= github.com/grafana/tempo-operator/internal/version @@ -135,7 +132,7 @@ run: manifests generate fmt ## Run a controller from your host. -kubectl delete ns $(OPERATOR_NAMESPACE) -kubectl delete mutatingwebhookconfigurations.admissionregistration.k8s.io tempo-operator-mutating-webhook-configuration -kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io tempo-operator-validating-webhook-configuration - ENABLE_WEBHOOKS=false go run ./main.go start + set -a && . .env && ENABLE_WEBHOOKS=false go run ./main.go start .PHONY: docker-build docker-build: ## Build docker image with the manager. From f29369f82ad42ebe7aa3dda03b5cd76f7f815f12 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 11 Jan 2024 17:55:13 +0100 Subject: [PATCH 20/36] rebuild bundle, fix linter Signed-off-by: Andreas Gerstmayr --- .../tempo-operator.clusterserviceversion.yaml | 20 +++++++++++-------- .../tempo-operator.clusterserviceversion.yaml | 20 +++++++++++-------- docs/spec/tempomonolithic.yaml | 5 ++--- internal/manifests/monolithic/build_test.go | 3 ++- .../manifests/monolithic/configmap_test.go | 5 +++-- internal/manifests/monolithic/service_test.go | 3 ++- .../manifests/monolithic/statefulset_test.go | 7 ++++--- 7 files changed, 37 insertions(+), 26 deletions(-) diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index ee830dea9..2566a66d3 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2024-01-10T20:43:08Z" + createdAt: "2024-01-11T16:44:31Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -65,7 +65,7 @@ metadata: operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/grafana/tempo-operator support: Grafana Tempo Operator SIG - name: tempo-operator.v0.6.0 + name: tempo-operator.v0.7.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -106,6 +106,10 @@ spec: name: "" version: v1 specDescriptors: + - displayName: Extra Configurations + path: extraConfig + - displayName: Tempo Extra Configurations + path: extraConfig.tempo - description: HashRing defines the spec for the distributed hash ring configuration. displayName: Hash Ring path: hashRing @@ -856,14 +860,14 @@ spec: - --config=controller_manager_config.yaml env: - name: RELATED_IMAGE_TEMPO - value: docker.io/grafana/tempo:2.3.0 + value: docker.io/grafana/tempo:2.3.1 - name: RELATED_IMAGE_TEMPO_QUERY - value: docker.io/grafana/tempo-query:2.3.0 + value: docker.io/grafana/tempo-query:2.3.1 - name: RELATED_IMAGE_TEMPO_GATEWAY value: quay.io/observatorium/api:main-2023-11-20-81f8fdf - name: RELATED_IMAGE_TEMPO_GATEWAY_OPA value: quay.io/observatorium/opa-openshift:main-2023-10-13-13d8960 - image: ghcr.io/grafana/tempo-operator/tempo-operator:v0.6.0 + image: ghcr.io/grafana/tempo-operator/tempo-operator:v0.7.0 livenessProbe: httpGet: path: /healthz @@ -1000,15 +1004,15 @@ spec: provider: name: Grafana Tempo Operator SIG relatedImages: - - image: docker.io/grafana/tempo:2.3.0 + - image: docker.io/grafana/tempo:2.3.1 name: tempo - - image: docker.io/grafana/tempo-query:2.3.0 + - image: docker.io/grafana/tempo-query:2.3.1 name: tempo-query - image: quay.io/observatorium/api:main-2023-11-20-81f8fdf name: tempo-gateway - image: quay.io/observatorium/opa-openshift:main-2023-10-13-13d8960 name: tempo-gateway-opa - version: 0.6.0 + version: 0.7.0 webhookdefinitions: - admissionReviewVersions: - v1 diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index 13e58e248..f08a89369 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2024-01-10T20:43:07Z" + createdAt: "2024-01-11T16:44:29Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -65,7 +65,7 @@ metadata: operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/grafana/tempo-operator support: Grafana Tempo Operator SIG - name: tempo-operator.v0.6.0 + name: tempo-operator.v0.7.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -106,6 +106,10 @@ spec: name: "" version: v1 specDescriptors: + - displayName: Extra Configurations + path: extraConfig + - displayName: Tempo Extra Configurations + path: extraConfig.tempo - description: HashRing defines the spec for the distributed hash ring configuration. displayName: Hash Ring path: hashRing @@ -856,14 +860,14 @@ spec: - --config=controller_manager_config.yaml env: - name: RELATED_IMAGE_TEMPO - value: docker.io/grafana/tempo:2.3.0 + value: docker.io/grafana/tempo:2.3.1 - name: RELATED_IMAGE_TEMPO_QUERY - value: docker.io/grafana/tempo-query:2.3.0 + value: docker.io/grafana/tempo-query:2.3.1 - name: RELATED_IMAGE_TEMPO_GATEWAY value: quay.io/observatorium/api:main-2023-11-20-81f8fdf - name: RELATED_IMAGE_TEMPO_GATEWAY_OPA value: quay.io/observatorium/opa-openshift:main-2023-10-13-13d8960 - image: ghcr.io/grafana/tempo-operator/tempo-operator:v0.6.0 + image: ghcr.io/grafana/tempo-operator/tempo-operator:v0.7.0 livenessProbe: httpGet: path: /healthz @@ -1011,15 +1015,15 @@ spec: provider: name: Grafana Tempo Operator SIG relatedImages: - - image: docker.io/grafana/tempo:2.3.0 + - image: docker.io/grafana/tempo:2.3.1 name: tempo - - image: docker.io/grafana/tempo-query:2.3.0 + - image: docker.io/grafana/tempo-query:2.3.1 name: tempo-query - image: quay.io/observatorium/api:main-2023-11-20-81f8fdf name: tempo-gateway - image: quay.io/observatorium/opa-openshift:main-2023-10-13-13d8960 name: tempo-gateway-opa - version: 0.6.0 + version: 0.7.0 webhookdefinitions: - admissionReviewVersions: - v1 diff --git a/docs/spec/tempomonolithic.yaml b/docs/spec/tempomonolithic.yaml index 6a53a8c77..bdbf9c1fa 100644 --- a/docs/spec/tempomonolithic.yaml +++ b/docs/spec/tempomonolithic.yaml @@ -8,9 +8,9 @@ spec: # TempoMonolithicSpec defines the desir ingestion: # Ingestion defines the trace ingestion configuration otlp: # OTLP defines the ingestion configuration for OTLP grpc: # GRPC defines the OTLP/gRPC configuration - enabled: false # Enabled defines if the OTLP over gRPC is enabled + enabled: false # Enabled defines if OTLP over gRPC is enabled http: # HTTP defines the OTLP/HTTP configuration - enabled: false # Enabled defines if the OTLP over HTTP is enabled + enabled: false # Enabled defines if OTLP over HTTP is enabled tls: # TLS defines the TLS configuration for ingestion ca: "" # CA defines the name of a secret containing the CA certificate cert: "" # Cert defines the name of a secret containing the TLS certificate and private key @@ -33,7 +33,6 @@ spec: # TempoMonolithicSpec defines the desir backend: "" # Backend defines the backend for storing traces pv: # PV defines the Persistent Volume configuration size: 1Gi # Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. - secret: "" # Secret defines name of a secret containing the credentials for accessing the specified backend storage wal: # WAL defines the write-ahead logging (WAL) configuration size: 1Gi # Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. status: # TempoMonolithicStatus defines the observed state of TempoMonolithic. diff --git a/internal/manifests/monolithic/build_test.go b/internal/manifests/monolithic/build_test.go index 43844425a..781ecc78e 100644 --- a/internal/manifests/monolithic/build_test.go +++ b/internal/manifests/monolithic/build_test.go @@ -3,9 +3,10 @@ package monolithic import ( "testing" - "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" ) func TestBuildAll(t *testing.T) { diff --git a/internal/manifests/monolithic/configmap_test.go b/internal/manifests/monolithic/configmap_test.go index ffc81771e..ec24d1c17 100644 --- a/internal/manifests/monolithic/configmap_test.go +++ b/internal/manifests/monolithic/configmap_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" - configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" - "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" ) func TestBuildConfigMap(t *testing.T) { diff --git a/internal/manifests/monolithic/service_test.go b/internal/manifests/monolithic/service_test.go index fdfb76589..9e2bbfe7a 100644 --- a/internal/manifests/monolithic/service_test.go +++ b/internal/manifests/monolithic/service_test.go @@ -3,11 +3,12 @@ package monolithic import ( "testing" - "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" ) func TestBuildTempoService(t *testing.T) { diff --git a/internal/manifests/monolithic/statefulset_test.go b/internal/manifests/monolithic/statefulset_test.go index 897129903..53698e8fd 100644 --- a/internal/manifests/monolithic/statefulset_test.go +++ b/internal/manifests/monolithic/statefulset_test.go @@ -3,9 +3,6 @@ package monolithic import ( "testing" - configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" - "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" - "github.com/grafana/tempo-operator/internal/manifests/manifestutils" "github.com/operator-framework/operator-lib/proxy" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" @@ -13,6 +10,10 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" + + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + "github.com/grafana/tempo-operator/internal/manifests/manifestutils" ) var ( From f0c9ffc1192122de3edd9fec22419e9cbb5126a8 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 11 Jan 2024 18:39:38 +0100 Subject: [PATCH 21/36] fix typo in jaeger-grpc port name Signed-off-by: Andreas Gerstmayr --- tests/e2e-openshift/monitoring/02-assert.yaml | 4 ++-- tests/e2e/compatibility/01-assert.yaml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/e2e-openshift/monitoring/02-assert.yaml b/tests/e2e-openshift/monitoring/02-assert.yaml index 8da18ae97..d221c4bc9 100644 --- a/tests/e2e-openshift/monitoring/02-assert.yaml +++ b/tests/e2e-openshift/monitoring/02-assert.yaml @@ -75,10 +75,10 @@ spec: port: 9095 protocol: TCP targetPort: grpc - - name: jaeger-gprc + - name: jaeger-grpc port: 16685 protocol: TCP - targetPort: jaeger-gprc + targetPort: jaeger-grpc - name: jaeger-ui port: 16686 protocol: TCP diff --git a/tests/e2e/compatibility/01-assert.yaml b/tests/e2e/compatibility/01-assert.yaml index a7b1fdea0..56d28738b 100644 --- a/tests/e2e/compatibility/01-assert.yaml +++ b/tests/e2e/compatibility/01-assert.yaml @@ -291,10 +291,10 @@ spec: port: 9095 protocol: TCP targetPort: grpc - - name: jaeger-gprc + - name: jaeger-grpc port: 16685 protocol: TCP - targetPort: jaeger-gprc + targetPort: jaeger-grpc - name: jaeger-ui port: 16686 protocol: TCP @@ -334,10 +334,10 @@ spec: port: 9096 protocol: TCP targetPort: grpclb - - name: jaeger-gprc + - name: jaeger-grpc port: 16685 protocol: TCP - targetPort: jaeger-gprc + targetPort: jaeger-grpc - name: jaeger-ui port: 16686 protocol: TCP From c0343f123c0c037277bf9ff3b346e8bea5bddda2 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Mon, 15 Jan 2024 19:17:32 +0100 Subject: [PATCH 22/36] add warning when overriding tempo config Signed-off-by: Andreas Gerstmayr --- Makefile | 2 +- .../tempo/v1alpha1/tempomonolithic_webhook.go | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 8ead9162f..4d53838c0 100644 --- a/Makefile +++ b/Makefile @@ -132,7 +132,7 @@ run: manifests generate fmt ## Run a controller from your host. -kubectl delete ns $(OPERATOR_NAMESPACE) -kubectl delete mutatingwebhookconfigurations.admissionregistration.k8s.io tempo-operator-mutating-webhook-configuration -kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io tempo-operator-validating-webhook-configuration - set -a && . .env && ENABLE_WEBHOOKS=false go run ./main.go start + set -a && . .env && ENABLE_WEBHOOKS=false go run ./main.go --zap-log-level=info start .PHONY: docker-build docker-build: ## Build docker image with the manager. diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook.go b/apis/tempo/v1alpha1/tempomonolithic_webhook.go index 18411e2f8..3947202d1 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook.go +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook.go @@ -1,7 +1,9 @@ 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" @@ -16,9 +18,6 @@ func (r *TempoMonolithic) SetupWebhookWithManager(mgr ctrl.Manager) error { // Default implements webhook.Defaulter so a webhook will be registered for the type. func (r *TempoMonolithic) Default() { - log := ctrl.Log.WithName("tempomonolithic-webhook") - log.V(1).Info("running defaulter webhook", "name", r.Name) - if r.Spec.Storage == nil { r.Spec.Storage = &MonolithicStorageSpec{} } @@ -70,8 +69,19 @@ func (r *TempoMonolithic) ValidateDelete() (admission.Warnings, error) { return r.validate() } -func (r *TempoMonolithic) validate() (admission.Warnings, error) { +func (tempo *TempoMonolithic) validate() (admission.Warnings, error) { log := ctrl.Log.WithName("tempomonolithic-webhook") - log.V(1).Info("running validating webhook", "name", r.Name) - return nil, nil + 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) } From 0c9b6e66a76d2acb395b78b390adca82df6e1a2d Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Tue, 16 Jan 2024 12:53:03 +0100 Subject: [PATCH 23/36] specify default value of storage.traces.backend Signed-off-by: Andreas Gerstmayr --- apis/tempo/v1alpha1/tempomonolithic_types.go | 1 + .../tempo-operator.clusterserviceversion.yaml | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 1 + .../tempo-operator.clusterserviceversion.yaml | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 1 + .../tempo.grafana.com_tempomonolithics.yaml | 1 + docs/spec/tempomonolithic.yaml | 38 ------------------- 7 files changed, 6 insertions(+), 40 deletions(-) delete mode 100644 docs/spec/tempomonolithic.yaml diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index 30d1791c0..35da682b5 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -52,6 +52,7 @@ type MonolithicTracesStorageSpec struct { // Backend defines the backend for storing traces // // +kubebuilder:validation:Required + // +kubebuilder:default=memory Backend MonolithicTracesStorageBackend `json:"backend"` // WAL defines the write-ahead logging (WAL) configuration diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index 2566a66d3..b92b974cf 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2024-01-11T16:44:31Z" + createdAt: "2024-01-16T11:52:12Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml index a725edd00..f338fcd21 100644 --- a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -170,6 +170,7 @@ spec: for traces properties: backend: + default: memory description: Backend defines the backend for storing traces enum: - memory diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index f08a89369..214db9f84 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2024-01-11T16:44:29Z" + createdAt: "2024-01-16T11:52:11Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml index a725edd00..f338fcd21 100644 --- a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -170,6 +170,7 @@ spec: for traces properties: backend: + default: memory description: Backend defines the backend for storing traces enum: - memory diff --git a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml index 9dc254f55..c4209d910 100644 --- a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml +++ b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml @@ -167,6 +167,7 @@ spec: for traces properties: backend: + default: memory description: Backend defines the backend for storing traces enum: - memory diff --git a/docs/spec/tempomonolithic.yaml b/docs/spec/tempomonolithic.yaml deleted file mode 100644 index bdbf9c1fa..000000000 --- a/docs/spec/tempomonolithic.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: tempo.grafana.com/v1alpha1 # APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources -kind: TempoMonolithic # Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds -metadata: - name: example -spec: # TempoMonolithicSpec defines the desired state of TempoMonolithic. - extraConfig: # ExtraConfig defines any extra (overlay) configuration for components - tempo: {} # Tempo defines any extra Tempo configuration, which will be merged with the operator's generated Tempo configuration - ingestion: # Ingestion defines the trace ingestion configuration - otlp: # OTLP defines the ingestion configuration for OTLP - grpc: # GRPC defines the OTLP/gRPC configuration - enabled: false # Enabled defines if OTLP over gRPC is enabled - http: # HTTP defines the OTLP/HTTP configuration - enabled: false # Enabled defines if OTLP over HTTP is enabled - tls: # TLS defines the TLS configuration for ingestion - ca: "" # CA defines the name of a secret containing the CA certificate - cert: "" # Cert defines the name of a secret containing the TLS certificate and private key - enabled: false # Enabled defines if TLS is enabled for ingestion - jaegerui: # JaegerUI defines the Jaeger UI configuration - enabled: false # Enabled defines if the Jaeger UI should be enabled - ingress: # Ingress defines the ingress configuration for Jaeger UI - enabled: false # Enabled defines if an Ingress object should be created for Jaeger UI - route: # Route defines the route configuration for Jaeger UI - enabled: false # Enabled defines if a Route object should be created for Jaeger UI - management: "" # ManagementState defines whether this instance is managed by the operator or self-managed - observability: # Observability defines observability configuration for the Tempo deployment - metrics: # Metrics defines the metrics configuration of the Tempo deployment - prometheusRules: # ServiceMonitors defines the PrometheusRule configuration - enabled: false # Enabled defines if the operator should create PrometheusRules for this Tempo deployment - serviceMonitors: # ServiceMonitors defines the ServiceMonitor configuration - enabled: false # Enabled defines if the operator should create ServiceMonitors for this Tempo deployment - storage: # Storage defines the backend storage configuration - traces: # Traces defines the backend storage configuration for traces - backend: "" # Backend defines the backend for storing traces - pv: # PV defines the Persistent Volume configuration - size: 1Gi # Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. - wal: # WAL defines the write-ahead logging (WAL) configuration - size: 1Gi # Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. -status: # TempoMonolithicStatus defines the observed state of TempoMonolithic. From fd37ff8dd5d1c8d53b23d8ca1316322f7daed6f5 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 17 Jan 2024 15:13:43 +0100 Subject: [PATCH 24/36] update storage docs Signed-off-by: Andreas Gerstmayr --- apis/tempo/v1alpha1/tempomonolithic_types.go | 6 +++--- .../manifests/tempo-operator.clusterserviceversion.yaml | 2 +- .../manifests/tempo.grafana.com_tempomonolithics.yaml | 3 ++- .../manifests/tempo-operator.clusterserviceversion.yaml | 2 +- .../manifests/tempo.grafana.com_tempomonolithics.yaml | 3 ++- config/crd/bases/tempo.grafana.com_tempomonolithics.yaml | 3 ++- docs/operator/api.md | 6 +++--- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index 35da682b5..1043b8d5b 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -49,7 +49,7 @@ type MonolithicStorageSpec struct { // MonolithicTracesStorageSpec defines the traces storage for the Tempo deployment. type MonolithicTracesStorageSpec struct { - // Backend defines the backend for storing traces + // Backend defines the backend for storing traces. Default: memory // // +kubebuilder:validation:Required // +kubebuilder:default=memory @@ -72,9 +72,9 @@ type MonolithicTracesStorageSpec struct { type MonolithicTracesStorageBackend string const ( - // MonolithicTracesStorageBackendMemory specifies a in-memory storage backend. + // MonolithicTracesStorageBackendMemory defines storing traces in a tmpfs (in-memory filesystem). MonolithicTracesStorageBackendMemory MonolithicTracesStorageBackend = "memory" - // MonolithicTracesStorageBackendPersistentVolume specifies a Persistent Volume storage backend. + // MonolithicTracesStorageBackendPersistentVolume defines storing traces in a Persistent Volume. MonolithicTracesStorageBackendPersistentVolume MonolithicTracesStorageBackend = "pv" ) diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index b92b974cf..ac82af739 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2024-01-16T11:52:12Z" + createdAt: "2024-01-17T14:13:24Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml index f338fcd21..3e5a76f3a 100644 --- a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -171,7 +171,8 @@ spec: properties: backend: default: memory - description: Backend defines the backend for storing traces + description: 'Backend defines the backend for storing traces. + Default: memory' enum: - memory - pv diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index 214db9f84..80bd368c3 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator - createdAt: "2024-01-16T11:52:11Z" + createdAt: "2024-01-17T14:13:23Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml index f338fcd21..3e5a76f3a 100644 --- a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -171,7 +171,8 @@ spec: properties: backend: default: memory - description: Backend defines the backend for storing traces + description: 'Backend defines the backend for storing traces. + Default: memory' enum: - memory - pv diff --git a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml index c4209d910..8f9be083b 100644 --- a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml +++ b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml @@ -168,7 +168,8 @@ spec: properties: backend: default: memory - description: Backend defines the backend for storing traces + description: 'Backend defines the backend for storing traces. + Default: memory' enum: - memory - pv diff --git a/docs/operator/api.md b/docs/operator/api.md index 7b3d6db0d..9c2cc921a 100644 --- a/docs/operator/api.md +++ b/docs/operator/api.md @@ -2588,12 +2588,12 @@ MonolithicTracesStorageSpec

"memory"

-

MonolithicTracesStorageBackendMemory specifies a in-memory storage backend.

+

MonolithicTracesStorageBackendMemory defines storing traces in a tmpfs (in-memory filesystem).

"pv"

-

MonolithicTracesStorageBackendPersistentVolume specifies a Persistent Volume storage backend.

+

MonolithicTracesStorageBackendPersistentVolume defines storing traces in a Persistent Volume.

@@ -2703,7 +2703,7 @@ MonolithicTracesStorageBackend -

Backend defines the backend for storing traces

+

Backend defines the backend for storing traces. Default: memory

From 754486e33aa298b80d7433253dbcf08f823b5d0b Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 17 Jan 2024 18:25:38 +0100 Subject: [PATCH 25/36] drop .env file Signed-off-by: Andreas Gerstmayr --- .env | 4 ---- 1 file changed, 4 deletions(-) delete mode 100755 .env diff --git a/.env b/.env deleted file mode 100755 index 94de74570..000000000 --- a/.env +++ /dev/null @@ -1,4 +0,0 @@ -RELATED_IMAGE_TEMPO=docker.io/grafana/tempo:2.3.0 -RELATED_IMAGE_TEMPO_QUERY=docker.io/grafana/tempo-query:2.3.0 -RELATED_IMAGE_TEMPO_GATEWAY=quay.io/observatorium/api:main-2023-11-20-81f8fdf -RELATED_IMAGE_TEMPO_GATEWAY_OPA=quay.io/observatorium/opa-openshift:main-2023-10-13-13d8960 From 3f57be48dda5d823d3ccdc8a74b55b67af2b5c97 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 18 Jan 2024 13:26:48 +0100 Subject: [PATCH 26/36] reconcile: retry on conflict Signed-off-by: Andreas Gerstmayr --- controllers/tempo/common.go | 10 +++++++++- controllers/tempo/tempomonolithic_controller.go | 11 +---------- internal/manifests/monolithic/build.go | 2 +- internal/manifests/monolithic/service.go | 8 ++++---- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/controllers/tempo/common.go b/controllers/tempo/common.go index 801ae73c2..9806ce3db 100644 --- a/controllers/tempo/common.go +++ b/controllers/tempo/common.go @@ -10,8 +10,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/util/retry" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/grafana/tempo-operator/internal/manifests" ) @@ -56,7 +58,13 @@ func reconcileManagedObjects( desired := obj.DeepCopyObject().(client.Object) mutateFn := manifests.MutateFuncFor(obj, desired) - op, err := ctrl.CreateOrUpdate(ctx, k8sclient, obj, mutateFn) + + var op controllerutil.OperationResult + err := retry.RetryOnConflict(retry.DefaultRetry, func() error { + var err error + op, err = ctrl.CreateOrUpdate(ctx, k8sclient, obj, mutateFn) + return err + }) var immutableErr *manifests.ImmutableErr if err != nil && errors.As(err, &immutableErr) { diff --git a/controllers/tempo/tempomonolithic_controller.go b/controllers/tempo/tempomonolithic_controller.go index 9f2240c5a..0e3492d1d 100644 --- a/controllers/tempo/tempomonolithic_controller.go +++ b/controllers/tempo/tempomonolithic_controller.go @@ -94,17 +94,8 @@ func (r *TempoMonolithicReconciler) getOwnedObjects(ctx context.Context, tempo v // Add all resources where the operator can conditionally create an object. // For example, Ingress and Route can be enabled or disabled in the CR. - serviceList := &corev1.ServiceList{} - err := r.List(ctx, serviceList, listOps) - if err != nil { - return nil, fmt.Errorf("error listing services: %w", err) - } - for i := range serviceList.Items { - ownedObjects[serviceList.Items[i].GetUID()] = &serviceList.Items[i] - } - ingressList := &networkingv1.IngressList{} - err = r.List(ctx, ingressList, listOps) + err := r.List(ctx, ingressList, listOps) if err != nil { return nil, fmt.Errorf("error listing ingress: %w", err) } diff --git a/internal/manifests/monolithic/build.go b/internal/manifests/monolithic/build.go index df13c49d5..4a2662772 100644 --- a/internal/manifests/monolithic/build.go +++ b/internal/manifests/monolithic/build.go @@ -6,7 +6,7 @@ import ( // BuildAll generates all manifests. func BuildAll(opts Options) ([]client.Object, error) { - var manifests []client.Object + manifests := []client.Object{} configMap, configChecksum, err := BuildConfigMap(opts) if err != nil { diff --git a/internal/manifests/monolithic/service.go b/internal/manifests/monolithic/service.go index 4a3a2d202..6cdeda695 100644 --- a/internal/manifests/monolithic/service.go +++ b/internal/manifests/monolithic/service.go @@ -13,7 +13,7 @@ import ( // BuildTempoService creates the service for a monolithic deployment. func BuildTempoService(opts Options) *corev1.Service { tempo := opts.Tempo - labels := Labels(opts.Tempo.Name) + labels := Labels(tempo.Name) ports := []corev1.ServicePort{ { Name: manifestutils.HttpPortName, @@ -43,7 +43,7 @@ func BuildTempoService(opts Options) *corev1.Service { } } - if opts.Tempo.Spec.JaegerUI != nil && opts.Tempo.Spec.JaegerUI.Enabled { + if tempo.Spec.JaegerUI != nil && tempo.Spec.JaegerUI.Enabled { ports = append(ports, []corev1.ServicePort{ { Name: manifestutils.JaegerGRPCQuery, @@ -69,8 +69,8 @@ func BuildTempoService(opts Options) *corev1.Service { Kind: "Service", }, ObjectMeta: metav1.ObjectMeta{ - Name: naming.Name("", opts.Tempo.Name), - Namespace: opts.Tempo.Namespace, + Name: naming.Name("", tempo.Name), + Namespace: tempo.Namespace, Labels: labels, }, Spec: corev1.ServiceSpec{ From 19ca826da44fa1755298d0833fd4a2e775162d0d Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 18 Jan 2024 16:12:09 +0100 Subject: [PATCH 27/36] update changelog, set size defaults, add api docs Signed-off-by: Andreas Gerstmayr --- .chloggen/monolithic_mode.yaml | 4 +- Makefile | 2 +- apis/tempo/v1alpha1/tempomonolithic_types.go | 7 +++- .../tempo/v1alpha1/tempomonolithic_webhook.go | 2 +- .../tempo-operator.clusterserviceversion.yaml | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 3 ++ .../tempo-operator.clusterserviceversion.yaml | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 3 ++ .../tempo.grafana.com_tempomonolithics.yaml | 3 ++ docs/operator/api.md | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 38 +++++++++++++++++++ internal/manifests/monolithic/configmap.go | 2 +- internal/manifests/monolithic/statefulset.go | 11 +++++- 13 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 docs/spec/tempo.grafana.com_tempomonolithics.yaml diff --git a/.chloggen/monolithic_mode.yaml b/.chloggen/monolithic_mode.yaml index cd27b900e..7d811f2ac 100755 --- a/.chloggen/monolithic_mode.yaml +++ b/.chloggen/monolithic_mode.yaml @@ -13,4 +13,6 @@ issues: [710] # (Optional) One or more lines of additional information to render under the primary note. # These lines will be padded with 2 spaces and then inserted directly into the document. # Use pipe (|) for multiline entries. -subtext: +subtext: | + The operator exposes a new CRD `TempoMonolithic`, which runs a Tempo instance in monolithic mode. + The monolithic mode supports the following additional storage backends: **in-memory and file system (persistent volume)**. diff --git a/Makefile b/Makefile index 0a1c0d05f..aa0596902 100644 --- a/Makefile +++ b/Makefile @@ -455,7 +455,7 @@ cmctl: } .PHONY: api-docs -api-docs: docs/operator/api.md docs/operator/feature-gates.md docs/spec/tempo.grafana.com_tempostacks.yaml +api-docs: docs/operator/api.md docs/operator/feature-gates.md docs/spec/tempo.grafana.com_tempostacks.yaml docs/spec/tempo.grafana.com_tempomonolithics.yaml TYPES_TARGET := $(shell find apis/tempo -type f -iname "*_types.go") docs/operator/api.md: $(TYPES_TARGET) gen-crd-api-reference-docs diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index 1043b8d5b..34e31a8f7 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -74,8 +74,8 @@ type MonolithicTracesStorageBackend string const ( // MonolithicTracesStorageBackendMemory defines storing traces in a tmpfs (in-memory filesystem). MonolithicTracesStorageBackendMemory MonolithicTracesStorageBackend = "memory" - // MonolithicTracesStorageBackendPersistentVolume defines storing traces in a Persistent Volume. - MonolithicTracesStorageBackendPersistentVolume MonolithicTracesStorageBackend = "pv" + // MonolithicTracesStorageBackendPV defines storing traces in a Persistent Volume. + MonolithicTracesStorageBackendPV MonolithicTracesStorageBackend = "pv" ) // MonolithicTracesStorageWALSpec defines the write-ahead logging (WAL) configuration. @@ -83,6 +83,7 @@ type MonolithicTracesStorageWALSpec struct { // Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. // // +kubebuilder:validation:Required + // +kubebuilder:default="10Gi" Size resource.Quantity `json:"size"` } @@ -91,6 +92,7 @@ type MonolithicTracesStoragePVSpec struct { // Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. // // +kubebuilder:validation:Required + // +kubebuilder:default="10Gi" Size resource.Quantity `json:"size"` } @@ -125,6 +127,7 @@ type MonolithicIngestionOTLPProtocolsGRPCSpec struct { // Enabled defines if OTLP over gRPC is enabled // // +kubebuilder:validation:Required + // +kubebuilder:default=true Enabled bool `json:"enabled"` } diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook.go b/apis/tempo/v1alpha1/tempomonolithic_webhook.go index 3947202d1..422f42e8b 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook.go +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook.go @@ -32,7 +32,7 @@ func (r *TempoMonolithic) Default() { } } - if r.Spec.Storage.Traces.Backend == MonolithicTracesStorageBackendPersistentVolume && r.Spec.Storage.Traces.PV == nil { + if r.Spec.Storage.Traces.Backend == MonolithicTracesStorageBackendPV && r.Spec.Storage.Traces.PV == nil { r.Spec.Storage.Traces.PV = &MonolithicTracesStoragePVSpec{ Size: tenGBQuantity, } diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index 43f767e83..22ce8c6a9 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.7.0 - createdAt: "2024-01-17T16:42:02Z" + createdAt: "2024-01-18T14:39:12Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml index 3e5a76f3a..9d35d0453 100644 --- a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -57,6 +57,7 @@ spec: description: GRPC defines the OTLP/gRPC configuration properties: enabled: + default: true description: Enabled defines if OTLP over gRPC is enabled type: boolean required: @@ -184,6 +185,7 @@ spec: anyOf: - type: integer - type: string + default: 10Gi description: Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ @@ -198,6 +200,7 @@ spec: anyOf: - type: integer - type: string + default: 10Gi description: Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index d5db0c530..e6c436619 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.7.0 - createdAt: "2024-01-17T16:42:01Z" + createdAt: "2024-01-18T14:39:10Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml index 3e5a76f3a..9d35d0453 100644 --- a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -57,6 +57,7 @@ spec: description: GRPC defines the OTLP/gRPC configuration properties: enabled: + default: true description: Enabled defines if OTLP over gRPC is enabled type: boolean required: @@ -184,6 +185,7 @@ spec: anyOf: - type: integer - type: string + default: 10Gi description: Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ @@ -198,6 +200,7 @@ spec: anyOf: - type: integer - type: string + default: 10Gi description: Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ diff --git a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml index 8f9be083b..75252b61d 100644 --- a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml +++ b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml @@ -54,6 +54,7 @@ spec: description: GRPC defines the OTLP/gRPC configuration properties: enabled: + default: true description: Enabled defines if OTLP over gRPC is enabled type: boolean required: @@ -181,6 +182,7 @@ spec: anyOf: - type: integer - type: string + default: 10Gi description: Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ @@ -195,6 +197,7 @@ spec: anyOf: - type: integer - type: string + default: 10Gi description: Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ diff --git a/docs/operator/api.md b/docs/operator/api.md index 9c2cc921a..f778a4dc5 100644 --- a/docs/operator/api.md +++ b/docs/operator/api.md @@ -2593,7 +2593,7 @@ MonolithicTracesStorageSpec

"pv"

-

MonolithicTracesStorageBackendPersistentVolume defines storing traces in a Persistent Volume.

+

MonolithicTracesStorageBackendPV defines storing traces in a Persistent Volume.

diff --git a/docs/spec/tempo.grafana.com_tempomonolithics.yaml b/docs/spec/tempo.grafana.com_tempomonolithics.yaml new file mode 100644 index 000000000..0f6f4002b --- /dev/null +++ b/docs/spec/tempo.grafana.com_tempomonolithics.yaml @@ -0,0 +1,38 @@ +apiVersion: tempo.grafana.com/v1alpha1 # APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources +kind: TempoMonolithic # Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds +metadata: + name: example +spec: # TempoMonolithicSpec defines the desired state of TempoMonolithic. + extraConfig: # ExtraConfig defines any extra (overlay) configuration for components + tempo: {} # Tempo defines any extra Tempo configuration, which will be merged with the operator's generated Tempo configuration + ingestion: # Ingestion defines the trace ingestion configuration + otlp: # OTLP defines the ingestion configuration for OTLP + grpc: # GRPC defines the OTLP/gRPC configuration + enabled: true # Enabled defines if OTLP over gRPC is enabled + http: # HTTP defines the OTLP/HTTP configuration + enabled: false # Enabled defines if OTLP over HTTP is enabled + tls: # TLS defines the TLS configuration for ingestion + ca: "" # CA defines the name of a secret containing the CA certificate + cert: "" # Cert defines the name of a secret containing the TLS certificate and private key + enabled: false # Enabled defines if TLS is enabled for ingestion + jaegerui: # JaegerUI defines the Jaeger UI configuration + enabled: false # Enabled defines if the Jaeger UI should be enabled + ingress: # Ingress defines the ingress configuration for Jaeger UI + enabled: false # Enabled defines if an Ingress object should be created for Jaeger UI + route: # Route defines the route configuration for Jaeger UI + enabled: false # Enabled defines if a Route object should be created for Jaeger UI + management: "" # ManagementState defines whether this instance is managed by the operator or self-managed + observability: # Observability defines observability configuration for the Tempo deployment + metrics: # Metrics defines the metrics configuration of the Tempo deployment + prometheusRules: # ServiceMonitors defines the PrometheusRule configuration + enabled: false # Enabled defines if the operator should create PrometheusRules for this Tempo deployment + serviceMonitors: # ServiceMonitors defines the ServiceMonitor configuration + enabled: false # Enabled defines if the operator should create ServiceMonitors for this Tempo deployment + storage: # Storage defines the backend storage configuration + traces: # Traces defines the backend storage configuration for traces + backend: "memory" # Backend defines the backend for storing traces. Default: memory + pv: # PV defines the Persistent Volume configuration + size: "10Gi" # Size defines the size of the Persistent Volume for storing the traces. Defaults to 10Gi. + wal: # WAL defines the write-ahead logging (WAL) configuration + size: "10Gi" # Size defines the size of the Persistent Volume for storing the WAL. Defaults to 10Gi. +status: # TempoMonolithicStatus defines the observed state of TempoMonolithic. diff --git a/internal/manifests/monolithic/configmap.go b/internal/manifests/monolithic/configmap.go index 6f44380cf..e104e5465 100644 --- a/internal/manifests/monolithic/configmap.go +++ b/internal/manifests/monolithic/configmap.go @@ -102,7 +102,7 @@ func buildTempoConfig(opts Options) ([]byte, error) { config.Storage.Trace.WAL.Path = "/var/tempo/wal" switch tempo.Spec.Storage.Traces.Backend { case v1alpha1.MonolithicTracesStorageBackendMemory, - v1alpha1.MonolithicTracesStorageBackendPersistentVolume: + v1alpha1.MonolithicTracesStorageBackendPV: config.Storage.Trace.Backend = "local" config.Storage.Trace.Local.Path = "/var/tempo/blocks" diff --git a/internal/manifests/monolithic/statefulset.go b/internal/manifests/monolithic/statefulset.go index 2f3113a81..a014cc0c1 100644 --- a/internal/manifests/monolithic/statefulset.go +++ b/internal/manifests/monolithic/statefulset.go @@ -65,6 +65,13 @@ func BuildTempoStatefulset(opts Options) (*appsv1.StatefulSet, error) { "-mem-ballast-size-mbs=1024", "-log.level=info", }, + + // The Tempo Helm chart mounts /var/tempo if persistence is enabled. + // Tempo writes its WAL to /var/tempo/wal, and if the local storage backend is enabled, parquet blocks to /var/tempo/blocks. + // + // Let's mount /var/tempo as WAL, in case Tempo writes caches to additional locations in /var/tempo in the future. + // If memory or pv storage is enabled, /var/tempo/blocks will be mounted in a different volume (configured in configureStorage()). + // This is to avoid confusion why a PV is required when selecting object storage. VolumeMounts: []corev1.VolumeMount{ { Name: manifestutils.ConfigVolumeName, @@ -73,7 +80,7 @@ func BuildTempoStatefulset(opts Options) (*appsv1.StatefulSet, error) { }, { Name: walVolumeName, - MountPath: "/var/tempo/wal", + MountPath: "/var/tempo", }, }, Ports: buildTempoPorts(opts), @@ -166,7 +173,7 @@ func configureStorage(opts Options, sts *appsv1.StatefulSet) error { }, }) - case v1alpha1.MonolithicTracesStorageBackendPersistentVolume: + case v1alpha1.MonolithicTracesStorageBackendPV: if tempo.Spec.Storage.Traces.WAL == nil { return errors.New("please configure .spec.storage.traces.wal") } From cd64880e04af84818ec1716b683e0a9f650deb94 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 18 Jan 2024 16:13:40 +0100 Subject: [PATCH 28/36] update changelog Signed-off-by: Andreas Gerstmayr --- .chloggen/monolithic_mode.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.chloggen/monolithic_mode.yaml b/.chloggen/monolithic_mode.yaml index 7d811f2ac..ddca4642f 100755 --- a/.chloggen/monolithic_mode.yaml +++ b/.chloggen/monolithic_mode.yaml @@ -14,5 +14,5 @@ issues: [710] # These lines will be padded with 2 spaces and then inserted directly into the document. # Use pipe (|) for multiline entries. subtext: | - The operator exposes a new CRD `TempoMonolithic`, which runs a Tempo instance in monolithic mode. - The monolithic mode supports the following additional storage backends: **in-memory and file system (persistent volume)**. + The operator exposes a new CRD `TempoMonolithic`, which manages a Tempo instance in monolithic mode. + The monolithic mode supports the following additional storage backends: in-memory and file system (persistent volume). From a9e41e97b286e7d5c1fee6594cb9b3d0d6e45c69 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 18 Jan 2024 16:19:13 +0100 Subject: [PATCH 29/36] fix test Signed-off-by: Andreas Gerstmayr --- internal/manifests/monolithic/statefulset_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/manifests/monolithic/statefulset_test.go b/internal/manifests/monolithic/statefulset_test.go index 53698e8fd..ff35eb612 100644 --- a/internal/manifests/monolithic/statefulset_test.go +++ b/internal/manifests/monolithic/statefulset_test.go @@ -94,7 +94,7 @@ func TestStatefulsetMemoryStorage(t *testing.T) { }, { Name: "tempo-wal", - MountPath: "/var/tempo/wal", + MountPath: "/var/tempo", }, { Name: "tempo-blocks", @@ -189,7 +189,7 @@ func TestStatefulsetPVStorage(t *testing.T) { }, { Name: "tempo-wal", - MountPath: "/var/tempo/wal", + MountPath: "/var/tempo", }, { Name: "tempo-blocks", From 6657c890668f1e43bbc50563b6ade05c775cbde7 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 18 Jan 2024 17:45:56 +0100 Subject: [PATCH 30/36] add controller test Signed-off-by: Andreas Gerstmayr --- .../tempo/tempomonolithic_controller_test.go | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 controllers/tempo/tempomonolithic_controller_test.go diff --git a/controllers/tempo/tempomonolithic_controller_test.go b/controllers/tempo/tempomonolithic_controller_test.go new file mode 100644 index 000000000..2114df792 --- /dev/null +++ b/controllers/tempo/tempomonolithic_controller_test.go @@ -0,0 +1,66 @@ +package controllers + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" +) + +func TestReconcileMonolithic(t *testing.T) { + nsn := types.NamespacedName{Name: "sample", Namespace: "default"} + tempo := &v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + }, + } + err := k8sClient.Create(context.Background(), tempo) + require.NoError(t, err) + + reconciler := TempoMonolithicReconciler{ + Client: k8sClient, + Scheme: testScheme, + CtrlConfig: configv1alpha1.ProjectConfig{}, + } + reconcile, err := reconciler.Reconcile(context.Background(), ctrl.Request{NamespacedName: nsn}) + require.NoError(t, err) + assert.Equal(t, false, reconcile.Requeue) + + // Check if objects of specific types were created and are managed by the operator + opts := []client.ListOption{ + client.InNamespace(nsn.Namespace), + client.MatchingLabels(map[string]string{ + "app.kubernetes.io/instance": nsn.Name, + "app.kubernetes.io/managed-by": "tempo-operator", + }), + } + { + list := &corev1.ConfigMapList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &appsv1.StatefulSetList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &corev1.ServiceList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } +} From 9a95fcebca1840ac7e52978ac4f8f36a3b0a82fb Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 24 Jan 2024 19:33:36 +0100 Subject: [PATCH 31/36] enable OTLP/HTTP by default Signed-off-by: Andreas Gerstmayr --- .../tempo/v1alpha1/tempomonolithic_webhook.go | 19 +++++++++++++------ .../v1alpha1/tempomonolithic_webhook_test.go | 16 ++++++++++++++-- tests/e2e/monolithic-smoketest/01-assert.yaml | 4 ++++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook.go b/apis/tempo/v1alpha1/tempomonolithic_webhook.go index 422f42e8b..8285556e9 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook.go +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook.go @@ -39,12 +39,19 @@ func (r *TempoMonolithic) Default() { } if r.Spec.Ingestion == nil { - r.Spec.Ingestion = &MonolithicIngestionSpec{ - OTLP: &MonolithicIngestionOTLPSpec{ - GRPC: &MonolithicIngestionOTLPProtocolsGRPCSpec{ - Enabled: true, - }, - }, + 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_webhook_test.go index 49ae4d72e..93c68c8db 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook_test.go @@ -13,7 +13,7 @@ func TestMonolithicDefault(t *testing.T) { expected *TempoMonolithic }{ { - name: "empty spec, set memory backend and enable OTLP/gRPC", + name: "empty spec, set memory backend and enable OTLP/gRPC and OTLP/HTTP", input: &TempoMonolithic{ Spec: TempoMonolithicSpec{}, }, @@ -29,6 +29,9 @@ func TestMonolithicDefault(t *testing.T) { GRPC: &MonolithicIngestionOTLPProtocolsGRPCSpec{ Enabled: true, }, + HTTP: &MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, }, }, }, @@ -63,6 +66,9 @@ func TestMonolithicDefault(t *testing.T) { GRPC: &MonolithicIngestionOTLPProtocolsGRPCSpec{ Enabled: true, }, + HTTP: &MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, }, }, }, @@ -82,7 +88,10 @@ func TestMonolithicDefault(t *testing.T) { }, Ingestion: &MonolithicIngestionSpec{ OTLP: &MonolithicIngestionOTLPSpec{ - // HTTP is already set, GRPC should not be enabled by webhook + // GRPC is explicitly disabled and should not be enabled by webhook + GRPC: &MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: false, + }, HTTP: &MonolithicIngestionOTLPProtocolsHTTPSpec{ Enabled: true, }, @@ -102,6 +111,9 @@ func TestMonolithicDefault(t *testing.T) { }, Ingestion: &MonolithicIngestionSpec{ OTLP: &MonolithicIngestionOTLPSpec{ + GRPC: &MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: false, + }, HTTP: &MonolithicIngestionOTLPProtocolsHTTPSpec{ Enabled: true, }, diff --git a/tests/e2e/monolithic-smoketest/01-assert.yaml b/tests/e2e/monolithic-smoketest/01-assert.yaml index 2c50d42e4..8767efc3f 100644 --- a/tests/e2e/monolithic-smoketest/01-assert.yaml +++ b/tests/e2e/monolithic-smoketest/01-assert.yaml @@ -38,6 +38,10 @@ spec: port: 4317 protocol: TCP targetPort: otlp-grpc + - name: otlp-http + port: 4318 + protocol: TCP + targetPort: otlp-http - name: jaeger-grpc port: 16685 protocol: TCP From 1cf69764377b0f876a89344a91ebd070603328c4 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 25 Jan 2024 11:36:33 +0100 Subject: [PATCH 32/36] drop TLS struct (will reuse TLS struct from TempoStack in follow-up PR) Signed-off-by: Andreas Gerstmayr --- apis/tempo/v1alpha1/tempomonolithic_types.go | 25 ---- apis/tempo/v1alpha1/zz_generated.deepcopy.go | 20 --- .../tempo-operator.clusterserviceversion.yaml | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 21 --- .../tempo-operator.clusterserviceversion.yaml | 2 +- .../tempo.grafana.com_tempomonolithics.yaml | 21 --- .../tempo.grafana.com_tempomonolithics.yaml | 21 --- docs/operator/api.md | 121 ------------------ .../tempo.grafana.com_tempomonolithics.yaml | 4 - 9 files changed, 2 insertions(+), 235 deletions(-) diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index 34e31a8f7..2c3c7357b 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -102,11 +102,6 @@ type MonolithicIngestionSpec struct { // // +kubebuilder:validation:Optional OTLP *MonolithicIngestionOTLPSpec `json:"otlp,omitempty"` - - // TLS defines the TLS configuration for ingestion - // - // +kubebuilder:validation:Optional - TLS *MonolithicIngestionTLSSpec `json:"tls,omitempty"` } // MonolithicIngestionOTLPSpec defines the settings for OTLP ingestion. @@ -139,26 +134,6 @@ type MonolithicIngestionOTLPProtocolsHTTPSpec struct { Enabled bool `json:"enabled"` } -// MonolithicIngestionTLSSpec defines the TLS settings for ingestion. -type MonolithicIngestionTLSSpec struct { - // Enabled defines if TLS is enabled for ingestion - // - // +kubebuilder:validation:Required - Enabled bool `json:"enabled"` - - // CA defines the name of a secret containing the CA certificate - // - // +kubebuilder:validation:Required - // +kubebuilder:validation:MinLength=1 - CA string `json:"ca"` - - // Cert defines the name of a secret containing the TLS certificate and private key - // - // +kubebuilder:validation:Required - // +kubebuilder:validation:MinLength=1 - Cert string `json:"cert"` -} - // MonolithicJaegerUISpec defines the settings for the Jaeger UI. type MonolithicJaegerUISpec struct { // Enabled defines if the Jaeger UI should be enabled diff --git a/apis/tempo/v1alpha1/zz_generated.deepcopy.go b/apis/tempo/v1alpha1/zz_generated.deepcopy.go index b74602d7f..261d6fb23 100644 --- a/apis/tempo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/tempo/v1alpha1/zz_generated.deepcopy.go @@ -461,11 +461,6 @@ func (in *MonolithicIngestionSpec) DeepCopyInto(out *MonolithicIngestionSpec) { *out = new(MonolithicIngestionOTLPSpec) (*in).DeepCopyInto(*out) } - if in.TLS != nil { - in, out := &in.TLS, &out.TLS - *out = new(MonolithicIngestionTLSSpec) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicIngestionSpec. @@ -478,21 +473,6 @@ func (in *MonolithicIngestionSpec) DeepCopy() *MonolithicIngestionSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MonolithicIngestionTLSSpec) DeepCopyInto(out *MonolithicIngestionTLSSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicIngestionTLSSpec. -func (in *MonolithicIngestionTLSSpec) DeepCopy() *MonolithicIngestionTLSSpec { - if in == nil { - return nil - } - out := new(MonolithicIngestionTLSSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MonolithicJaegerUIIngressSpec) DeepCopyInto(out *MonolithicJaegerUIIngressSpec) { *out = *in diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index 22ce8c6a9..426234feb 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.7.0 - createdAt: "2024-01-18T14:39:12Z" + createdAt: "2024-01-25T10:35:19Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml index 9d35d0453..e43b04047 100644 --- a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -73,27 +73,6 @@ spec: - enabled type: object type: object - tls: - description: TLS defines the TLS configuration for ingestion - properties: - ca: - description: CA defines the name of a secret containing the - CA certificate - minLength: 1 - type: string - cert: - description: Cert defines the name of a secret containing - the TLS certificate and private key - minLength: 1 - type: string - enabled: - description: Enabled defines if TLS is enabled for ingestion - type: boolean - required: - - ca - - cert - - enabled - type: object type: object jaegerui: description: JaegerUI defines the Jaeger UI configuration diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index e6c436619..fb4a6a1ff 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.7.0 - createdAt: "2024-01-18T14:39:10Z" + createdAt: "2024-01-25T10:35:18Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" diff --git a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml index 9d35d0453..e43b04047 100644 --- a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -73,27 +73,6 @@ spec: - enabled type: object type: object - tls: - description: TLS defines the TLS configuration for ingestion - properties: - ca: - description: CA defines the name of a secret containing the - CA certificate - minLength: 1 - type: string - cert: - description: Cert defines the name of a secret containing - the TLS certificate and private key - minLength: 1 - type: string - enabled: - description: Enabled defines if TLS is enabled for ingestion - type: boolean - required: - - ca - - cert - - enabled - type: object type: object jaegerui: description: JaegerUI defines the Jaeger UI configuration diff --git a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml index 75252b61d..f81b4b6c7 100644 --- a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml +++ b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml @@ -70,27 +70,6 @@ spec: - enabled type: object type: object - tls: - description: TLS defines the TLS configuration for ingestion - properties: - ca: - description: CA defines the name of a secret containing the - CA certificate - minLength: 1 - type: string - cert: - description: Cert defines the name of a secret containing - the TLS certificate and private key - minLength: 1 - type: string - enabled: - description: Enabled defines if TLS is enabled for ingestion - type: boolean - required: - - ca - - cert - - enabled - type: object type: object jaegerui: description: JaegerUI defines the Jaeger UI configuration diff --git a/docs/operator/api.md b/docs/operator/api.md index f778a4dc5..ac8738806 100644 --- a/docs/operator/api.md +++ b/docs/operator/api.md @@ -1913,127 +1913,6 @@ MonolithicIngestionOTLPSpec - - - - -tls
- - - - - -MonolithicIngestionTLSSpec - - - - - - - - - -

TLS defines the TLS configuration for ingestion

- - - - - - - -## MonolithicIngestionTLSSpec { #tempo-grafana-com-v1alpha1-MonolithicIngestionTLSSpec } - -

- -(Appears on:MonolithicIngestionSpec) - -

- -
- -

MonolithicIngestionTLSSpec defines the TLS settings for ingestion.

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldDescription
- -enabled
- - - -bool - - - -
- -

Enabled defines if TLS is enabled for ingestion

- -
- -ca
- - - -string - - - -
- -

CA defines the name of a secret containing the CA certificate

- -
- -cert
- - - -string - - - -
- -

Cert defines the name of a secret containing the TLS certificate and private key

- -
diff --git a/docs/spec/tempo.grafana.com_tempomonolithics.yaml b/docs/spec/tempo.grafana.com_tempomonolithics.yaml index 0f6f4002b..f3c18340f 100644 --- a/docs/spec/tempo.grafana.com_tempomonolithics.yaml +++ b/docs/spec/tempo.grafana.com_tempomonolithics.yaml @@ -11,10 +11,6 @@ spec: # TempoMonolithicSpec defines the desir enabled: true # Enabled defines if OTLP over gRPC is enabled http: # HTTP defines the OTLP/HTTP configuration enabled: false # Enabled defines if OTLP over HTTP is enabled - tls: # TLS defines the TLS configuration for ingestion - ca: "" # CA defines the name of a secret containing the CA certificate - cert: "" # Cert defines the name of a secret containing the TLS certificate and private key - enabled: false # Enabled defines if TLS is enabled for ingestion jaegerui: # JaegerUI defines the Jaeger UI configuration enabled: false # Enabled defines if the Jaeger UI should be enabled ingress: # Ingress defines the ingress configuration for Jaeger UI From 16b70304720c0a3d94170c643ce40e2e7c4cb4b6 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 25 Jan 2024 12:01:03 +0100 Subject: [PATCH 33/36] add comment to default function Signed-off-by: Andreas Gerstmayr --- apis/tempo/v1alpha1/tempomonolithic_webhook.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apis/tempo/v1alpha1/tempomonolithic_webhook.go b/apis/tempo/v1alpha1/tempomonolithic_webhook.go index 8285556e9..e0d240986 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_webhook.go +++ b/apis/tempo/v1alpha1/tempomonolithic_webhook.go @@ -16,7 +16,9 @@ func (r *TempoMonolithic) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -// Default implements webhook.Defaulter so a webhook will be registered for the type. +// 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{} From 822c01668e945e74c03e035a7e0d3b188d9730c4 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Fri, 26 Jan 2024 12:40:24 +0100 Subject: [PATCH 34/36] show diff using cmp.Diff() in logs when immutable field is changed Signed-off-by: Andreas Gerstmayr --- go.mod | 2 +- internal/manifests/mutate.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index c095e6ba7..2b926d8fa 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/ViaQ/logerr/v2 v2.1.0 github.com/go-logr/logr v1.4.1 github.com/go-logr/zapr v1.3.0 + github.com/google/go-cmp v0.6.0 github.com/grafana-operator/grafana-operator/v5 v5.5.2 github.com/imdario/mergo v0.3.16 github.com/novln/docker-parser v1.0.0 @@ -53,7 +54,6 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 // indirect github.com/google/uuid v1.3.1 // indirect diff --git a/internal/manifests/mutate.go b/internal/manifests/mutate.go index 691554e50..029a4f120 100644 --- a/internal/manifests/mutate.go +++ b/internal/manifests/mutate.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/ViaQ/logerr/v2/kverrors" + "github.com/google/go-cmp/cmp" grafanav1 "github.com/grafana-operator/grafana-operator/v5/api/v1beta1" "github.com/imdario/mergo" routev1 "github.com/openshift/api/route/v1" @@ -26,7 +27,7 @@ type ImmutableErr struct { } func (m *ImmutableErr) Error() string { - return fmt.Sprintf("update to immutable field %s is forbidden", m.field) + return fmt.Sprintf("update to immutable field %s is forbidden, diff: %s", m.field, cmp.Diff(m.existing, m.desired)) } // MutateFuncFor returns a mutate function based on the From bf76bef29b063f92db38fad79a4f11e51d2129f8 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Fri, 26 Jan 2024 13:11:54 +0100 Subject: [PATCH 35/36] re-use ExtraConfig from TempoStack Signed-off-by: Andreas Gerstmayr --- apis/tempo/v1alpha1/tempomonolithic_types.go | 10 +- apis/tempo/v1alpha1/tempostack_types.go | 2 + apis/tempo/v1alpha1/zz_generated.deepcopy.go | 18 +-- .../tempo-operator.clusterserviceversion.yaml | 11 +- .../tempo.grafana.com_tempostacks.yaml | 2 + .../tempo-operator.clusterserviceversion.yaml | 11 +- .../tempo.grafana.com_tempostacks.yaml | 2 + .../bases/tempo.grafana.com_tempostacks.yaml | 2 + .../tempo-operator.clusterserviceversion.yaml | 9 +- .../tempo-operator.clusterserviceversion.yaml | 9 +- docs/operator/api.md | 66 +---------- docs/spec/tempo.grafana.com_tempostacks.yaml | 2 +- internal/manifests/config/configmap.go | 4 +- internal/manifests/config/extra.go | 5 +- internal/manifests/config/extra_test.go | 6 +- internal/manifests/monolithic/configmap.go | 34 +----- .../manifests/monolithic/configmap_test.go | 110 ++++++++++++------ 17 files changed, 140 insertions(+), 163 deletions(-) diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index 2c3c7357b..bf89df673 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -1,7 +1,6 @@ package v1alpha1 import ( - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -36,7 +35,7 @@ type TempoMonolithicSpec struct { // ExtraConfig defines any extra (overlay) configuration for components // // +kubebuilder:validation:Optional - ExtraConfig *MonolithicExtraConfigSpec `json:"extraConfig,omitempty"` + ExtraConfig *ExtraConfigSpec `json:"extraConfig,omitempty"` } // MonolithicStorageSpec defines the storage for the Tempo deployment. @@ -205,13 +204,6 @@ type MonolithicObservabilityMetricsPrometheusRulesSpec struct { Enabled bool `json:"enabled"` } -// MonolithicExtraConfigSpec defines extra configuration for this deployment. -type MonolithicExtraConfigSpec struct { - // Tempo defines any extra Tempo configuration, which will be merged with the operator's generated Tempo configuration - // +kubebuilder:validation:Optional - Tempo apiextensionsv1.JSON `json:"tempo,omitempty"` -} - // TempoMonolithicStatus defines the observed state of TempoMonolithic. type TempoMonolithicStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster diff --git a/apis/tempo/v1alpha1/tempostack_types.go b/apis/tempo/v1alpha1/tempostack_types.go index 5e293ff06..d5c2a9d73 100644 --- a/apis/tempo/v1alpha1/tempostack_types.go +++ b/apis/tempo/v1alpha1/tempostack_types.go @@ -132,6 +132,8 @@ type TempoStackSpec struct { // ExtraConfigSpec defines extra configurations for tempo that will be merged with the operator generated, configurations defined here // has precedence and could override generated config. type ExtraConfigSpec struct { + // Tempo defines any extra Tempo configuration, which will be merged with the operator's generated Tempo configuration + // // +optional // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Tempo Extra Configurations" Tempo apiextensionsv1.JSON `json:"tempo,omitempty"` diff --git a/apis/tempo/v1alpha1/zz_generated.deepcopy.go b/apis/tempo/v1alpha1/zz_generated.deepcopy.go index 261d6fb23..ea10afd40 100644 --- a/apis/tempo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/tempo/v1alpha1/zz_generated.deepcopy.go @@ -382,22 +382,6 @@ func (in *MetricsConfigSpec) DeepCopy() *MetricsConfigSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MonolithicExtraConfigSpec) DeepCopyInto(out *MonolithicExtraConfigSpec) { - *out = *in - in.Tempo.DeepCopyInto(&out.Tempo) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicExtraConfigSpec. -func (in *MonolithicExtraConfigSpec) DeepCopy() *MonolithicExtraConfigSpec { - if in == nil { - return nil - } - out := new(MonolithicExtraConfigSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MonolithicIngestionOTLPProtocolsGRPCSpec) DeepCopyInto(out *MonolithicIngestionOTLPProtocolsGRPCSpec) { *out = *in @@ -1165,7 +1149,7 @@ func (in *TempoMonolithicSpec) DeepCopyInto(out *TempoMonolithicSpec) { } if in.ExtraConfig != nil { in, out := &in.ExtraConfig, &out.ExtraConfig - *out = new(MonolithicExtraConfigSpec) + *out = new(ExtraConfigSpec) (*in).DeepCopyInto(*out) } } diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index 426234feb..f839ea08d 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.7.0 - createdAt: "2024-01-25T10:35:19Z" + createdAt: "2024-01-26T12:10:07Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -75,6 +75,11 @@ spec: displayName: Tempo Monolithic kind: TempoMonolithic name: tempomonolithics.tempo.grafana.com + specDescriptors: + - description: Tempo defines any extra Tempo configuration, which will be merged + with the operator's generated Tempo configuration + displayName: Tempo Extra Configurations + path: extraConfig.tempo version: v1alpha1 - description: TempoStack is the spec for Tempo deployments. displayName: TempoStack @@ -108,7 +113,9 @@ spec: specDescriptors: - displayName: Extra Configurations path: extraConfig - - displayName: Tempo Extra Configurations + - description: Tempo defines any extra Tempo configuration, which will be merged + with the operator's generated Tempo configuration + displayName: Tempo Extra Configurations path: extraConfig.tempo - description: HashRing defines the spec for the distributed hash ring configuration. displayName: Hash Ring diff --git a/bundle/community/manifests/tempo.grafana.com_tempostacks.yaml b/bundle/community/manifests/tempo.grafana.com_tempostacks.yaml index 5a309fae3..de8d6a55a 100644 --- a/bundle/community/manifests/tempo.grafana.com_tempostacks.yaml +++ b/bundle/community/manifests/tempo.grafana.com_tempostacks.yaml @@ -59,6 +59,8 @@ spec: defined here has precedence and could override generated config. properties: tempo: + description: Tempo defines any extra Tempo configuration, which + will be merged with the operator's generated Tempo configuration x-kubernetes-preserve-unknown-fields: true type: object hashRing: diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index fb4a6a1ff..c449b0849 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -56,7 +56,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.7.0 - createdAt: "2024-01-25T10:35:18Z" + createdAt: "2024-01-26T12:10:06Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -75,6 +75,11 @@ spec: displayName: Tempo Monolithic kind: TempoMonolithic name: tempomonolithics.tempo.grafana.com + specDescriptors: + - description: Tempo defines any extra Tempo configuration, which will be merged + with the operator's generated Tempo configuration + displayName: Tempo Extra Configurations + path: extraConfig.tempo version: v1alpha1 - description: TempoStack is the spec for Tempo deployments. displayName: TempoStack @@ -108,7 +113,9 @@ spec: specDescriptors: - displayName: Extra Configurations path: extraConfig - - displayName: Tempo Extra Configurations + - description: Tempo defines any extra Tempo configuration, which will be merged + with the operator's generated Tempo configuration + displayName: Tempo Extra Configurations path: extraConfig.tempo - description: HashRing defines the spec for the distributed hash ring configuration. displayName: Hash Ring diff --git a/bundle/openshift/manifests/tempo.grafana.com_tempostacks.yaml b/bundle/openshift/manifests/tempo.grafana.com_tempostacks.yaml index 5a309fae3..de8d6a55a 100644 --- a/bundle/openshift/manifests/tempo.grafana.com_tempostacks.yaml +++ b/bundle/openshift/manifests/tempo.grafana.com_tempostacks.yaml @@ -59,6 +59,8 @@ spec: defined here has precedence and could override generated config. properties: tempo: + description: Tempo defines any extra Tempo configuration, which + will be merged with the operator's generated Tempo configuration x-kubernetes-preserve-unknown-fields: true type: object hashRing: diff --git a/config/crd/bases/tempo.grafana.com_tempostacks.yaml b/config/crd/bases/tempo.grafana.com_tempostacks.yaml index 21a86ff84..a59411931 100644 --- a/config/crd/bases/tempo.grafana.com_tempostacks.yaml +++ b/config/crd/bases/tempo.grafana.com_tempostacks.yaml @@ -56,6 +56,8 @@ spec: defined here has precedence and could override generated config. properties: tempo: + description: Tempo defines any extra Tempo configuration, which + will be merged with the operator's generated Tempo configuration x-kubernetes-preserve-unknown-fields: true type: object hashRing: diff --git a/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml b/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml index 5e3ff8ea9..b97879b30 100644 --- a/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml +++ b/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml @@ -22,6 +22,11 @@ spec: displayName: Tempo Monolithic kind: TempoMonolithic name: tempomonolithics.tempo.grafana.com + specDescriptors: + - description: Tempo defines any extra Tempo configuration, which will be merged + with the operator's generated Tempo configuration + displayName: Tempo Extra Configurations + path: extraConfig.tempo version: v1alpha1 - description: TempoStack is the spec for Tempo deployments. displayName: TempoStack @@ -55,7 +60,9 @@ spec: specDescriptors: - displayName: Extra Configurations path: extraConfig - - displayName: Tempo Extra Configurations + - description: Tempo defines any extra Tempo configuration, which will be merged + with the operator's generated Tempo configuration + displayName: Tempo Extra Configurations path: extraConfig.tempo - description: HashRing defines the spec for the distributed hash ring configuration. displayName: Hash Ring diff --git a/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml b/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml index 5e3ff8ea9..b97879b30 100644 --- a/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml +++ b/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml @@ -22,6 +22,11 @@ spec: displayName: Tempo Monolithic kind: TempoMonolithic name: tempomonolithics.tempo.grafana.com + specDescriptors: + - description: Tempo defines any extra Tempo configuration, which will be merged + with the operator's generated Tempo configuration + displayName: Tempo Extra Configurations + path: extraConfig.tempo version: v1alpha1 - description: TempoStack is the spec for Tempo deployments. displayName: TempoStack @@ -55,7 +60,9 @@ spec: specDescriptors: - displayName: Extra Configurations path: extraConfig - - displayName: Tempo Extra Configurations + - description: Tempo defines any extra Tempo configuration, which will be merged + with the operator's generated Tempo configuration + displayName: Tempo Extra Configurations path: extraConfig.tempo - description: HashRing defines the spec for the distributed hash ring configuration. displayName: Hash Ring diff --git a/docs/operator/api.md b/docs/operator/api.md index ac8738806..abf7529a2 100644 --- a/docs/operator/api.md +++ b/docs/operator/api.md @@ -572,7 +572,7 @@ Feature Gates.ProjectConfig

-(Appears on:TempoStackSpec) +(Appears on:TempoMonolithicSpec, TempoStackSpec)

@@ -621,6 +621,8 @@ k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON (Optional) +

Tempo defines any extra Tempo configuration, which will be merged with the operator’s generated Tempo configuration

+ @@ -1609,64 +1611,6 @@ using an in-process OpenPolicyAgent Rego authorizer.

-## MonolithicExtraConfigSpec { #tempo-grafana-com-v1alpha1-MonolithicExtraConfigSpec } - -

- -(Appears on:TempoMonolithicSpec) - -

- -
- -

MonolithicExtraConfigSpec defines extra configuration for this deployment.

- -
- - - - - - - - - - - - - - - - - - - - - - - - - -
FieldDescription
- -tempo
- - - - - -k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON - - - - - -
- -

Tempo defines any extra Tempo configuration, which will be merged with the operator’s generated Tempo configuration

- -
- ## MonolithicIngestionOTLPProtocolsGRPCSpec { #tempo-grafana-com-v1alpha1-MonolithicIngestionOTLPProtocolsGRPCSpec }

@@ -4944,9 +4888,9 @@ MonolithicObservabilitySpec - + -MonolithicExtraConfigSpec +ExtraConfigSpec diff --git a/docs/spec/tempo.grafana.com_tempostacks.yaml b/docs/spec/tempo.grafana.com_tempostacks.yaml index a2d94299b..cfbbe0631 100644 --- a/docs/spec/tempo.grafana.com_tempostacks.yaml +++ b/docs/spec/tempo.grafana.com_tempostacks.yaml @@ -4,7 +4,7 @@ metadata: name: example spec: # TempoStackSpec defines the desired state of TempoStack. extraConfig: # ExtraConfigSpec defines extra configurations for tempo that will be merged with the operator generated, configurations defined here has precedence and could override generated config. - tempo: {} + tempo: {} # Tempo defines any extra Tempo configuration, which will be merged with the operator's generated Tempo configuration hashRing: # HashRing defines the spec for the distributed hash ring configuration. memberlist: # MemberList configuration spec enableIPv6: false # EnableIPv6 enables IPv6 support for the memberlist based hash ring. diff --git a/internal/manifests/config/configmap.go b/internal/manifests/config/configmap.go index 2e58c0107..1d9c75017 100644 --- a/internal/manifests/config/configmap.go +++ b/internal/manifests/config/configmap.go @@ -40,13 +40,13 @@ func BuildConfigMap(params manifestutils.Params) (*corev1.ConfigMap, string, err if params.Tempo.Spec.ExtraConfig != nil { // For we only support tempo for now. - config, err = mergeExtraConfigWithConfig(params.Tempo.Spec.ExtraConfig.Tempo, config) + config, err = MergeExtraConfigWithConfig(params.Tempo.Spec.ExtraConfig.Tempo, config) if err != nil { return nil, "", err } // Is the same tempo config with certain TLS fields disabled. - frontendConfig, err = mergeExtraConfigWithConfig(params.Tempo.Spec.ExtraConfig.Tempo, frontendConfig) + frontendConfig, err = MergeExtraConfigWithConfig(params.Tempo.Spec.ExtraConfig.Tempo, frontendConfig) if err != nil { return nil, "", err } diff --git a/internal/manifests/config/extra.go b/internal/manifests/config/extra.go index 9a14d7272..f7b7d30cd 100644 --- a/internal/manifests/config/extra.go +++ b/internal/manifests/config/extra.go @@ -9,7 +9,8 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) -func mergeExtraConfigWithConfig(overridesJSON apiextensionsv1.JSON, templateResults []byte) ([]byte, error) { +func MergeExtraConfigWithConfig(overridesJSON apiextensionsv1.JSON, templateResults []byte) ([]byte, error) { + // mergo.Merge requires that both variables have the same type renderedTemplateMap := make(map[string]interface{}) overrides := make(map[string]interface{}) @@ -17,6 +18,7 @@ func mergeExtraConfigWithConfig(overridesJSON apiextensionsv1.JSON, templateResu return templateResults, nil } + // Unmarshal overlay of type []byte to map[string]interface{} if err := json.Unmarshal(overridesJSON.Raw, &overrides); err != nil { return nil, err } @@ -25,6 +27,7 @@ func mergeExtraConfigWithConfig(overridesJSON apiextensionsv1.JSON, templateResu return nil, err } + // Override generated config with extra config if err := mergo.Merge(&renderedTemplateMap, overrides, mergo.WithOverride); err != nil { return nil, err } diff --git a/internal/manifests/config/extra_test.go b/internal/manifests/config/extra_test.go index 3a74b745d..f2d9e3800 100644 --- a/internal/manifests/config/extra_test.go +++ b/internal/manifests/config/extra_test.go @@ -55,7 +55,7 @@ server: extraConfig := apiextensionsv1.JSON{Raw: raw} - result, err := mergeExtraConfigWithConfig(extraConfig, []byte(input)) + result, err := MergeExtraConfigWithConfig(extraConfig, []byte(input)) require.NoError(t, err) require.YAMLEq(t, expCfg, string(result)) @@ -77,7 +77,7 @@ storage: ` extraConfig := apiextensionsv1.JSON{} - result, err := mergeExtraConfigWithConfig(extraConfig, []byte(input)) + result, err := MergeExtraConfigWithConfig(extraConfig, []byte(input)) require.NoError(t, err) require.YAMLEq(t, input, string(result)) } @@ -100,6 +100,6 @@ storage: Raw: []byte("{{{{}"), } - _, err := mergeExtraConfigWithConfig(extraConfig, []byte(input)) + _, err := MergeExtraConfigWithConfig(extraConfig, []byte(input)) require.Error(t, err) } diff --git a/internal/manifests/monolithic/configmap.go b/internal/manifests/monolithic/configmap.go index e104e5465..85f2c67bf 100644 --- a/internal/manifests/monolithic/configmap.go +++ b/internal/manifests/monolithic/configmap.go @@ -2,16 +2,15 @@ package monolithic import ( "crypto/sha256" - "encoding/json" "fmt" - "github.com/imdario/mergo" "gopkg.in/yaml.v2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" + tempoStackConfig "github.com/grafana/tempo-operator/internal/manifests/config" "github.com/grafana/tempo-operator/internal/manifests/manifestutils" "github.com/grafana/tempo-operator/internal/manifests/naming" ) @@ -121,39 +120,16 @@ func buildTempoConfig(opts Options) ([]byte, error) { } } - if tempo.Spec.ExtraConfig == nil || len(tempo.Spec.ExtraConfig.Tempo.Raw) == 0 { - return yaml.Marshal(config) - } else { - return overlayJson(config, tempo.Spec.ExtraConfig.Tempo.Raw) - } -} - -func overlayJson(config tempoConfig, overlay []byte) ([]byte, error) { - // mergo.Merge requires that both variables have the same type - generatedCfg := make(map[string]interface{}) - overlayCfg := make(map[string]interface{}) - - // Convert tempoConfig{} to map[string]interface{} generatedYaml, err := yaml.Marshal(config) if err != nil { return nil, err } - if err := yaml.Unmarshal(generatedYaml, &generatedCfg); err != nil { - return nil, err - } - // Unmarshal overlay of type []byte to map[string]interface{} - if err := json.Unmarshal(overlay, &overlayCfg); err != nil { - return nil, err - } - - // Override generated config with extra config - err = mergo.Merge(&generatedCfg, overlayCfg, mergo.WithOverride) - if err != nil { - return nil, err + if tempo.Spec.ExtraConfig == nil || len(tempo.Spec.ExtraConfig.Tempo.Raw) == 0 { + return generatedYaml, nil + } else { + return tempoStackConfig.MergeExtraConfigWithConfig(tempo.Spec.ExtraConfig.Tempo, generatedYaml) } - - return yaml.Marshal(generatedCfg) } func buildTempoQueryConfig() ([]byte, error) { diff --git a/internal/manifests/monolithic/configmap_test.go b/internal/manifests/monolithic/configmap_test.go index ec24d1c17..34b22cf5f 100644 --- a/internal/manifests/monolithic/configmap_test.go +++ b/internal/manifests/monolithic/configmap_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/require" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" configv1alpha1 "github.com/grafana/tempo-operator/apis/config/v1alpha1" @@ -49,32 +50,18 @@ func TestBuildConfigMap(t *testing.T) { } func TestBuildConfig(t *testing.T) { - opts := Options{ - CtrlConfig: configv1alpha1.ProjectConfig{ - DefaultImages: configv1alpha1.ImagesSpec{ - Tempo: "docker.io/grafana/tempo:x.y.z", - }, - }, - Tempo: v1alpha1.TempoMonolithic{ - ObjectMeta: metav1.ObjectMeta{ - Name: "sample", - Namespace: "default", - }, - Spec: v1alpha1.TempoMonolithicSpec{}, - }, - } - tests := []struct { - name string - storage *v1alpha1.MonolithicStorageSpec - ingestion *v1alpha1.MonolithicIngestionSpec - expected string + name string + spec v1alpha1.TempoMonolithicSpec + expected string }{ { name: "memory storage", - storage: &v1alpha1.MonolithicStorageSpec{ - Traces: v1alpha1.MonolithicTracesStorageSpec{ - Backend: "memory", + spec: v1alpha1.TempoMonolithicSpec{ + Storage: &v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "memory", + }, }, }, expected: ` @@ -87,26 +74,66 @@ storage: path: /var/tempo/wal local: path: /var/tempo/blocks +distributor: + receivers: + otlp: + protocols: + grpc: + http: usage_report: reporting_enabled: false `, }, { name: "PV storage with OTLP/gRPC and OTLP/HTTP", - storage: &v1alpha1.MonolithicStorageSpec{ - Traces: v1alpha1.MonolithicTracesStorageSpec{ - Backend: "pv", + spec: v1alpha1.TempoMonolithicSpec{ + Storage: &v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "pv", + }, }, - }, - ingestion: &v1alpha1.MonolithicIngestionSpec{ - OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ - GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ - Enabled: true, + Ingestion: &v1alpha1.MonolithicIngestionSpec{ + OTLP: &v1alpha1.MonolithicIngestionOTLPSpec{ + GRPC: &v1alpha1.MonolithicIngestionOTLPProtocolsGRPCSpec{ + Enabled: true, + }, + HTTP: &v1alpha1.MonolithicIngestionOTLPProtocolsHTTPSpec{ + Enabled: true, + }, }, - HTTP: &v1alpha1.MonolithicIngestionOTLPProtocolsHTTPSpec{ - Enabled: true, + }, + }, + expected: ` +server: + http_listen_port: 3200 +storage: + trace: + backend: local + wal: + path: /var/tempo/wal + local: + path: /var/tempo/blocks +distributor: + receivers: + otlp: + protocols: + grpc: + http: +usage_report: + reporting_enabled: false +`, + }, + { + name: "extra config", + spec: v1alpha1.TempoMonolithicSpec{ + Storage: &v1alpha1.MonolithicStorageSpec{ + Traces: v1alpha1.MonolithicTracesStorageSpec{ + Backend: "memory", }, }, + ExtraConfig: &v1alpha1.ExtraConfigSpec{ + Tempo: apiextensionsv1.JSON{Raw: []byte(`{"storage": {"trace": {"wal": {"overlay_setting": "abc"}}}}`)}, + }, }, expected: ` server: @@ -116,6 +143,7 @@ storage: backend: local wal: path: /var/tempo/wal + overlay_setting: abc local: path: /var/tempo/blocks distributor: @@ -132,8 +160,22 @@ usage_report: for _, test := range tests { t.Run(test.name, func(t *testing.T) { - opts.Tempo.Spec.Storage = test.storage - opts.Tempo.Spec.Ingestion = test.ingestion + opts := Options{ + CtrlConfig: configv1alpha1.ProjectConfig{ + DefaultImages: configv1alpha1.ImagesSpec{ + Tempo: "docker.io/grafana/tempo:x.y.z", + }, + }, + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: test.spec, + }, + } + opts.Tempo.Default() + cfg, err := buildTempoConfig(opts) require.NoError(t, err) require.YAMLEq(t, test.expected, string(cfg)) From c5eba0d6a92a2933305c9d74e8825ec8a18a0b07 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Fri, 26 Jan 2024 13:19:44 +0100 Subject: [PATCH 36/36] add comment on exported function Signed-off-by: Andreas Gerstmayr --- internal/manifests/config/extra.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/manifests/config/extra.go b/internal/manifests/config/extra.go index f7b7d30cd..f80c25c0f 100644 --- a/internal/manifests/config/extra.go +++ b/internal/manifests/config/extra.go @@ -9,6 +9,7 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) +// MergeExtraConfigWithConfig overlays configuration from overridesJSON onto templateResults. func MergeExtraConfigWithConfig(overridesJSON apiextensionsv1.JSON, templateResults []byte) ([]byte, error) { // mergo.Merge requires that both variables have the same type renderedTemplateMap := make(map[string]interface{})