diff --git a/api/dash0monitoring/v1alpha1/dash0monitoring_types.go b/api/dash0monitoring/v1alpha1/dash0monitoring_types.go index 23e74c57..1ec5f5a6 100644 --- a/api/dash0monitoring/v1alpha1/dash0monitoring_types.go +++ b/api/dash0monitoring/v1alpha1/dash0monitoring_types.go @@ -14,29 +14,13 @@ const ( FinalizerId = "operator.dash0.com/dash0-monitoring-finalizer" ) -// Dash0MonitoringSpec defines the desired state of the Dash0 monitoring resource. +// Dash0MonitoringSpec describes the details of monitoring a single Kubernetes namespace with Dash0 and sending +// telemetry to an observability backend. type Dash0MonitoringSpec struct { - // The URL of the observability backend to which telemetry data will be sent. This property is mandatory. The value - // needs to be the OTLP/gRPC endpoint of your Dash0 organization. The correct OTLP/gRPC endpoint can be copied fom - // https://app.dash0.com/settings. The correct endpoint value will always start with `ingress.` and end in - // `dash0.com:4317`. + // The configuration of the observability backend to which telemetry data will be sent. This property is mandatory. // - // +kubebuilder:validation:Mandatory - Endpoint string `json:"endpoint"` - - // The Dash0 authorization token. This property is optional, but either this property or the SecretRef property has - // to be provided. If both are provided, the AuthorizationToken will be used and SecretRef will be ignored. The - // authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. - // - // +kubebuilder:validation:Optional - AuthorizationToken string `json:"authorizationToken"` - - // A reference to a Kubernetes secret containing the Dash0 authorization token. This property is optional, and is - // ignored if the AuthorizationToken property is set. The authorization token for your Dash0 organization - // can be copied from https://app.dash0.com/settings. - // - // +kubebuilder:validation:Optional - SecretRef string `json:"secretRef"` + // +kubebuilder:validation:Required + Export `json:"export"` // Global opt-out for workload instrumentation for the target namespace. There are three possible settings: `all`, // `created-and-updated` and `none`. By default, the setting `all` is assumed. @@ -69,10 +53,116 @@ type Dash0MonitoringSpec struct { // More fine-grained per-workload control over instrumentation is available by setting the label // dash0.com/enable=false on individual workloads. // - // +kubebuilder:validation:Optional + // +kubebuilder:default=all InstrumentWorkloads InstrumentWorkloadsMode `json:"instrumentWorkloads,omitempty"` } +// Export describes the observability backend to which telemetry data will be sent. This can either be Dash0 or another +// OTLP-compatible backend. +// +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:MaxProperties=1 +type Export struct { + // The configuration of the Dash0 ingress endpoint to which telemetry data will be sent. + // + // +kubebuilder:validation:Optional + Dash0 *Dash0Configuration `json:"dash0,omitempty"` + + // The settings for an exporter to send telemetry to an arbitrary OTLP-compatible receiver via HTTP. + // + // +kubebuilder:validation:Optional + Http *HttpConfiguration `json:"http,omitempty"` + + // The settings for an exporter to send telemetry to an arbitrary OTLP-compatible receiver via gRPC. + // + // +kubebuilder:validation:Optional + Grpc *GrpcConfiguration `json:"grpc,omitempty"` +} + +// Dash0Configuration describes to which Dash0 ingress endpoint telemetry data will be sent. +type Dash0Configuration struct { + // The URL of the Dash0 ingress endpoint to which telemetry data will be sent. This property is mandatory. The value needs to be the OTLP/gRPC endpoint of your Dash0 organization. The correct OTLP/gRPC endpoint can be copied fom https://app.dash0.com/settings. The correct endpoint value will always start with `ingress.` and end in `dash0.com:4317`. + // + // +kubebuilder:validation:Required + Endpoint string `json:"endpoint"` + + // The name of the Dash0 dataset to which telemetry data will be sent. This property is optional. If omitted, the default dataset of your Dash0 organization will be used. + // + // +kubebuilder:default=default + Dataset string `json:"dataset,omitempty"` + + // Mandatory authorization settings for sending data to Dash0. + // + // +kubebuilder:validation:Required + Authorization Authorization `json:"authorization"` +} + +// Authorization contains the authorization settings for Dash0. +// +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:MaxProperties=1 +type Authorization struct { + // The Dash0 authorization token. This property is optional, but either this property or the SecretRef property has to be provided. If both are provided, the AuthorizationToken will be used and SecretRef will be ignored. The authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. + // + // +kubebuilder:validation:Optional + Token *string `json:"token"` // either token or secret ref, with token taking precedence + + // A reference to a Kubernetes secret containing the Dash0 authorization token. This property is optional, and is ignored if the AuthorizationToken property is set. The authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. + // + // +kubebuilder:validation:Optional + SecretRef *string `json:"secretRef"` +} + +// HttpConfiguration describe the settings for an exporter to send telemetry to an arbitrary OTLP-compatible receiver +// via HTTP. +type HttpConfiguration struct { + // The URL of the OTLP-compatible receiver to which telemetry data will be sent. This property is mandatory. + // + // +kubebuilder:validation:Required + Url string `json:"url"` + + // Additional headers to be sent with each HTTP request, for example for authorization. This property is optional. + // + // +kubebuilder:validation:Optional + Headers []Header `json:"headers,omitempty"` + + // The encoding of the OTLP data when sent via HTTP. Can be either protobuf or json, defaults to protobuf. + // + // +kubebuilder:default=protobuf + Encoding OtlpEncoding `json:"encoding,omitempty"` +} + +// GrpcConfiguration descibe the settings for an exporter to send telemetry to an arbitrary OTLP-compatible receiver +// via gRPC. +type GrpcConfiguration struct { + // The URL of the OTLP-compatible receiver to which telemetry data will be sent. This property is mandatory. + // + // +kubebuilder:validation:Required + Url string `json:"url"` + + // Additional headers to be sent with each gRPC request, for example for authorization. This property is optional. + // + // +kubebuilder:validation:Optional + Headers []Header `json:"headers,omitempty"` +} + +// OtlpEncoding describes the encoding of the OTLP data when sent via HTTP. +// +// +kubebuilder:validation:Enum=protobuf;json +type OtlpEncoding string + +const ( + Protobuf OtlpEncoding = "protobuf" + Json OtlpEncoding = "json" +) + +type Header struct { + // +kubebuilder:validation:Required + Key string `json:"key"` + // +kubebuilder:validation:Required + Value string `json:"value"` +} + // InstrumentWorkloadsMode describes when exactly workloads will be instrumented. Only one of the following modes // may be specified. If none of the following policies is specified, the default one is All. See // Dash0MonitoringSpec#InstrumentWorkloads for more details. diff --git a/api/dash0monitoring/v1alpha1/zz_generated.deepcopy.go b/api/dash0monitoring/v1alpha1/zz_generated.deepcopy.go index 1422377a..c60d9c66 100644 --- a/api/dash0monitoring/v1alpha1/zz_generated.deepcopy.go +++ b/api/dash0monitoring/v1alpha1/zz_generated.deepcopy.go @@ -12,12 +12,53 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Authorization) DeepCopyInto(out *Authorization) { + *out = *in + if in.Token != nil { + in, out := &in.Token, &out.Token + *out = new(string) + **out = **in + } + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Authorization. +func (in *Authorization) DeepCopy() *Authorization { + if in == nil { + return nil + } + out := new(Authorization) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Dash0Configuration) DeepCopyInto(out *Dash0Configuration) { + *out = *in + in.Authorization.DeepCopyInto(&out.Authorization) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dash0Configuration. +func (in *Dash0Configuration) DeepCopy() *Dash0Configuration { + if in == nil { + return nil + } + out := new(Dash0Configuration) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Dash0Monitoring) DeepCopyInto(out *Dash0Monitoring) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -74,6 +115,7 @@ func (in *Dash0MonitoringList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Dash0MonitoringSpec) DeepCopyInto(out *Dash0MonitoringSpec) { *out = *in + in.Export.DeepCopyInto(&out.Export) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dash0MonitoringSpec. @@ -107,3 +149,88 @@ func (in *Dash0MonitoringStatus) DeepCopy() *Dash0MonitoringStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Export) DeepCopyInto(out *Export) { + *out = *in + if in.Dash0 != nil { + in, out := &in.Dash0, &out.Dash0 + *out = new(Dash0Configuration) + (*in).DeepCopyInto(*out) + } + if in.Http != nil { + in, out := &in.Http, &out.Http + *out = new(HttpConfiguration) + (*in).DeepCopyInto(*out) + } + if in.Grpc != nil { + in, out := &in.Grpc, &out.Grpc + *out = new(GrpcConfiguration) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Export. +func (in *Export) DeepCopy() *Export { + if in == nil { + return nil + } + out := new(Export) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GrpcConfiguration) DeepCopyInto(out *GrpcConfiguration) { + *out = *in + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make([]Header, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GrpcConfiguration. +func (in *GrpcConfiguration) DeepCopy() *GrpcConfiguration { + if in == nil { + return nil + } + out := new(GrpcConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Header) DeepCopyInto(out *Header) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Header. +func (in *Header) DeepCopy() *Header { + if in == nil { + return nil + } + out := new(Header) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HttpConfiguration) DeepCopyInto(out *HttpConfiguration) { + *out = *in + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make([]Header, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HttpConfiguration. +func (in *HttpConfiguration) DeepCopy() *HttpConfiguration { + if in == nil { + return nil + } + out := new(HttpConfiguration) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/operator.dash0.com_dash0monitorings.yaml b/config/crd/bases/operator.dash0.com_dash0monitorings.yaml index 6b717723..b9b9caff 100644 --- a/config/crd/bases/operator.dash0.com_dash0monitorings.yaml +++ b/config/crd/bases/operator.dash0.com_dash0monitorings.yaml @@ -37,23 +37,123 @@ spec: metadata: type: object spec: - description: Dash0MonitoringSpec defines the desired state of the Dash0 - monitoring resource. + description: |- + Dash0MonitoringSpec describes the details of monitoring a single Kubernetes namespace with Dash0 and sending + telemetry to an observability backend. properties: - authorizationToken: - description: |- - The Dash0 authorization token. This property is optional, but either this property or the SecretRef property has - to be provided. If both are provided, the AuthorizationToken will be used and SecretRef will be ignored. The - authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. - type: string - endpoint: - description: |- - The URL of the observability backend to which telemetry data will be sent. This property is mandatory. The value - needs to be the OTLP/gRPC endpoint of your Dash0 organization. The correct OTLP/gRPC endpoint can be copied fom - https://app.dash0.com/settings. The correct endpoint value will always start with `ingress.` and end in - `dash0.com:4317`. - type: string + export: + description: The configuration of the observability backend to which + telemetry data will be sent. This property is mandatory. + maxProperties: 1 + minProperties: 1 + properties: + dash0: + description: The configuration of the Dash0 ingress endpoint to + which telemetry data will be sent. + properties: + authorization: + description: Mandatory authorization settings for sending + data to Dash0. + maxProperties: 1 + minProperties: 1 + properties: + secretRef: + description: A reference to a Kubernetes secret containing + the Dash0 authorization token. This property is optional, + and is ignored if the AuthorizationToken property is + set. The authorization token for your Dash0 organization + can be copied from https://app.dash0.com/settings. + type: string + token: + description: The Dash0 authorization token. This property + is optional, but either this property or the SecretRef + property has to be provided. If both are provided, the + AuthorizationToken will be used and SecretRef will be + ignored. The authorization token for your Dash0 organization + can be copied from https://app.dash0.com/settings. + type: string + type: object + dataset: + default: default + description: The name of the Dash0 dataset to which telemetry + data will be sent. This property is optional. If omitted, + the default dataset of your Dash0 organization will be used. + type: string + endpoint: + description: The URL of the Dash0 ingress endpoint to which + telemetry data will be sent. This property is mandatory. + The value needs to be the OTLP/gRPC endpoint of your Dash0 + organization. The correct OTLP/gRPC endpoint can be copied + fom https://app.dash0.com/settings. The correct endpoint + value will always start with `ingress.` and end in `dash0.com:4317`. + type: string + required: + - authorization + - endpoint + type: object + grpc: + description: The settings for an exporter to send telemetry to + an arbitrary OTLP-compatible receiver via gRPC. + properties: + headers: + description: Additional headers to be sent with each gRPC + request, for example for authorization. This property is + optional. + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + url: + description: The URL of the OTLP-compatible receiver to which + telemetry data will be sent. This property is mandatory. + type: string + required: + - url + type: object + http: + description: The settings for an exporter to send telemetry to + an arbitrary OTLP-compatible receiver via HTTP. + properties: + encoding: + default: protobuf + description: The encoding of the OTLP data when sent via HTTP. + Can be either protobuf or json, defaults to protobuf. + enum: + - protobuf + - json + type: string + headers: + description: Additional headers to be sent with each HTTP + request, for example for authorization. This property is + optional. + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + url: + description: The URL of the OTLP-compatible receiver to which + telemetry data will be sent. This property is mandatory. + type: string + required: + - url + type: object + type: object instrumentWorkloads: + default: all description: |- Global opt-out for workload instrumentation for the target namespace. There are three possible settings: `all`, `created-and-updated` and `none`. By default, the setting `all` is assumed. @@ -95,14 +195,8 @@ spec: - created-and-updated - none type: string - secretRef: - description: |- - A reference to a Kubernetes secret containing the Dash0 authorization token. This property is optional, and is - ignored if the AuthorizationToken property is set. The authorization token for your Dash0 organization - can be copied from https://app.dash0.com/settings. - type: string required: - - endpoint + - export type: object status: description: Dash0MonitoringStatus defines the observed state of the Dash0 diff --git a/helm-chart/dash0-operator/README.md b/helm-chart/dash0-operator/README.md index 4ea2105b..db62d9d1 100644 --- a/helm-chart/dash0-operator/README.md +++ b/helm-chart/dash0-operator/README.md @@ -52,15 +52,18 @@ into that namespace: Create a file `dash0-monitoring.yaml` with the following content: ```yaml apiVersion: operator.dash0.com/v1alpha1 -kind: Dash0 +kind: Dash0Monitoring metadata: name: dash0-monitoring-resource spec: - # Replace this value with the actual OTLP/gRPC endpoint of your Dash0 organization. - endpoint: ingress... # TODO needs to be replaced with the actual value, see below + export: + dash0: + # Replace this value with the actual OTLP/gRPC endpoint of your Dash0 organization. + endpoint: ingress... # TODO needs to be replaced with the actual value, see below - # Either provide the Dash0 authorization token as a string via the property authorizationToken: - authorizationToken: auth_... # TODO needs to be replaced with the actual value, see below + authorization: + # Provide the Dash0 authorization token as a string via the token property: + token: auth_... # TODO needs to be replaced with the actual value, see below # Opt-out settings for particular use cases. The default value is "all". Other possible values are # "created-and-updated" and "none". @@ -75,7 +78,7 @@ At this point, you need to provide two configuration settings: Note that the correct endpoint value will always start with `ingress.` and end in `dash0.com:4317`. A protocol prefix (eg. `https://`) should not be included in the value. * `authorizationToken` or `secretRef`: Exactly one of these two properties needs to be provided. - If both are provided, `authorizationToken` will be used and `secretRef` will be ignored. + Providing both will cause a validation error when installing the Dash0Monitoring resource. * `authorizationToken`: Replace the value in the example above with the Dash0 authorization token of your organization. The authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. @@ -85,6 +88,7 @@ At this point, you need to provide two configuration settings: Use the `secretRef` property and a Kubernetes secret if you want to avoid that. * `secretRef`: Replace the value in the example above with the name of an existing Kubernetes secret in the Dash0 operator's namespace. + See the next section for an example file that uses a `secretRef`. The secret needs to contain the Dash0 authorization token. See below for details on how exactly the secret should be created. Note that by default, Kubernetes secrets are stored _unencrypted_, and anyone with API access to the Kubernetes @@ -177,11 +181,14 @@ kind: Dash0Monitoring metadata: name: dash0-monitoring-resource spec: - # Replace this value with the actual OTLP/gRPC endpoint of your Dash0 organization. - endpoint: ingress... # TODO needs to be replaced with the actual value, see below - - # Or provide the name of a secret existing in the Dash0 operator's namespace as the property secretRef: - secretRef: dash0-authorization-secret + export: + dash0: + # Replace this value with the actual OTLP/gRPC endpoint of your Dash0 organization. + endpoint: ingress... # TODO needs to be replaced with the actual value, see below + + authorization: + # Provide the name of a secret existing in the Dash0 operator's namespace as secretRef: + secretRef: dash0-authorization-secret ``` Note that by default, Kubernetes secrets are stored _unencrypted_, and anyone with API access to the Kubernetes cluster diff --git a/helm-chart/dash0-operator/templates/operator/custom-resource-definition-dash0.yaml b/helm-chart/dash0-operator/templates/operator/custom-resource-definition-dash0.yaml index 88c1e97e..460785ab 100644 --- a/helm-chart/dash0-operator/templates/operator/custom-resource-definition-dash0.yaml +++ b/helm-chart/dash0-operator/templates/operator/custom-resource-definition-dash0.yaml @@ -36,23 +36,123 @@ spec: metadata: type: object spec: - description: Dash0MonitoringSpec defines the desired state of the Dash0 - monitoring resource. + description: |- + Dash0MonitoringSpec describes the details of monitoring a single Kubernetes namespace with Dash0 and sending + telemetry to an observability backend. properties: - authorizationToken: - description: |- - The Dash0 authorization token. This property is optional, but either this property or the SecretRef property has - to be provided. If both are provided, the AuthorizationToken will be used and SecretRef will be ignored. The - authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. - type: string - endpoint: - description: |- - The URL of the observability backend to which telemetry data will be sent. This property is mandatory. The value - needs to be the OTLP/gRPC endpoint of your Dash0 organization. The correct OTLP/gRPC endpoint can be copied fom - https://app.dash0.com/settings. The correct endpoint value will always start with `ingress.` and end in - `dash0.com:4317`. - type: string + export: + description: The configuration of the observability backend to which + telemetry data will be sent. This property is mandatory. + maxProperties: 1 + minProperties: 1 + properties: + dash0: + description: The configuration of the Dash0 ingress endpoint to + which telemetry data will be sent. + properties: + authorization: + description: Mandatory authorization settings for sending + data to Dash0. + maxProperties: 1 + minProperties: 1 + properties: + secretRef: + description: A reference to a Kubernetes secret containing + the Dash0 authorization token. This property is optional, + and is ignored if the AuthorizationToken property is + set. The authorization token for your Dash0 organization + can be copied from https://app.dash0.com/settings. + type: string + token: + description: The Dash0 authorization token. This property + is optional, but either this property or the SecretRef + property has to be provided. If both are provided, the + AuthorizationToken will be used and SecretRef will be + ignored. The authorization token for your Dash0 organization + can be copied from https://app.dash0.com/settings. + type: string + type: object + dataset: + default: default + description: The name of the Dash0 dataset to which telemetry + data will be sent. This property is optional. If omitted, + the default dataset of your Dash0 organization will be used. + type: string + endpoint: + description: The URL of the Dash0 ingress endpoint to which + telemetry data will be sent. This property is mandatory. + The value needs to be the OTLP/gRPC endpoint of your Dash0 + organization. The correct OTLP/gRPC endpoint can be copied + fom https://app.dash0.com/settings. The correct endpoint + value will always start with `ingress.` and end in `dash0.com:4317`. + type: string + required: + - authorization + - endpoint + type: object + grpc: + description: The settings for an exporter to send telemetry to + an arbitrary OTLP-compatible receiver via gRPC. + properties: + headers: + description: Additional headers to be sent with each gRPC + request, for example for authorization. This property is + optional. + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + url: + description: The URL of the OTLP-compatible receiver to which + telemetry data will be sent. This property is mandatory. + type: string + required: + - url + type: object + http: + description: The settings for an exporter to send telemetry to + an arbitrary OTLP-compatible receiver via HTTP. + properties: + encoding: + default: protobuf + description: The encoding of the OTLP data when sent via HTTP. + Can be either protobuf or json, defaults to protobuf. + enum: + - protobuf + - json + type: string + headers: + description: Additional headers to be sent with each HTTP + request, for example for authorization. This property is + optional. + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + url: + description: The URL of the OTLP-compatible receiver to which + telemetry data will be sent. This property is mandatory. + type: string + required: + - url + type: object + type: object instrumentWorkloads: + default: all description: |- Global opt-out for workload instrumentation for the target namespace. There are three possible settings: `all`, `created-and-updated` and `none`. By default, the setting `all` is assumed. @@ -94,14 +194,8 @@ spec: - created-and-updated - none type: string - secretRef: - description: |- - A reference to a Kubernetes secret containing the Dash0 authorization token. This property is optional, and is - ignored if the AuthorizationToken property is set. The authorization token for your Dash0 organization - can be copied from https://app.dash0.com/settings. - type: string required: - - endpoint + - export type: object status: description: Dash0MonitoringStatus defines the observed state of the Dash0 diff --git a/helm-chart/dash0-operator/tests/operator/__snapshot__/custom-resource-definition-dash0_test.yaml.snap b/helm-chart/dash0-operator/tests/operator/__snapshot__/custom-resource-definition-dash0_test.yaml.snap index 7f43aca9..869c4816 100644 --- a/helm-chart/dash0-operator/tests/operator/__snapshot__/custom-resource-definition-dash0_test.yaml.snap +++ b/helm-chart/dash0-operator/tests/operator/__snapshot__/custom-resource-definition-dash0_test.yaml.snap @@ -38,22 +38,95 @@ custom resource definition should match snapshot: metadata: type: object spec: - description: Dash0MonitoringSpec defines the desired state of the Dash0 monitoring resource. + description: |- + Dash0MonitoringSpec describes the details of monitoring a single Kubernetes namespace with Dash0 and sending + telemetry to an observability backend. properties: - authorizationToken: - description: |- - The Dash0 authorization token. This property is optional, but either this property or the SecretRef property has - to be provided. If both are provided, the AuthorizationToken will be used and SecretRef will be ignored. The - authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. - type: string - endpoint: - description: |- - The URL of the observability backend to which telemetry data will be sent. This property is mandatory. The value - needs to be the OTLP/gRPC endpoint of your Dash0 organization. The correct OTLP/gRPC endpoint can be copied fom - https://app.dash0.com/settings. The correct endpoint value will always start with `ingress.` and end in - `dash0.com:4317`. - type: string + export: + description: The configuration of the observability backend to which telemetry data will be sent. This property is mandatory. + maxProperties: 1 + minProperties: 1 + properties: + dash0: + description: The configuration of the Dash0 ingress endpoint to which telemetry data will be sent. + properties: + authorization: + description: Mandatory authorization settings for sending data to Dash0. + maxProperties: 1 + minProperties: 1 + properties: + secretRef: + description: A reference to a Kubernetes secret containing the Dash0 authorization token. This property is optional, and is ignored if the AuthorizationToken property is set. The authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. + type: string + token: + description: The Dash0 authorization token. This property is optional, but either this property or the SecretRef property has to be provided. If both are provided, the AuthorizationToken will be used and SecretRef will be ignored. The authorization token for your Dash0 organization can be copied from https://app.dash0.com/settings. + type: string + type: object + dataset: + default: default + description: The name of the Dash0 dataset to which telemetry data will be sent. This property is optional. If omitted, the default dataset of your Dash0 organization will be used. + type: string + endpoint: + description: The URL of the Dash0 ingress endpoint to which telemetry data will be sent. This property is mandatory. The value needs to be the OTLP/gRPC endpoint of your Dash0 organization. The correct OTLP/gRPC endpoint can be copied fom https://app.dash0.com/settings. The correct endpoint value will always start with `ingress.` and end in `dash0.com:4317`. + type: string + required: + - authorization + - endpoint + type: object + grpc: + description: The settings for an exporter to send telemetry to an arbitrary OTLP-compatible receiver via gRPC. + properties: + headers: + description: Additional headers to be sent with each gRPC request, for example for authorization. This property is optional. + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + url: + description: The URL of the OTLP-compatible receiver to which telemetry data will be sent. This property is mandatory. + type: string + required: + - url + type: object + http: + description: The settings for an exporter to send telemetry to an arbitrary OTLP-compatible receiver via HTTP. + properties: + encoding: + default: protobuf + description: The encoding of the OTLP data when sent via HTTP. Can be either protobuf or json, defaults to protobuf. + enum: + - protobuf + - json + type: string + headers: + description: Additional headers to be sent with each HTTP request, for example for authorization. This property is optional. + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + url: + description: The URL of the OTLP-compatible receiver to which telemetry data will be sent. This property is mandatory. + type: string + required: + - url + type: object + type: object instrumentWorkloads: + default: all description: |- Global opt-out for workload instrumentation for the target namespace. There are three possible settings: `all`, `created-and-updated` and `none`. By default, the setting `all` is assumed. @@ -95,14 +168,8 @@ custom resource definition should match snapshot: - created-and-updated - none type: string - secretRef: - description: |- - A reference to a Kubernetes secret containing the Dash0 authorization token. This property is optional, and is - ignored if the AuthorizationToken property is set. The authorization token for your Dash0 organization - can be copied from https://app.dash0.com/settings. - type: string required: - - endpoint + - export type: object status: description: Dash0MonitoringStatus defines the observed state of the Dash0 monitoring resource. diff --git a/internal/backendconnection/backendconnection_manager.go b/internal/backendconnection/backendconnection_manager.go index 037331a7..11520a8f 100644 --- a/internal/backendconnection/backendconnection_manager.go +++ b/internal/backendconnection/backendconnection_manager.go @@ -34,12 +34,6 @@ func (m *BackendConnectionManager) EnsureOpenTelemetryCollectorIsDeployedInDash0 ) error { logger := log.FromContext(ctx) - if dash0MonitoringResource.Spec.Endpoint == "" { - err := fmt.Errorf("no endpoint provided, unable to create the OpenTelemetry collector") - logger.Error(err, failedToCreateMsg) - return err - } - resourcesHaveBeenCreated, resourcesHaveBeenUpdated, err := m.OTelColResourceManager.CreateOrUpdateOpenTelemetryCollectorResources( ctx, diff --git a/internal/backendconnection/backendconnection_manager_test.go b/internal/backendconnection/backendconnection_manager_test.go index aced4980..dbd69ea7 100644 --- a/internal/backendconnection/backendconnection_manager_test.go +++ b/internal/backendconnection/backendconnection_manager_test.go @@ -26,8 +26,14 @@ var ( dash0MonitoringResource = &dash0v1alpha1.Dash0Monitoring{ Spec: dash0v1alpha1.Dash0MonitoringSpec{ - Endpoint: EndpointTest, - AuthorizationToken: AuthorizationTokenTest, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, }, } ) @@ -75,8 +81,16 @@ var _ = Describe("The backend connection manager", Ordered, func() { operatorNamespace, &dash0v1alpha1.Dash0Monitoring{ Spec: dash0v1alpha1.Dash0MonitoringSpec{ - AuthorizationToken: AuthorizationTokenTest, - }}) + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, + }, + }, + ) Expect(err).To(HaveOccurred()) VerifyCollectorResourcesDoNotExist(ctx, k8sClient, operatorNamespace) }) @@ -88,8 +102,14 @@ var _ = Describe("The backend connection manager", Ordered, func() { operatorNamespace, &dash0v1alpha1.Dash0Monitoring{ Spec: dash0v1alpha1.Dash0MonitoringSpec{ - Endpoint: EndpointTest, - }}) + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{}, + }, + }, + }, + }) Expect(err).NotTo(HaveOccurred()) VerifyCollectorResourcesExist(ctx, k8sClient, operatorNamespace) }) @@ -222,8 +242,14 @@ var _ = Describe("The backend connection manager", Ordered, func() { UID: "3c0e72bb-26a7-40a4-bbdd-b1c978278fc5", }, Spec: dash0v1alpha1.Dash0MonitoringSpec{ - Endpoint: EndpointTest, - AuthorizationToken: AuthorizationTokenTest, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, }, }, ) diff --git a/internal/backendconnection/otelcolresources/desired_state.go b/internal/backendconnection/otelcolresources/desired_state.go index 97a5dcd6..f8946ce8 100644 --- a/internal/backendconnection/otelcolresources/desired_state.go +++ b/internal/backendconnection/otelcolresources/desired_state.go @@ -20,20 +20,24 @@ import ( "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" + dash0v1alpha1 "github.com/dash0hq/dash0-operator/api/dash0monitoring/v1alpha1" "github.com/dash0hq/dash0-operator/internal/dash0/util" ) type oTelColConfig struct { - Namespace string - NamePrefix string - Endpoint string - AuthorizationToken string - SecretRef string - Images util.Images + Namespace string + NamePrefix string + Export dash0v1alpha1.Export + Images util.Images } func (c *oTelColConfig) hasAuthentication() bool { - return c.SecretRef != "" || c.AuthorizationToken != "" + if c.Export.Dash0 == nil { + return false + } + authorization := c.Export.Dash0.Authorization + return (authorization.Token != nil && *authorization.Token != "") || + (authorization.SecretRef != nil && *authorization.SecretRef != "") } type exportProtocol string @@ -107,7 +111,7 @@ var ( ) func assembleDesiredState(config *oTelColConfig) ([]client.Object, error) { - if config.Endpoint == "" { + if config.Export.Dash0.Endpoint == "" { return nil, fmt.Errorf("no endpoint provided, unable to create the OpenTelemetry collector") } @@ -155,7 +159,8 @@ func renderCollectorConfigs(templateValues *collectorConfigurationTemplateValues } func collectorConfigMap(config *oTelColConfig) (*corev1.ConfigMap, error) { - endpoint := config.Endpoint + endpoint := config.Export.Dash0.Endpoint + exportProtocol := grpcExportProtocol if url, err := url.ParseRequestURI(endpoint); err != nil { // Not a valid URL, assume it's grpc @@ -476,20 +481,22 @@ func daemonSet(config *oTelColConfig) *appsv1.DaemonSet { if config.hasAuthentication() { var authTokenEnvVar corev1.EnvVar - if config.AuthorizationToken != "" { + token := config.Export.Dash0.Authorization.Token + if token != nil && *token != "" { authTokenEnvVar = corev1.EnvVar{ Name: authTokenEnvVarName, - Value: config.AuthorizationToken, + Value: *token, } } else { + secretRef := config.Export.Dash0.Authorization.SecretRef authTokenEnvVar = corev1.EnvVar{ Name: authTokenEnvVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: config.SecretRef, + Name: *secretRef, }, - Key: "dash0-authorization-token", // TODO Make configurable + Key: "dash0-authorization-token", }, }, } diff --git a/internal/backendconnection/otelcolresources/desired_state_test.go b/internal/backendconnection/otelcolresources/desired_state_test.go index bc84ea75..66fa3865 100644 --- a/internal/backendconnection/otelcolresources/desired_state_test.go +++ b/internal/backendconnection/otelcolresources/desired_state_test.go @@ -11,6 +11,8 @@ import ( corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" + dash0v1alpha1 "github.com/dash0hq/dash0-operator/api/dash0monitoring/v1alpha1" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -25,21 +27,33 @@ const ( var _ = Describe("The desired state of the OpenTelemetry Collector resources", func() { It("should fail if no endpoint has been provided", func() { _, err := assembleDesiredState(&oTelColConfig{ - Namespace: namespace, - NamePrefix: namePrefix, - AuthorizationToken: AuthorizationTokenTest, - Images: TestImages, + Namespace: namespace, + NamePrefix: namePrefix, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, + Images: TestImages, }) Expect(err).To(HaveOccurred()) }) It("should describe the desired state as a set of Kubernetes client objects", func() { desiredState, err := assembleDesiredState(&oTelColConfig{ - Namespace: namespace, - NamePrefix: namePrefix, - Endpoint: EndpointTest, - AuthorizationToken: AuthorizationTokenTest, - Images: TestImages, + Namespace: namespace, + NamePrefix: namePrefix, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, + Images: TestImages, }) Expect(err).ToNot(HaveOccurred()) @@ -110,10 +124,16 @@ var _ = Describe("The desired state of the OpenTelemetry Collector resources", f It("should use the authorization token directly if provided", func() { desiredState, err := assembleDesiredState(&oTelColConfig{ - Namespace: namespace, - NamePrefix: namePrefix, - Endpoint: EndpointTest, - AuthorizationToken: AuthorizationTokenTest, + Namespace: namespace, + NamePrefix: namePrefix, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, }) Expect(err).ToNot(HaveOccurred()) @@ -131,8 +151,14 @@ var _ = Describe("The desired state of the OpenTelemetry Collector resources", f desiredState, err := assembleDesiredState(&oTelColConfig{ Namespace: namespace, NamePrefix: namePrefix, - Endpoint: EndpointTest, - SecretRef: SecretRefTest, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{ + SecretRef: &SecretRefTest, + }, + }, + }, }) Expect(err).ToNot(HaveOccurred()) @@ -152,7 +178,12 @@ var _ = Describe("The desired state of the OpenTelemetry Collector resources", f desiredState, err := assembleDesiredState(&oTelColConfig{ Namespace: namespace, NamePrefix: namePrefix, - Endpoint: EndpointTest, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{}, + }, + }, }) Expect(err).ToNot(HaveOccurred()) diff --git a/internal/backendconnection/otelcolresources/otelcol_resources.go b/internal/backendconnection/otelcolresources/otelcol_resources.go index 795b54ef..32d401c1 100644 --- a/internal/backendconnection/otelcolresources/otelcol_resources.go +++ b/internal/backendconnection/otelcolresources/otelcol_resources.go @@ -38,12 +38,10 @@ func (m *OTelColResourceManager) CreateOrUpdateOpenTelemetryCollectorResources( logger *logr.Logger, ) (bool, bool, error) { config := &oTelColConfig{ - Namespace: namespace, - NamePrefix: m.OTelCollectorNamePrefix, - Endpoint: dash0MonitoringResource.Spec.Endpoint, - AuthorizationToken: dash0MonitoringResource.Spec.AuthorizationToken, - SecretRef: dash0MonitoringResource.Spec.SecretRef, - Images: images, + Namespace: namespace, + NamePrefix: m.OTelCollectorNamePrefix, + Export: dash0MonitoringResource.Spec.Export, + Images: images, } desiredState, err := assembleDesiredState(config) if err != nil { @@ -208,12 +206,10 @@ func (m *OTelColResourceManager) DeleteResources( logger *logr.Logger, ) error { config := &oTelColConfig{ - Namespace: namespace, - NamePrefix: m.OTelCollectorNamePrefix, - Endpoint: dash0MonitoringResource.Spec.Endpoint, - AuthorizationToken: dash0MonitoringResource.Spec.AuthorizationToken, - SecretRef: dash0MonitoringResource.Spec.SecretRef, - Images: images, + Namespace: namespace, + NamePrefix: m.OTelCollectorNamePrefix, + Export: dash0MonitoringResource.Spec.Export, + Images: images, } allObjects, err := assembleDesiredState(config) if err != nil { diff --git a/internal/dash0/controller/dash0_controller_test.go b/internal/dash0/controller/dash0_controller_test.go index 81a0949e..6a85bb44 100644 --- a/internal/dash0/controller/dash0_controller_test.go +++ b/internal/dash0/controller/dash0_controller_test.go @@ -749,10 +749,15 @@ var _ = Describe("The Dash0 controller", Ordered, func() { Namespace: Dash0MonitoringResourceQualifiedName.Namespace, }, Spec: dash0v1alpha1.Dash0MonitoringSpec{ - Endpoint: EndpointTest, - AuthorizationToken: AuthorizationTokenTest, - SecretRef: SecretRefTest, InstrumentWorkloads: "invalid", + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, }, })).ToNot(Succeed()) }) diff --git a/test-resources/customresources/dash0monitoring/dash0monitoring.secret.yaml b/test-resources/customresources/dash0monitoring/dash0monitoring.secret.yaml index b61b3779..563a73fe 100644 --- a/test-resources/customresources/dash0monitoring/dash0monitoring.secret.yaml +++ b/test-resources/customresources/dash0monitoring/dash0monitoring.secret.yaml @@ -3,17 +3,19 @@ kind: Dash0Monitoring metadata: name: dash0-monitoring-resource spec: - # production: endpoint: ingress.eu-west-1.aws.dash0.com:4317 - # development: ingress.eu-west-1.aws.dash0-dev.com:4317 - endpoint: ingress.eu-west-1.aws.dash0-dev.com:4317 + export: + dash0: + # production: endpoint: ingress.eu-west-1.aws.dash0.com:4317 + # development: ingress.eu-west-1.aws.dash0-dev.com:4317 + endpoint: ingress.eu-west-1.aws.dash0-dev.com:4317 - # Either provide the name of a secret existing in the Dash0 operator's namespace as secretRef: - secretRef: dash0-authorization-secret + authorization: + # Either provide the name of a secret existing in the Dash0 operator's namespace as secretRef: + secretRef: dash0-authorization-secret - # Or provide the token directly as authorizationToken: - # authorizationToken: auth_... + # Or provide the token directly as authorizationToken: + # authorizationToken: auth_... # Opt-out settings for particular use cases. The default value is "all". Other possible values are # "created-and-updated" and "none". # instrumentWorkloads: all - diff --git a/test-resources/customresources/dash0monitoring/dash0monitoring.token.yaml.template b/test-resources/customresources/dash0monitoring/dash0monitoring.token.yaml.template index de73484c..8e097cbe 100644 --- a/test-resources/customresources/dash0monitoring/dash0monitoring.token.yaml.template +++ b/test-resources/customresources/dash0monitoring/dash0monitoring.token.yaml.template @@ -3,15 +3,18 @@ kind: Dash0Monitoring metadata: name: dash0-monitoring-resource spec: - # production: endpoint: ingress.eu-west-1.aws.dash0.com:4317 - # development: ingress.eu-west-1.aws.dash0-dev.com:4317 - endpoint: ingress.eu-west-1.aws.dash0-dev.com:4317 + export: + dash0: + # production: endpoint: ingress.eu-west-1.aws.dash0.com:4317 + # development: ingress.eu-west-1.aws.dash0-dev.com:4317 + endpoint: ingress.eu-west-1.aws.dash0-dev.com:4317 - # Either provide the name of a secret existing in the Dash0 operator's namespace as secretRef: - # secretRef: dash0-authorization-secret + authorization: + # Either provide the name of a secret existing in the Dash0 operator's namespace as secretRef: + # secretRef: dash0-authorization-secret - # Or provide the token directly as authorizationToken: - authorizationToken: "$DASH0_AUTHORIZATION_TOKEN" + # Or provide the token directly as authorizationToken: + token: "$DASH0_AUTHORIZATION_TOKEN" # Opt-out settings for particular use cases. The default value is "all". Other possible values are # "created-and-updated" and "none". diff --git a/test/e2e/dash0monitoring.e2e.yaml.template b/test/e2e/dash0monitoring.e2e.yaml.template index 93dd750e..4e5b8691 100644 --- a/test/e2e/dash0monitoring.e2e.yaml.template +++ b/test/e2e/dash0monitoring.e2e.yaml.template @@ -4,4 +4,8 @@ metadata: name: dash0-monitoring-resource-e2e spec: instrumentWorkloads: {{ .InstrumentWorkloads }} - endpoint: {{ .Endpoint }} + export: + dash0: + endpoint: {{ .Endpoint }} + authorization: + token: dummy \ No newline at end of file diff --git a/test/util/constants.go b/test/util/constants.go index defc606d..80ea9ebe 100644 --- a/test/util/constants.go +++ b/test/util/constants.go @@ -30,11 +30,12 @@ const ( OTelCollectorBaseUrlTest = "http://$(DASH0_NODE_IP):40318" EndpointTest = "endpoint.dash0.com:4317" - AuthorizationTokenTest = "authorization-token" - SecretRefTest = "secret-ref" ) var ( + AuthorizationTokenTest = "authorization-token" + SecretRefTest = "secret-ref" + ArbitraryNumer int64 = 1302 TestImages = util.Images{ diff --git a/test/util/dash0_monitoring_resource.go b/test/util/dash0_monitoring_resource.go index 5c16c9f2..258610ca 100644 --- a/test/util/dash0_monitoring_resource.go +++ b/test/util/dash0_monitoring_resource.go @@ -61,11 +61,15 @@ func EnsureDash0MonitoringResourceExistsWithNamespacedName( instrumentWorkloads dash0v1alpha1.InstrumentWorkloadsMode, ) *dash0v1alpha1.Dash0Monitoring { By("creating the Dash0 monitoring resource") - spec := dash0v1alpha1.Dash0MonitoringSpec{ - Endpoint: EndpointTest, - AuthorizationToken: AuthorizationTokenTest, - SecretRef: SecretRefTest, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, } if instrumentWorkloads != "" { spec.InstrumentWorkloads = instrumentWorkloads @@ -98,9 +102,14 @@ func CreateDash0MonitoringResource( Namespace: dash0MonitoringResourceName.Namespace, }, Spec: dash0v1alpha1.Dash0MonitoringSpec{ - Endpoint: EndpointTest, - AuthorizationToken: AuthorizationTokenTest, - SecretRef: SecretRefTest, + Export: dash0v1alpha1.Export{ + Dash0: &dash0v1alpha1.Dash0Configuration{ + Endpoint: EndpointTest, + Authorization: dash0v1alpha1.Authorization{ + Token: &AuthorizationTokenTest, + }, + }, + }, }, } Expect(k8sClient.Create(ctx, dash0MonitoringResource)).To(Succeed())