diff --git a/Makefile b/Makefile index ddf45c9a5..04a032356 100644 --- a/Makefile +++ b/Makefile @@ -198,4 +198,12 @@ cli-install: .PHONY: cli-upgrade cli-upgrade: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" - cd ./cli ; go run -tags=embed_manifests . upgrade --version $(ODIGOS_CLI_VERSION) --yes \ No newline at end of file + cd ./cli ; go run -tags=embed_manifests . upgrade --version $(ODIGOS_CLI_VERSION) --yes + +.PHONY: api-all +api-all: + make -C api all + +.PHONY: crd-apply +crd-apply: api-all cli-upgrade + @echo "Applying changes to CRDs in api directory" diff --git a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml index 6eaec9bcb..253d81a5b 100644 --- a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml +++ b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml @@ -214,6 +214,75 @@ spec: required: - libraryName type: object + payloadCollection: + properties: + dbQuery: + description: rule for collecting db payloads for the + mentioned workload and instrumentation libraries + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + type: object + httpRequest: + description: |- + Collect HTTP request payload data when available. + Can be a client (outgoing) request or a server (incoming) request, depending on the instrumentation library + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + mimeTypes: + description: |- + Limit payload collection to specific mime types based on the content type header. + When not specified, all mime types payloads will be collected. + Empty array will make the rule ineffective. + items: + type: string + type: array + type: object + httpResponse: + description: |- + rule for collecting the response part of an http payload. + Can be a client response or a server response, depending on the instrumentation library + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + mimeTypes: + description: |- + Limit payload collection to specific mime types based on the content type header. + When not specified, all mime types payloads will be collected. + Empty array will make the rule ineffective. + items: + type: string + type: array + type: object + type: object traceConfig: properties: enabled: @@ -241,9 +310,78 @@ spec: - unknown - ignored type: string + payloadCollection: + properties: + dbQuery: + description: rule for collecting db payloads for the mentioned + workload and instrumentation libraries + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + type: object + httpRequest: + description: |- + Collect HTTP request payload data when available. + Can be a client (outgoing) request or a server (incoming) request, depending on the instrumentation library + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + mimeTypes: + description: |- + Limit payload collection to specific mime types based on the content type header. + When not specified, all mime types payloads will be collected. + Empty array will make the rule ineffective. + items: + type: string + type: array + type: object + httpResponse: + description: |- + rule for collecting the response part of an http payload. + Can be a client response or a server response, depending on the instrumentation library + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + mimeTypes: + description: |- + Limit payload collection to specific mime types based on the content type header. + When not specified, all mime types payloads will be collected. + Empty array will make the rule ineffective. + items: + type: string + type: array + type: object + type: object required: - - instrumentationLibraryConfigs - language + - payloadCollection type: object type: array type: object diff --git a/api/config/crd/bases/odigos.io_instrumentationrules.yaml b/api/config/crd/bases/odigos.io_instrumentationrules.yaml new file mode 100644 index 000000000..afcc05807 --- /dev/null +++ b/api/config/crd/bases/odigos.io_instrumentationrules.yaml @@ -0,0 +1,271 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + labels: + metadata.labels.odigos.io/config: "1" + metadata.labels.odigos.io/system-object: "true" + name: instrumentationrules.odigos.io +spec: + group: odigos.io + names: + kind: InstrumentationRule + listKind: InstrumentationRuleList + plural: instrumentationrules + singular: instrumentationrule + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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: + properties: + disabled: + description: A boolean field allowing to temporarily disable the rule, + but keep it around for future use + type: boolean + instrumentationLibraries: + description: |- + For fine grained control, the user can specify the instrumentation library to use. + One can specify same rule for multiple languages and libraries at the same time. + If nil, all instrumentation libraries will be used. + If empty, no instrumentation libraries will be used. + items: + description: |- + Includes the instrumentation library name, span kind (for golang) and language + which identifies a specific library globally. + properties: + language: + description: The language in which this library will collect + data + enum: + - java + - python + - go + - dotnet + - javascript + - mysql + - nginx + - unknown + - ignored + type: string + name: + description: The name of the instrumentation library + type: string + spanKind: + description: |- + SpanKind is only supported by Golang and will be ignored for any other SDK language. + In Go, SpanKind is used because the same instrumentation library can be utilized for different span kinds (e.g., client/server). + enum: + - client + - server + - producer + - consumer + - internal + type: string + required: + - language + - name + type: object + type: array + notes: + description: 'A free-form text field that allows you to attach notes + regarding the rule for convenience. For example: why it was added. + Odigos does not use or assume any meaning from this field.' + type: string + payloadCollection: + description: Allows to configure payload collection aspects for different + types of payloads. + properties: + dbQuery: + description: rule for collecting db payloads for the mentioned + workload and instrumentation libraries + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + type: object + httpRequest: + description: |- + Collect HTTP request payload data when available. + Can be a client (outgoing) request or a server (incoming) request, depending on the instrumentation library + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + mimeTypes: + description: |- + Limit payload collection to specific mime types based on the content type header. + When not specified, all mime types payloads will be collected. + Empty array will make the rule ineffective. + items: + type: string + type: array + type: object + httpResponse: + description: |- + rule for collecting the response part of an http payload. + Can be a client response or a server response, depending on the instrumentation library + properties: + dropPartialPayloads: + description: |- + If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + type: boolean + maxPayloadLength: + description: |- + Maximum length of the payload to collect. + If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + format: int64 + type: integer + mimeTypes: + description: |- + Limit payload collection to specific mime types based on the content type header. + When not specified, all mime types payloads will be collected. + Empty array will make the rule ineffective. + items: + type: string + type: array + type: object + type: object + ruleName: + description: Allows you to attach a meaningful name to the rule for + convenience. Odigos does not use or assume any meaning from this + field. + type: string + workloads: + description: An array of workload objects (name, namespace, kind) + to which the rule should be applied. If not specified, the rule + will be applied to all workloads. empty array will render the rule + inactive. + items: + description: |- + PodWorkload represents the higher-level controller managing a specific Pod within a Kubernetes cluster. + It contains essential details about the controller such as its Name, Namespace, and Kind. + 'Kind' refers to the type of controller, which can be a Deployment, StatefulSet, or DaemonSet. + This struct is useful for identifying and interacting with the overarching entity + that governs the lifecycle and behavior of a Pod, especially in contexts where + understanding the relationship between a Pod and its controlling workload is crucial. + properties: + kind: + description: |- + 1. the pascal case representation of the workload kind + it is used in k8s api objects as the `Kind` field. + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + - namespace + type: object + type: array + type: object + status: + properties: + conditions: + description: |- + Represents the observations of a instrumentationrule's current state. + Known .status.conditions.type are: "Available", "Progressing" + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationlibraryconfig.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationlibraryconfig.go index 3ee719d0b..c13a1b5dc 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationlibraryconfig.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationlibraryconfig.go @@ -17,11 +17,16 @@ limitations under the License. package v1alpha1 +import ( + instrumentationrules "github.com/odigos-io/odigos/api/odigos/v1alpha1/instrumentationrules" +) + // InstrumentationLibraryConfigApplyConfiguration represents a declarative configuration of the InstrumentationLibraryConfig type for use // with apply. type InstrumentationLibraryConfigApplyConfiguration struct { InstrumentationLibraryId *InstrumentationLibraryIdApplyConfiguration `json:"libraryId,omitempty"` TraceConfig *InstrumentationLibraryConfigTracesApplyConfiguration `json:"traceConfig,omitempty"` + PayloadCollection *instrumentationrules.PayloadCollection `json:"payloadCollection,omitempty"` } // InstrumentationLibraryConfigApplyConfiguration constructs a declarative configuration of the InstrumentationLibraryConfig type for use with @@ -45,3 +50,11 @@ func (b *InstrumentationLibraryConfigApplyConfiguration) WithTraceConfig(value * b.TraceConfig = value return b } + +// WithPayloadCollection sets the PayloadCollection field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PayloadCollection field is set to the value of the last call. +func (b *InstrumentationLibraryConfigApplyConfiguration) WithPayloadCollection(value instrumentationrules.PayloadCollection) *InstrumentationLibraryConfigApplyConfiguration { + b.PayloadCollection = &value + return b +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationlibraryglobalid.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationlibraryglobalid.go new file mode 100644 index 000000000..1106d610c --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationlibraryglobalid.go @@ -0,0 +1,60 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + common "github.com/odigos-io/odigos/common" +) + +// InstrumentationLibraryGlobalIdApplyConfiguration represents a declarative configuration of the InstrumentationLibraryGlobalId type for use +// with apply. +type InstrumentationLibraryGlobalIdApplyConfiguration struct { + Name *string `json:"name,omitempty"` + SpanKind *common.SpanKind `json:"spanKind,omitempty"` + Language *common.ProgrammingLanguage `json:"language,omitempty"` +} + +// InstrumentationLibraryGlobalIdApplyConfiguration constructs a declarative configuration of the InstrumentationLibraryGlobalId type for use with +// apply. +func InstrumentationLibraryGlobalId() *InstrumentationLibraryGlobalIdApplyConfiguration { + return &InstrumentationLibraryGlobalIdApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *InstrumentationLibraryGlobalIdApplyConfiguration) WithName(value string) *InstrumentationLibraryGlobalIdApplyConfiguration { + b.Name = &value + return b +} + +// WithSpanKind sets the SpanKind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SpanKind field is set to the value of the last call. +func (b *InstrumentationLibraryGlobalIdApplyConfiguration) WithSpanKind(value common.SpanKind) *InstrumentationLibraryGlobalIdApplyConfiguration { + b.SpanKind = &value + return b +} + +// WithLanguage sets the Language field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Language field is set to the value of the last call. +func (b *InstrumentationLibraryGlobalIdApplyConfiguration) WithLanguage(value common.ProgrammingLanguage) *InstrumentationLibraryGlobalIdApplyConfiguration { + b.Language = &value + return b +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrule.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrule.go new file mode 100644 index 000000000..d37e7f3b1 --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrule.go @@ -0,0 +1,224 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// InstrumentationRuleApplyConfiguration represents a declarative configuration of the InstrumentationRule type for use +// with apply. +type InstrumentationRuleApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *InstrumentationRuleSpecApplyConfiguration `json:"spec,omitempty"` + Status *InstrumentationRuleStatusApplyConfiguration `json:"status,omitempty"` +} + +// InstrumentationRule constructs a declarative configuration of the InstrumentationRule type for use with +// apply. +func InstrumentationRule(name, namespace string) *InstrumentationRuleApplyConfiguration { + b := &InstrumentationRuleApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("InstrumentationRule") + b.WithAPIVersion("odigos.io/v1alpha1") + return b +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithKind(value string) *InstrumentationRuleApplyConfiguration { + b.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithAPIVersion(value string) *InstrumentationRuleApplyConfiguration { + b.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithName(value string) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithGenerateName(value string) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithNamespace(value string) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithUID(value types.UID) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithResourceVersion(value string) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithGeneration(value int64) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithCreationTimestamp(value metav1.Time) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *InstrumentationRuleApplyConfiguration) WithLabels(entries map[string]string) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.Labels == nil && len(entries) > 0 { + b.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *InstrumentationRuleApplyConfiguration) WithAnnotations(entries map[string]string) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.Annotations == nil && len(entries) > 0 { + b.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *InstrumentationRuleApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.OwnerReferences = append(b.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *InstrumentationRuleApplyConfiguration) WithFinalizers(values ...string) *InstrumentationRuleApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.Finalizers = append(b.Finalizers, values[i]) + } + return b +} + +func (b *InstrumentationRuleApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithSpec(value *InstrumentationRuleSpecApplyConfiguration) *InstrumentationRuleApplyConfiguration { + b.Spec = value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *InstrumentationRuleApplyConfiguration) WithStatus(value *InstrumentationRuleStatusApplyConfiguration) *InstrumentationRuleApplyConfiguration { + b.Status = value + return b +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *InstrumentationRuleApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.Name +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrulespec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrulespec.go new file mode 100644 index 000000000..057674636 --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrulespec.go @@ -0,0 +1,100 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + instrumentationrules "github.com/odigos-io/odigos/api/odigos/v1alpha1/instrumentationrules" + workload "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +// InstrumentationRuleSpecApplyConfiguration represents a declarative configuration of the InstrumentationRuleSpec type for use +// with apply. +type InstrumentationRuleSpecApplyConfiguration struct { + RuleName *string `json:"ruleName,omitempty"` + Notes *string `json:"notes,omitempty"` + Disabled *bool `json:"disabled,omitempty"` + Workloads *[]workload.PodWorkload `json:"workloads,omitempty"` + InstrumentationLibraries *[]InstrumentationLibraryGlobalIdApplyConfiguration `json:"instrumentationLibraries,omitempty"` + PayloadCollection *instrumentationrules.PayloadCollection `json:"payloadCollection,omitempty"` +} + +// InstrumentationRuleSpecApplyConfiguration constructs a declarative configuration of the InstrumentationRuleSpec type for use with +// apply. +func InstrumentationRuleSpec() *InstrumentationRuleSpecApplyConfiguration { + return &InstrumentationRuleSpecApplyConfiguration{} +} + +// WithRuleName sets the RuleName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RuleName field is set to the value of the last call. +func (b *InstrumentationRuleSpecApplyConfiguration) WithRuleName(value string) *InstrumentationRuleSpecApplyConfiguration { + b.RuleName = &value + return b +} + +// WithNotes sets the Notes field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Notes field is set to the value of the last call. +func (b *InstrumentationRuleSpecApplyConfiguration) WithNotes(value string) *InstrumentationRuleSpecApplyConfiguration { + b.Notes = &value + return b +} + +// WithDisabled sets the Disabled field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Disabled field is set to the value of the last call. +func (b *InstrumentationRuleSpecApplyConfiguration) WithDisabled(value bool) *InstrumentationRuleSpecApplyConfiguration { + b.Disabled = &value + return b +} + +// WithWorkloads sets the Workloads field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Workloads field is set to the value of the last call. +func (b *InstrumentationRuleSpecApplyConfiguration) WithWorkloads(value []workload.PodWorkload) *InstrumentationRuleSpecApplyConfiguration { + b.Workloads = &value + return b +} + +func (b *InstrumentationRuleSpecApplyConfiguration) ensureInstrumentationLibraryGlobalIdApplyConfigurationExists() { + if b.InstrumentationLibraries == nil { + b.InstrumentationLibraries = &[]InstrumentationLibraryGlobalIdApplyConfiguration{} + } +} + +// WithInstrumentationLibraries adds the given value to the InstrumentationLibraries field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the InstrumentationLibraries field. +func (b *InstrumentationRuleSpecApplyConfiguration) WithInstrumentationLibraries(values ...*InstrumentationLibraryGlobalIdApplyConfiguration) *InstrumentationRuleSpecApplyConfiguration { + b.ensureInstrumentationLibraryGlobalIdApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithInstrumentationLibraries") + } + *b.InstrumentationLibraries = append(*b.InstrumentationLibraries, *values[i]) + } + return b +} + +// WithPayloadCollection sets the PayloadCollection field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PayloadCollection field is set to the value of the last call. +func (b *InstrumentationRuleSpecApplyConfiguration) WithPayloadCollection(value instrumentationrules.PayloadCollection) *InstrumentationRuleSpecApplyConfiguration { + b.PayloadCollection = &value + return b +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrulestatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrulestatus.go new file mode 100644 index 000000000..28834b6ed --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationrulestatus.go @@ -0,0 +1,47 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// InstrumentationRuleStatusApplyConfiguration represents a declarative configuration of the InstrumentationRuleStatus type for use +// with apply. +type InstrumentationRuleStatusApplyConfiguration struct { + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` +} + +// InstrumentationRuleStatusApplyConfiguration constructs a declarative configuration of the InstrumentationRuleStatus type for use with +// apply. +func InstrumentationRuleStatus() *InstrumentationRuleStatusApplyConfiguration { + return &InstrumentationRuleStatusApplyConfiguration{} +} + +// WithConditions adds the given value to the Conditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Conditions field. +func (b *InstrumentationRuleStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationRuleStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sdkconfig.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sdkconfig.go index 8e9521b53..014da6d62 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sdkconfig.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sdkconfig.go @@ -18,6 +18,7 @@ limitations under the License. package v1alpha1 import ( + instrumentationrules "github.com/odigos-io/odigos/api/odigos/v1alpha1/instrumentationrules" common "github.com/odigos-io/odigos/common" ) @@ -27,6 +28,7 @@ type SdkConfigApplyConfiguration struct { Language *common.ProgrammingLanguage `json:"language,omitempty"` InstrumentationLibraryConfigs []InstrumentationLibraryConfigApplyConfiguration `json:"instrumentationLibraryConfigs,omitempty"` HeadSamplingConfig *HeadSamplingConfigApplyConfiguration `json:"headSamplerConfig,omitempty"` + DefaultPayloadCollection *instrumentationrules.PayloadCollection `json:"payloadCollection,omitempty"` } // SdkConfigApplyConfiguration constructs a declarative configuration of the SdkConfig type for use with @@ -63,3 +65,11 @@ func (b *SdkConfigApplyConfiguration) WithHeadSamplingConfig(value *HeadSampling b.HeadSamplingConfig = value return b } + +// WithDefaultPayloadCollection sets the DefaultPayloadCollection field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DefaultPayloadCollection field is set to the value of the last call. +func (b *SdkConfigApplyConfiguration) WithDefaultPayloadCollection(value instrumentationrules.PayloadCollection) *SdkConfigApplyConfiguration { + b.DefaultPayloadCollection = &value + return b +} diff --git a/api/generated/odigos/applyconfiguration/utils.go b/api/generated/odigos/applyconfiguration/utils.go index 72efff442..3fb86f36c 100644 --- a/api/generated/odigos/applyconfiguration/utils.go +++ b/api/generated/odigos/applyconfiguration/utils.go @@ -73,12 +73,20 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.InstrumentationLibraryConfigApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationLibraryConfigTraces"): return &odigosv1alpha1.InstrumentationLibraryConfigTracesApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationLibraryGlobalId"): + return &odigosv1alpha1.InstrumentationLibraryGlobalIdApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationLibraryId"): return &odigosv1alpha1.InstrumentationLibraryIdApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationLibraryOptions"): return &odigosv1alpha1.InstrumentationLibraryOptionsApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationLibraryStatus"): return &odigosv1alpha1.InstrumentationLibraryStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationRule"): + return &odigosv1alpha1.InstrumentationRuleApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationRuleSpec"): + return &odigosv1alpha1.InstrumentationRuleSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationRuleStatus"): + return &odigosv1alpha1.InstrumentationRuleStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentedApplication"): return &odigosv1alpha1.InstrumentedApplicationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentedApplicationSpec"): diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentationrule.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentationrule.go new file mode 100644 index 000000000..dff76a9c6 --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentationrule.go @@ -0,0 +1,196 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + json "encoding/json" + "fmt" + + odigosv1alpha1 "github.com/odigos-io/odigos/api/generated/odigos/applyconfiguration/odigos/v1alpha1" + v1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeInstrumentationRules implements InstrumentationRuleInterface +type FakeInstrumentationRules struct { + Fake *FakeOdigosV1alpha1 + ns string +} + +var instrumentationrulesResource = v1alpha1.SchemeGroupVersion.WithResource("instrumentationrules") + +var instrumentationrulesKind = v1alpha1.SchemeGroupVersion.WithKind("InstrumentationRule") + +// Get takes name of the instrumentationRule, and returns the corresponding instrumentationRule object, and an error if there is any. +func (c *FakeInstrumentationRules) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.InstrumentationRule, err error) { + emptyResult := &v1alpha1.InstrumentationRule{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(instrumentationrulesResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.InstrumentationRule), err +} + +// List takes label and field selectors, and returns the list of InstrumentationRules that match those selectors. +func (c *FakeInstrumentationRules) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.InstrumentationRuleList, err error) { + emptyResult := &v1alpha1.InstrumentationRuleList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(instrumentationrulesResource, instrumentationrulesKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.InstrumentationRuleList{ListMeta: obj.(*v1alpha1.InstrumentationRuleList).ListMeta} + for _, item := range obj.(*v1alpha1.InstrumentationRuleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested instrumentationRules. +func (c *FakeInstrumentationRules) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(instrumentationrulesResource, c.ns, opts)) + +} + +// Create takes the representation of a instrumentationRule and creates it. Returns the server's representation of the instrumentationRule, and an error, if there is any. +func (c *FakeInstrumentationRules) Create(ctx context.Context, instrumentationRule *v1alpha1.InstrumentationRule, opts v1.CreateOptions) (result *v1alpha1.InstrumentationRule, err error) { + emptyResult := &v1alpha1.InstrumentationRule{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(instrumentationrulesResource, c.ns, instrumentationRule, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.InstrumentationRule), err +} + +// Update takes the representation of a instrumentationRule and updates it. Returns the server's representation of the instrumentationRule, and an error, if there is any. +func (c *FakeInstrumentationRules) Update(ctx context.Context, instrumentationRule *v1alpha1.InstrumentationRule, opts v1.UpdateOptions) (result *v1alpha1.InstrumentationRule, err error) { + emptyResult := &v1alpha1.InstrumentationRule{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(instrumentationrulesResource, c.ns, instrumentationRule, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.InstrumentationRule), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeInstrumentationRules) UpdateStatus(ctx context.Context, instrumentationRule *v1alpha1.InstrumentationRule, opts v1.UpdateOptions) (result *v1alpha1.InstrumentationRule, err error) { + emptyResult := &v1alpha1.InstrumentationRule{} + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceActionWithOptions(instrumentationrulesResource, "status", c.ns, instrumentationRule, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.InstrumentationRule), err +} + +// Delete takes name of the instrumentationRule and deletes it. Returns an error if one occurs. +func (c *FakeInstrumentationRules) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(instrumentationrulesResource, c.ns, name, opts), &v1alpha1.InstrumentationRule{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeInstrumentationRules) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(instrumentationrulesResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.InstrumentationRuleList{}) + return err +} + +// Patch applies the patch and returns the patched instrumentationRule. +func (c *FakeInstrumentationRules) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.InstrumentationRule, err error) { + emptyResult := &v1alpha1.InstrumentationRule{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationrulesResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.InstrumentationRule), err +} + +// Apply takes the given apply declarative configuration, applies it and returns the applied instrumentationRule. +func (c *FakeInstrumentationRules) Apply(ctx context.Context, instrumentationRule *odigosv1alpha1.InstrumentationRuleApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.InstrumentationRule, err error) { + if instrumentationRule == nil { + return nil, fmt.Errorf("instrumentationRule provided to Apply must not be nil") + } + data, err := json.Marshal(instrumentationRule) + if err != nil { + return nil, err + } + name := instrumentationRule.Name + if name == nil { + return nil, fmt.Errorf("instrumentationRule.Name must be provided to Apply") + } + emptyResult := &v1alpha1.InstrumentationRule{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationrulesResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.InstrumentationRule), err +} + +// ApplyStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). +func (c *FakeInstrumentationRules) ApplyStatus(ctx context.Context, instrumentationRule *odigosv1alpha1.InstrumentationRuleApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.InstrumentationRule, err error) { + if instrumentationRule == nil { + return nil, fmt.Errorf("instrumentationRule provided to Apply must not be nil") + } + data, err := json.Marshal(instrumentationRule) + if err != nil { + return nil, err + } + name := instrumentationRule.Name + if name == nil { + return nil, fmt.Errorf("instrumentationRule.Name must be provided to Apply") + } + emptyResult := &v1alpha1.InstrumentationRule{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationrulesResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions(), "status"), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.InstrumentationRule), err +} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_odigos_client.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_odigos_client.go index a735f3c10..9f70f41c2 100644 --- a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_odigos_client.go +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_odigos_client.go @@ -43,6 +43,10 @@ func (c *FakeOdigosV1alpha1) InstrumentationInstances(namespace string) v1alpha1 return &FakeInstrumentationInstances{c, namespace} } +func (c *FakeOdigosV1alpha1) InstrumentationRules(namespace string) v1alpha1.InstrumentationRuleInterface { + return &FakeInstrumentationRules{c, namespace} +} + func (c *FakeOdigosV1alpha1) InstrumentedApplications(namespace string) v1alpha1.InstrumentedApplicationInterface { return &FakeInstrumentedApplications{c, namespace} } diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/generated_expansion.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/generated_expansion.go index 884a88019..a6b52f8da 100644 --- a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/generated_expansion.go +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/generated_expansion.go @@ -25,6 +25,8 @@ type InstrumentationConfigExpansion interface{} type InstrumentationInstanceExpansion interface{} +type InstrumentationRuleExpansion interface{} + type InstrumentedApplicationExpansion interface{} type OdigosConfigurationExpansion interface{} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentationrule.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentationrule.go new file mode 100644 index 000000000..0f6931135 --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentationrule.go @@ -0,0 +1,72 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + + odigosv1alpha1 "github.com/odigos-io/odigos/api/generated/odigos/applyconfiguration/odigos/v1alpha1" + scheme "github.com/odigos-io/odigos/api/generated/odigos/clientset/versioned/scheme" + v1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// InstrumentationRulesGetter has a method to return a InstrumentationRuleInterface. +// A group's client should implement this interface. +type InstrumentationRulesGetter interface { + InstrumentationRules(namespace string) InstrumentationRuleInterface +} + +// InstrumentationRuleInterface has methods to work with InstrumentationRule resources. +type InstrumentationRuleInterface interface { + Create(ctx context.Context, instrumentationRule *v1alpha1.InstrumentationRule, opts v1.CreateOptions) (*v1alpha1.InstrumentationRule, error) + Update(ctx context.Context, instrumentationRule *v1alpha1.InstrumentationRule, opts v1.UpdateOptions) (*v1alpha1.InstrumentationRule, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, instrumentationRule *v1alpha1.InstrumentationRule, opts v1.UpdateOptions) (*v1alpha1.InstrumentationRule, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.InstrumentationRule, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.InstrumentationRuleList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.InstrumentationRule, err error) + Apply(ctx context.Context, instrumentationRule *odigosv1alpha1.InstrumentationRuleApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.InstrumentationRule, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, instrumentationRule *odigosv1alpha1.InstrumentationRuleApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.InstrumentationRule, err error) + InstrumentationRuleExpansion +} + +// instrumentationRules implements InstrumentationRuleInterface +type instrumentationRules struct { + *gentype.ClientWithListAndApply[*v1alpha1.InstrumentationRule, *v1alpha1.InstrumentationRuleList, *odigosv1alpha1.InstrumentationRuleApplyConfiguration] +} + +// newInstrumentationRules returns a InstrumentationRules +func newInstrumentationRules(c *OdigosV1alpha1Client, namespace string) *instrumentationRules { + return &instrumentationRules{ + gentype.NewClientWithListAndApply[*v1alpha1.InstrumentationRule, *v1alpha1.InstrumentationRuleList, *odigosv1alpha1.InstrumentationRuleApplyConfiguration]( + "instrumentationrules", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1alpha1.InstrumentationRule { return &v1alpha1.InstrumentationRule{} }, + func() *v1alpha1.InstrumentationRuleList { return &v1alpha1.InstrumentationRuleList{} }), + } +} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/odigos_client.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/odigos_client.go index c83e8ceb1..06398b492 100644 --- a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/odigos_client.go +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/odigos_client.go @@ -31,6 +31,7 @@ type OdigosV1alpha1Interface interface { DestinationsGetter InstrumentationConfigsGetter InstrumentationInstancesGetter + InstrumentationRulesGetter InstrumentedApplicationsGetter OdigosConfigurationsGetter ProcessorsGetter @@ -57,6 +58,10 @@ func (c *OdigosV1alpha1Client) InstrumentationInstances(namespace string) Instru return newInstrumentationInstances(c, namespace) } +func (c *OdigosV1alpha1Client) InstrumentationRules(namespace string) InstrumentationRuleInterface { + return newInstrumentationRules(c, namespace) +} + func (c *OdigosV1alpha1Client) InstrumentedApplications(namespace string) InstrumentedApplicationInterface { return newInstrumentedApplications(c, namespace) } diff --git a/api/generated/odigos/informers/externalversions/generic.go b/api/generated/odigos/informers/externalversions/generic.go index 6126c8840..4e3bd2081 100644 --- a/api/generated/odigos/informers/externalversions/generic.go +++ b/api/generated/odigos/informers/externalversions/generic.go @@ -60,6 +60,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().InstrumentationConfigs().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationinstances"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().InstrumentationInstances().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("instrumentationrules"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().InstrumentationRules().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentedapplications"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().InstrumentedApplications().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("odigosconfigurations"): diff --git a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentationrule.go b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentationrule.go new file mode 100644 index 000000000..0df370208 --- /dev/null +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentationrule.go @@ -0,0 +1,89 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + versioned "github.com/odigos-io/odigos/api/generated/odigos/clientset/versioned" + internalinterfaces "github.com/odigos-io/odigos/api/generated/odigos/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/odigos-io/odigos/api/generated/odigos/listers/odigos/v1alpha1" + odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// InstrumentationRuleInformer provides access to a shared informer and lister for +// InstrumentationRules. +type InstrumentationRuleInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.InstrumentationRuleLister +} + +type instrumentationRuleInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewInstrumentationRuleInformer constructs a new informer for InstrumentationRule type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewInstrumentationRuleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredInstrumentationRuleInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredInstrumentationRuleInformer constructs a new informer for InstrumentationRule type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredInstrumentationRuleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OdigosV1alpha1().InstrumentationRules(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OdigosV1alpha1().InstrumentationRules(namespace).Watch(context.TODO(), options) + }, + }, + &odigosv1alpha1.InstrumentationRule{}, + resyncPeriod, + indexers, + ) +} + +func (f *instrumentationRuleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredInstrumentationRuleInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *instrumentationRuleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&odigosv1alpha1.InstrumentationRule{}, f.defaultInformer) +} + +func (f *instrumentationRuleInformer) Lister() v1alpha1.InstrumentationRuleLister { + return v1alpha1.NewInstrumentationRuleLister(f.Informer().GetIndexer()) +} diff --git a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go index c2938282e..52778ee8d 100644 --- a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go @@ -31,6 +31,8 @@ type Interface interface { InstrumentationConfigs() InstrumentationConfigInformer // InstrumentationInstances returns a InstrumentationInstanceInformer. InstrumentationInstances() InstrumentationInstanceInformer + // InstrumentationRules returns a InstrumentationRuleInformer. + InstrumentationRules() InstrumentationRuleInformer // InstrumentedApplications returns a InstrumentedApplicationInformer. InstrumentedApplications() InstrumentedApplicationInformer // OdigosConfigurations returns a OdigosConfigurationInformer. @@ -70,6 +72,11 @@ func (v *version) InstrumentationInstances() InstrumentationInstanceInformer { return &instrumentationInstanceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// InstrumentationRules returns a InstrumentationRuleInformer. +func (v *version) InstrumentationRules() InstrumentationRuleInformer { + return &instrumentationRuleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // InstrumentedApplications returns a InstrumentedApplicationInformer. func (v *version) InstrumentedApplications() InstrumentedApplicationInformer { return &instrumentedApplicationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go b/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go index badc3a1a3..7adce7617 100644 --- a/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go +++ b/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go @@ -49,6 +49,14 @@ type InstrumentationInstanceListerExpansion interface{} // InstrumentationInstanceNamespaceLister. type InstrumentationInstanceNamespaceListerExpansion interface{} +// InstrumentationRuleListerExpansion allows custom methods to be added to +// InstrumentationRuleLister. +type InstrumentationRuleListerExpansion interface{} + +// InstrumentationRuleNamespaceListerExpansion allows custom methods to be added to +// InstrumentationRuleNamespaceLister. +type InstrumentationRuleNamespaceListerExpansion interface{} + // InstrumentedApplicationListerExpansion allows custom methods to be added to // InstrumentedApplicationLister. type InstrumentedApplicationListerExpansion interface{} diff --git a/api/generated/odigos/listers/odigos/v1alpha1/instrumentationrule.go b/api/generated/odigos/listers/odigos/v1alpha1/instrumentationrule.go new file mode 100644 index 000000000..7fc2736ec --- /dev/null +++ b/api/generated/odigos/listers/odigos/v1alpha1/instrumentationrule.go @@ -0,0 +1,69 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/listers" + "k8s.io/client-go/tools/cache" +) + +// InstrumentationRuleLister helps list InstrumentationRules. +// All objects returned here must be treated as read-only. +type InstrumentationRuleLister interface { + // List lists all InstrumentationRules in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.InstrumentationRule, err error) + // InstrumentationRules returns an object that can list and get InstrumentationRules. + InstrumentationRules(namespace string) InstrumentationRuleNamespaceLister + InstrumentationRuleListerExpansion +} + +// instrumentationRuleLister implements the InstrumentationRuleLister interface. +type instrumentationRuleLister struct { + listers.ResourceIndexer[*v1alpha1.InstrumentationRule] +} + +// NewInstrumentationRuleLister returns a new InstrumentationRuleLister. +func NewInstrumentationRuleLister(indexer cache.Indexer) InstrumentationRuleLister { + return &instrumentationRuleLister{listers.New[*v1alpha1.InstrumentationRule](indexer, v1alpha1.Resource("instrumentationrule"))} +} + +// InstrumentationRules returns an object that can list and get InstrumentationRules. +func (s *instrumentationRuleLister) InstrumentationRules(namespace string) InstrumentationRuleNamespaceLister { + return instrumentationRuleNamespaceLister{listers.NewNamespaced[*v1alpha1.InstrumentationRule](s.ResourceIndexer, namespace)} +} + +// InstrumentationRuleNamespaceLister helps list and get InstrumentationRules. +// All objects returned here must be treated as read-only. +type InstrumentationRuleNamespaceLister interface { + // List lists all InstrumentationRules in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.InstrumentationRule, err error) + // Get retrieves the InstrumentationRule from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.InstrumentationRule, error) + InstrumentationRuleNamespaceListerExpansion +} + +// instrumentationRuleNamespaceLister implements the InstrumentationRuleNamespaceLister +// interface. +type instrumentationRuleNamespaceLister struct { + listers.ResourceIndexer[*v1alpha1.InstrumentationRule] +} diff --git a/api/go.mod b/api/go.mod index 91c07f0ae..463c1996c 100644 --- a/api/go.mod +++ b/api/go.mod @@ -15,6 +15,7 @@ require ( ) require ( + github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect diff --git a/api/go.sum b/api/go.sum index f2431a2df..a28d8f392 100644 --- a/api/go.sum +++ b/api/go.sum @@ -5,12 +5,16 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= @@ -46,6 +50,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -96,6 +102,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -104,11 +112,17 @@ go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= diff --git a/api/hack/update-codegen.sh b/api/hack/update-codegen.sh index de89facfe..062c80e5e 100755 --- a/api/hack/update-codegen.sh +++ b/api/hack/update-codegen.sh @@ -29,4 +29,4 @@ kube::codegen::gen_client \ --output-dir "${SCRIPT_ROOT}/generated/odigos" \ --output-pkg "github.com/odigos-io/odigos/api/generated/odigos" \ --boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \ - "${SCRIPT_ROOT}" \ No newline at end of file + "${SCRIPT_ROOT}" diff --git a/api/odigos/v1alpha1/instrumentatioconfig_types.go b/api/odigos/v1alpha1/instrumentationconfig_types.go similarity index 95% rename from api/odigos/v1alpha1/instrumentatioconfig_types.go rename to api/odigos/v1alpha1/instrumentationconfig_types.go index ff4af4c47..41d425d69 100644 --- a/api/odigos/v1alpha1/instrumentatioconfig_types.go +++ b/api/odigos/v1alpha1/instrumentationconfig_types.go @@ -1,6 +1,7 @@ package v1alpha1 import ( + "github.com/odigos-io/odigos/api/odigos/v1alpha1/instrumentationrules" "github.com/odigos-io/odigos/common" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -41,12 +42,14 @@ type SdkConfig struct { Language common.ProgrammingLanguage `json:"language"` // configurations for the instrumentation libraries the the SDK should use - InstrumentationLibraryConfigs []InstrumentationLibraryConfig `json:"instrumentationLibraryConfigs"` + InstrumentationLibraryConfigs []InstrumentationLibraryConfig `json:"instrumentationLibraryConfigs,omitempty"` // HeadSamplingConfig is a set sampling rules. // This config currently only applies to root spans. // In the Future we might add another level of configuration base on the parent span (ParentBased Sampling) - HeadSamplingConfig HeadSamplingConfig `json:"headSamplerConfig,omitempty"` + HeadSamplingConfig *HeadSamplingConfig `json:"headSamplerConfig,omitempty"` + + DefaultPayloadCollection *instrumentationrules.PayloadCollection `json:"payloadCollection"` } // 'Operand' represents the attributes and values that an operator acts upon in an expression @@ -105,6 +108,8 @@ type InstrumentationLibraryConfig struct { InstrumentationLibraryId InstrumentationLibraryId `json:"libraryId"` TraceConfig *InstrumentationLibraryConfigTraces `json:"traceConfig,omitempty"` + + PayloadCollection *instrumentationrules.PayloadCollection `json:"payloadCollection,omitempty"` } type InstrumentationLibraryId struct { diff --git a/api/odigos/v1alpha1/instrumentationrule_type.go b/api/odigos/v1alpha1/instrumentationrule_type.go new file mode 100644 index 000000000..95f96fe3b --- /dev/null +++ b/api/odigos/v1alpha1/instrumentationrule_type.go @@ -0,0 +1,99 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/odigos-io/odigos/api/odigos/v1alpha1/instrumentationrules" + "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Includes the instrumentation library name, span kind (for golang) and language +// which identifies a specific library globally. +type InstrumentationLibraryGlobalId struct { + + // The name of the instrumentation library + Name string `json:"name"` + + // SpanKind is only supported by Golang and will be ignored for any other SDK language. + // In Go, SpanKind is used because the same instrumentation library can be utilized for different span kinds (e.g., client/server). + SpanKind common.SpanKind `json:"spanKind,omitempty"` + + // The language in which this library will collect data + Language common.ProgrammingLanguage `json:"language"` +} + +type InstrumentationRuleSpec struct { + + // Allows you to attach a meaningful name to the rule for convenience. Odigos does not use or assume any meaning from this field. + RuleName string `json:"ruleName,omitempty"` + + // A free-form text field that allows you to attach notes regarding the rule for convenience. For example: why it was added. Odigos does not use or assume any meaning from this field. + Notes string `json:"notes,omitempty"` + + // A boolean field allowing to temporarily disable the rule, but keep it around for future use + Disabled bool `json:"disabled,omitempty"` + + // An array of workload objects (name, namespace, kind) to which the rule should be applied. If not specified, the rule will be applied to all workloads. empty array will render the rule inactive. + Workloads *[]workload.PodWorkload `json:"workloads,omitempty"` + + // For fine grained control, the user can specify the instrumentation library to use. + // One can specify same rule for multiple languages and libraries at the same time. + // If nil, all instrumentation libraries will be used. + // If empty, no instrumentation libraries will be used. + InstrumentationLibraries *[]InstrumentationLibraryGlobalId `json:"instrumentationLibraries,omitempty"` + + // Allows to configure payload collection aspects for different types of payloads. + PayloadCollection *instrumentationrules.PayloadCollection `json:"payloadCollection,omitempty"` +} + +type InstrumentationRuleStatus struct { + // Represents the observations of a instrumentationrule's current state. + // Known .status.conditions.type are: "Available", "Progressing" + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` +} + +//+genclient +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:metadata:labels=metadata.labels.odigos.io/config=1 +//+kubebuilder:metadata:labels=metadata.labels.odigos.io/system-object=true + +type InstrumentationRule struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec InstrumentationRuleSpec `json:"spec,omitempty"` + Status InstrumentationRuleStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +type InstrumentationRuleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []InstrumentationRule `json:"items"` +} + +func init() { + SchemeBuilder.Register(&InstrumentationRule{}, &InstrumentationRuleList{}) +} diff --git a/api/odigos/v1alpha1/instrumentationrules/payloadcollection.go b/api/odigos/v1alpha1/instrumentationrules/payloadcollection.go new file mode 100644 index 000000000..b75e8ab1c --- /dev/null +++ b/api/odigos/v1alpha1/instrumentationrules/payloadcollection.go @@ -0,0 +1,48 @@ +package instrumentationrules + +// +kubebuilder:object:generate=true +// +kubebuilder:deepcopy-gen=true +type HttpPayloadCollection struct { + + // Limit payload collection to specific mime types based on the content type header. + // When not specified, all mime types payloads will be collected. + // Empty array will make the rule ineffective. + MimeTypes *[]string `json:"mimeTypes,omitempty"` + + // Maximum length of the payload to collect. + // If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + MaxPayloadLength *int64 `json:"maxPayloadLength,omitempty"` + + // If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + // This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + DropPartialPayloads *bool `json:"dropPartialPayloads,omitempty"` +} + +// Rule for collecting payloads for a DbStatement +// +kubebuilder:object:generate=true +// +kubebuilder:deepcopy-gen=true +type DbQueryPayloadCollection struct { + + // Maximum length of the payload to collect. + // If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option + MaxPayloadLength *int64 `json:"maxPayloadLength,omitempty"` + + // If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. + // This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + DropPartialPayloads *bool `json:"dropPartialPayloads,omitempty"` +} + +// +kubebuilder:object:generate=true +// +kubebuilder:deepcopy-gen=true +type PayloadCollection struct { + // Collect HTTP request payload data when available. + // Can be a client (outgoing) request or a server (incoming) request, depending on the instrumentation library + HttpRequest *HttpPayloadCollection `json:"httpRequest,omitempty"` + + // rule for collecting the response part of an http payload. + // Can be a client response or a server response, depending on the instrumentation library + HttpResponse *HttpPayloadCollection `json:"httpResponse,omitempty"` + + // rule for collecting db payloads for the mentioned workload and instrumentation libraries + DbQuery *DbQueryPayloadCollection `json:"dbQuery,omitempty"` +} diff --git a/api/odigos/v1alpha1/instrumentationrules/zz_generated.deepcopy.go b/api/odigos/v1alpha1/instrumentationrules/zz_generated.deepcopy.go new file mode 100644 index 000000000..1c61a9709 --- /dev/null +++ b/api/odigos/v1alpha1/instrumentationrules/zz_generated.deepcopy.go @@ -0,0 +1,112 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package instrumentationrules + +import () + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbQueryPayloadCollection) DeepCopyInto(out *DbQueryPayloadCollection) { + *out = *in + if in.MaxPayloadLength != nil { + in, out := &in.MaxPayloadLength, &out.MaxPayloadLength + *out = new(int64) + **out = **in + } + if in.DropPartialPayloads != nil { + in, out := &in.DropPartialPayloads, &out.DropPartialPayloads + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbQueryPayloadCollection. +func (in *DbQueryPayloadCollection) DeepCopy() *DbQueryPayloadCollection { + if in == nil { + return nil + } + out := new(DbQueryPayloadCollection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HttpPayloadCollection) DeepCopyInto(out *HttpPayloadCollection) { + *out = *in + if in.MimeTypes != nil { + in, out := &in.MimeTypes, &out.MimeTypes + *out = new([]string) + if **in != nil { + in, out := *in, *out + *out = make([]string, len(*in)) + copy(*out, *in) + } + } + if in.MaxPayloadLength != nil { + in, out := &in.MaxPayloadLength, &out.MaxPayloadLength + *out = new(int64) + **out = **in + } + if in.DropPartialPayloads != nil { + in, out := &in.DropPartialPayloads, &out.DropPartialPayloads + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HttpPayloadCollection. +func (in *HttpPayloadCollection) DeepCopy() *HttpPayloadCollection { + if in == nil { + return nil + } + out := new(HttpPayloadCollection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PayloadCollection) DeepCopyInto(out *PayloadCollection) { + *out = *in + if in.HttpRequest != nil { + in, out := &in.HttpRequest, &out.HttpRequest + *out = new(HttpPayloadCollection) + (*in).DeepCopyInto(*out) + } + if in.HttpResponse != nil { + in, out := &in.HttpResponse, &out.HttpResponse + *out = new(HttpPayloadCollection) + (*in).DeepCopyInto(*out) + } + if in.DbQuery != nil { + in, out := &in.DbQuery, &out.DbQuery + *out = new(DbQueryPayloadCollection) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PayloadCollection. +func (in *PayloadCollection) DeepCopy() *PayloadCollection { + if in == nil { + return nil + } + out := new(PayloadCollection) + in.DeepCopyInto(out) + return out +} diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 417c21da1..f06b215ab 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -21,7 +21,9 @@ limitations under the License. package v1alpha1 import ( + "github.com/odigos-io/odigos/api/odigos/v1alpha1/instrumentationrules" "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -574,6 +576,11 @@ func (in *InstrumentationLibraryConfig) DeepCopyInto(out *InstrumentationLibrary *out = new(InstrumentationLibraryConfigTraces) (*in).DeepCopyInto(*out) } + if in.PayloadCollection != nil { + in, out := &in.PayloadCollection, &out.PayloadCollection + *out = new(instrumentationrules.PayloadCollection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationLibraryConfig. @@ -606,6 +613,21 @@ func (in *InstrumentationLibraryConfigTraces) DeepCopy() *InstrumentationLibrary return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationLibraryGlobalId) DeepCopyInto(out *InstrumentationLibraryGlobalId) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationLibraryGlobalId. +func (in *InstrumentationLibraryGlobalId) DeepCopy() *InstrumentationLibraryGlobalId { + if in == nil { + return nil + } + out := new(InstrumentationLibraryGlobalId) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstrumentationLibraryId) DeepCopyInto(out *InstrumentationLibraryId) { *out = *in @@ -672,6 +694,125 @@ func (in *InstrumentationLibraryStatus) DeepCopy() *InstrumentationLibraryStatus return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationRule) DeepCopyInto(out *InstrumentationRule) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationRule. +func (in *InstrumentationRule) DeepCopy() *InstrumentationRule { + if in == nil { + return nil + } + out := new(InstrumentationRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InstrumentationRule) 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 *InstrumentationRuleList) DeepCopyInto(out *InstrumentationRuleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]InstrumentationRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationRuleList. +func (in *InstrumentationRuleList) DeepCopy() *InstrumentationRuleList { + if in == nil { + return nil + } + out := new(InstrumentationRuleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InstrumentationRuleList) 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 *InstrumentationRuleSpec) DeepCopyInto(out *InstrumentationRuleSpec) { + *out = *in + if in.Workloads != nil { + in, out := &in.Workloads, &out.Workloads + *out = new([]workload.PodWorkload) + if **in != nil { + in, out := *in, *out + *out = make([]workload.PodWorkload, len(*in)) + copy(*out, *in) + } + } + if in.InstrumentationLibraries != nil { + in, out := &in.InstrumentationLibraries, &out.InstrumentationLibraries + *out = new([]InstrumentationLibraryGlobalId) + if **in != nil { + in, out := *in, *out + *out = make([]InstrumentationLibraryGlobalId, len(*in)) + copy(*out, *in) + } + } + if in.PayloadCollection != nil { + in, out := &in.PayloadCollection, &out.PayloadCollection + *out = new(instrumentationrules.PayloadCollection) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationRuleSpec. +func (in *InstrumentationRuleSpec) DeepCopy() *InstrumentationRuleSpec { + if in == nil { + return nil + } + out := new(InstrumentationRuleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationRuleStatus) DeepCopyInto(out *InstrumentationRuleStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationRuleStatus. +func (in *InstrumentationRuleStatus) DeepCopy() *InstrumentationRuleStatus { + if in == nil { + return nil + } + out := new(InstrumentationRuleStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstrumentedApplication) DeepCopyInto(out *InstrumentedApplication) { *out = *in @@ -1045,7 +1186,16 @@ func (in *SdkConfig) DeepCopyInto(out *SdkConfig) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.HeadSamplingConfig.DeepCopyInto(&out.HeadSamplingConfig) + if in.HeadSamplingConfig != nil { + in, out := &in.HeadSamplingConfig, &out.HeadSamplingConfig + *out = new(HeadSamplingConfig) + (*in).DeepCopyInto(*out) + } + if in.DefaultPayloadCollection != nil { + in, out := &in.DefaultPayloadCollection, &out.DefaultPayloadCollection + *out = new(instrumentationrules.PayloadCollection) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SdkConfig. diff --git a/cli/cmd/resources/instrumentor.go b/cli/cmd/resources/instrumentor.go index d105a6051..29e3fe223 100644 --- a/cli/cmd/resources/instrumentor.go +++ b/cli/cmd/resources/instrumentor.go @@ -186,6 +186,11 @@ func NewInstrumentorClusterRole() *rbacv1.ClusterRole { Resources: []string{"instrumentationconfigs"}, Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"instrumentationrules"}, + Verbs: []string{"get", "list", "watch"}, + }, }, } } diff --git a/docs/mint.json b/docs/mint.json index 9fc80556b..ae868ccd0 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -136,6 +136,13 @@ "pipeline/sources/troubleshooting" ] }, + { + "group": "Instrumentation Rules", + "pages": [ + "pipeline/rules/overview", + "pipeline/rules/payloadcollection" + ] + }, { "group": "Actions", "pages": [ diff --git a/docs/pipeline/rules/overview.mdx b/docs/pipeline/rules/overview.mdx new file mode 100644 index 000000000..8a54210f7 --- /dev/null +++ b/docs/pipeline/rules/overview.mdx @@ -0,0 +1,51 @@ +--- +title: "Instrumentation Rules Overview" +sidebarTitle: "Overview" +--- + +Instrumentation Rules control how telemetry is recorded from your application. + +## Rule Types: + +- [Payload Collection](/pipeline/rules/payloadcollection) + +## Scope + +A rule can be applied to a set of Odigos sources (k8s workloads) and instrumentation libraries. + +### Sources + +- To apply a rule to all sources in the cluster, omit the `workloads` field. +- To limit the rule to specific sources, provide an array of workload objects (name, namespace, kind). + +### Instrumentation Libraries + +This is an advanced feature that allows you to apply a rule to a specific instrumentation library. +It is recommended to set the same rules to all instrumentation libraries, and only use this feature when you need fine-grained control. + +- To apply the rule to all instrumentation libraries, omit the `instrumentationLibraries` field. +- Alternatively, you can specify a list of instrumentation libraries to which the rule should be applied. Instrumentation library is identified by a combination of it's unique name, and language (and span kind for golang). + +Any rule on a specific library will take precedence over a rule that does not specify a library. + +## Configuration Options + +- `ruleName` (optional): Allows you to attach a meaningful name to the rule for convenience and documentation. Odigos does not use or assume any meaning from this field. + +- `notes` (optional): A free-form text field that allows you to attach notes regarding the rule for convenience. For example: why it was added. Odigos does not use or assume any meaning from this field. + +- `disabled` (optional, default is `false`, e.g. enabled): A boolean field allowing to temporarily disable the rule, but keep it around for future use + +- `workloads` (optional, default is omitted, e.g. all workloads): An array of workload objects (name, namespace, kind) to which the rule should be applied. If not specified, the rule will be applied to all workloads. empty array will make the rule ineffective. + +- `instrumentationLibraries` (optional, default is omitted, e.g. all libraries): An array of instrumentation library id objects to which the rule should be applied. If not specified, the rule will be applied to all instrumentation libraries. empty array will make the rule ineffective. + +## Merging Rules + +You can specify multiple rules in your cluster: + +- When multiple teams work on the same cluster, they can each define their own rules. +- To define some global default behavior and override it for specific cases. +- If you need to create a temporary rule for a givin task which will be removed later. + +Odigos allows you to define multiple rules, and will merge them together to create a single rule per source. diff --git a/docs/pipeline/rules/payloadcollection.mdx b/docs/pipeline/rules/payloadcollection.mdx new file mode 100644 index 000000000..3b589ac39 --- /dev/null +++ b/docs/pipeline/rules/payloadcollection.mdx @@ -0,0 +1,122 @@ +--- +title: "Payload Collection Instrumentation Rule" +sidebarTitle: "Payload Collection" +--- + + available in Odigos pro only + +The "Payload Collection" Rule can be used to add span attributes containing payload data to traces. + +In many cases, the payload data can provide valuable context for understanding the behavior of your application. + +## Considerations + +Before enabling payload collection, please note the following: + +- PII (Personally Identifiable Information) and other potentially sensitive data may be present in the payload. Evaluate the risk of collecting this data, and consider using the [PII Masking Action](/pipeline/actions/attributes/piimasking) to mask sensitive data. +- Payload data can be large and may increase the size of your traces. This can impact the performance of your application and the cost of processing, storing and analyzing traces. +- The support for payload collection varies between instrumentation libraries and languages. Not all libraries support payload collection, and the supported payload types and formats may differ. Consult the documentation of the instrumentation library you are using for more information. + +## Basic Example + +The following example demonstrates how to enable payload collection for all supported workloads and instrumentation libraries in the cluster. + +Create a file named `payloadcollection.yaml` with the following content: + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationRule +metadata: + name: collect-all-payloads + namespace: odigos-system +spec: + ruleName: "collect all supported payloads" + payloadCollection: + httpRequest: {} + httpResponse: {} + dbQuery: {} +``` + +Apply the action to the cluster: + +```shell +kubectl apply -f payloadcollection.yaml +``` + +## Full Example + +The following example is a demonstration of all the options available in the "Payload Collection" Rule. +It is not meant to be used "as is", but rather as a reference to customize the rule to your needs. + +Create a file named `full-payload-collection-example.yaml` with the following content: + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationRule +metadata: + name: full-payload-collection-example + namespace: odigos-system +spec: + ruleName: "Full example for payload collection" + disabled: false + notes: "This rule showcase all the options available for payload collection rule" + workloads: + - kind: Deployment + name: example-deployment + namespace: default + - kind: DaemonSet + name: example-ds + namespace: default + instrumentationLibraries: + - language: go + name: "net/http" + spanKind: server + - language: go + name: "database/sql" + spanKind: client + payloadCollection: + httpRequest: + mimeTypes: + - "application/json" + maxPayloadLength: 2048 + dropPartialPayloads: true + httpResponse: + mimeTypes: + - "application/json" + - "text/plain" + maxPayloadLength: 8096 + dropPartialPayloads: true + dbQuery: + maxPayloadLength: 1024 + dropPartialPayloads: true +``` + +Apply the action to the cluster: + +```shell +kubectl apply -f full-payload-collection-example.yaml +``` + +### Configuration Options + +The full list of configuration options for the "Payload Collection" Rule are: + +- `httpRequest` (optional): Collect HTTP request payload data when available. Can be a client (outgoing) request or a server (incoming) request, depending on the instrumentation library + - `mimeTypes` (optional, default is nil - all mime types): Limit payload collection to specific mime types based on the content type header. When not specified, all mime types payloads will be collected. empty array will make the rule ineffective. + - `maxPayloadLength` (optional): Maximum length of the payload to collect. If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option. When not specified (recommended), the instrumentation library will use any reasonable default value. + - `dropPartialPayloads` (optional, default is false): If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + +- `httpResponse` (optional): Collect HTTP response payload data when available. Can be a client (incoming) response or a server (outgoing) response, depending on the instrumentation library + - `mimeTypes` (optional, default is nil - all mime types): Limit payload collection to specific mime types based on the content type header. When not specified, all mime types payloads will be collected. empty array will make the rule ineffective. + - `maxPayloadLength` (optional): Maximum length of the payload to collect. If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option. When not specified (recommended), the instrumentation library will use any reasonable default value + - `dropPartialPayloads` (optional, default is false): If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + +- `dbQuery` (optional): Collect database query payload info when available. + - `maxPayloadLength` (optional): Maximum length of the payload to collect. If the payload is longer than this value, it will be truncated or dropped, based on the value of `dropPartialPayloads` config option. When not specified (recommended), the instrumentation library will use any reasonable default value + - `dropPartialPayloads` (optional, default is false): If the payload is larger than the MaxPayloadLength, this parameter will determine if the payload should be partially collected up to the allowed length, or not collected at all. This is useful if you require some decoding of the payload (like json) and having it partially is not useful. + +## Merging Rules + +- Any payload collection rule for a specific instrumentation library will take precedence over a rule that does not specify a library. +- If multiple rules are defined for the same source, the rules will be merged together. Any conflicting options will be resolved by taking the "safest" option. +- Any unspecified options will fallback to a reasonable default value provided by the instrumentation library (recommended). diff --git a/helm/odigos/templates/instrumentor/clusterrole.yaml b/helm/odigos/templates/instrumentor/clusterrole.yaml index 67d9f499d..fb0dd5bab 100644 --- a/helm/odigos/templates/instrumentor/clusterrole.yaml +++ b/helm/odigos/templates/instrumentor/clusterrole.yaml @@ -98,3 +98,11 @@ rules: - patch - update - watch + - apiGroups: + - odigos.io + resources: + - instrumentationrules + verbs: + - get + - list + - watch diff --git a/instrumentor/controllers/instrumentationconfig/common.go b/instrumentor/controllers/instrumentationconfig/common.go new file mode 100644 index 000000000..667f04bc2 --- /dev/null +++ b/instrumentor/controllers/instrumentationconfig/common.go @@ -0,0 +1,215 @@ +package instrumentationconfig + +import ( + odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/api/odigos/v1alpha1/instrumentationrules" + "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +func updateInstrumentationConfigForWorkload(ic *odigosv1alpha1.InstrumentationConfig, ia *odigosv1alpha1.InstrumentedApplication, rules *odigosv1alpha1.InstrumentationRuleList) error { + + workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(ia.Name) + if err != nil { + return err + } + workload := workload.PodWorkload{ + Name: workloadName, + Namespace: ia.Namespace, + Kind: workloadKind, + } + + sdkConfigs := make([]odigosv1alpha1.SdkConfig, 0, len(ia.Spec.RuntimeDetails)) + + // create an empty sdk config for each detected programming language + for _, container := range ia.Spec.RuntimeDetails { + containerLanguage := container.Language + if containerLanguage == common.IgnoredProgrammingLanguage || containerLanguage == common.UnknownProgrammingLanguage { + continue + } + sdkConfigs = createDefaultSdkConfig(sdkConfigs, containerLanguage) + } + + // iterate over all the payload collection rules, and update the instrumentation config accordingly + for i := range rules.Items { + rule := &rules.Items[i] + if rule.Spec.Disabled { + continue + } + // filter out rules where the workload does not match + participating := isWorkloadParticipatingInRule(workload, rule) + if !participating { + continue + } + + for i := range sdkConfigs { + if rule.Spec.InstrumentationLibraries == nil { // nil means a rule in SDK level, that applies unless overridden by library level rule + sdkConfigs[i].DefaultPayloadCollection.HttpRequest = mergeHttpPayloadCollectionRules(sdkConfigs[i].DefaultPayloadCollection.HttpRequest, rule.Spec.PayloadCollection.HttpRequest) + sdkConfigs[i].DefaultPayloadCollection.HttpResponse = mergeHttpPayloadCollectionRules(sdkConfigs[i].DefaultPayloadCollection.HttpResponse, rule.Spec.PayloadCollection.HttpResponse) + sdkConfigs[i].DefaultPayloadCollection.DbQuery = mergeDbPayloadCollectionRules(sdkConfigs[i].DefaultPayloadCollection.DbQuery, rule.Spec.PayloadCollection.DbQuery) + } else { + for _, library := range *rule.Spec.InstrumentationLibraries { + libraryConfig := findOrCreateSdkLibraryConfig(&sdkConfigs[i], library) + if libraryConfig == nil { + // library is not relevant to this SDK + continue + } + libraryConfig.PayloadCollection.HttpRequest = mergeHttpPayloadCollectionRules(libraryConfig.PayloadCollection.HttpRequest, rule.Spec.PayloadCollection.HttpRequest) + libraryConfig.PayloadCollection.HttpResponse = mergeHttpPayloadCollectionRules(libraryConfig.PayloadCollection.HttpResponse, rule.Spec.PayloadCollection.HttpResponse) + libraryConfig.PayloadCollection.DbQuery = mergeDbPayloadCollectionRules(libraryConfig.PayloadCollection.DbQuery, rule.Spec.PayloadCollection.DbQuery) + } + } + } + } + + ic.Spec.SdkConfigs = sdkConfigs + + return nil +} + +// returns a pointer to the instrumentation library config, creating it if it does not exist +// the pointer can be used to modify the config +func findOrCreateSdkLibraryConfig(sdkConfig *odigosv1alpha1.SdkConfig, library odigosv1alpha1.InstrumentationLibraryGlobalId) *odigosv1alpha1.InstrumentationLibraryConfig { + if library.Language != sdkConfig.Language { + return nil + } + + for i, libConfig := range sdkConfig.InstrumentationLibraryConfigs { + if libConfig.InstrumentationLibraryId.InstrumentationLibraryName == library.Name && + libConfig.InstrumentationLibraryId.SpanKind == library.SpanKind { + + // if already present, return a pointer to it which can be modified by the caller + return &sdkConfig.InstrumentationLibraryConfigs[i] + } + } + newLibConfig := odigosv1alpha1.InstrumentationLibraryConfig{ + InstrumentationLibraryId: odigosv1alpha1.InstrumentationLibraryId{ + InstrumentationLibraryName: library.Name, + SpanKind: library.SpanKind, + }, + PayloadCollection: &instrumentationrules.PayloadCollection{}, + } + sdkConfig.InstrumentationLibraryConfigs = append(sdkConfig.InstrumentationLibraryConfigs, newLibConfig) + return &sdkConfig.InstrumentationLibraryConfigs[len(sdkConfig.InstrumentationLibraryConfigs)-1] +} + +func createDefaultSdkConfig(sdkConfigs []odigosv1alpha1.SdkConfig, containerLanguage common.ProgrammingLanguage) []odigosv1alpha1.SdkConfig { + // if the language is already present, do nothing + for _, sdkConfig := range sdkConfigs { + if sdkConfig.Language == containerLanguage { + return sdkConfigs + } + } + return append(sdkConfigs, odigosv1alpha1.SdkConfig{ + Language: containerLanguage, + DefaultPayloadCollection: &instrumentationrules.PayloadCollection{}, + }) +} + +// naive implementation, can be optimized. +// assumption is that the list of workloads is small +func isWorkloadParticipatingInRule(workload workload.PodWorkload, rule *odigosv1alpha1.InstrumentationRule) bool { + // nil means all workloads are participating + if rule.Spec.Workloads == nil { + return true + } + for _, allowedWorkload := range *rule.Spec.Workloads { + if allowedWorkload == workload { + return true + } + } + return false +} + +func mergeHttpPayloadCollectionRules(rule1 *instrumentationrules.HttpPayloadCollection, rule2 *instrumentationrules.HttpPayloadCollection) *instrumentationrules.HttpPayloadCollection { + + // nil means a rules has not yet been set, so return the other rule + if rule1 == nil { + return rule2 + } else if rule2 == nil { + return rule1 + } + + // merge of the 2 non nil rules + mergedRules := instrumentationrules.HttpPayloadCollection{} + + // MimeTypes is extended to include both. nil means "all" so treat it as such + if rule1.MimeTypes == nil || rule2.MimeTypes == nil { + mergedRules.MimeTypes = nil + } else { + mergeMimeTypeMap := make(map[string]struct{}) + for _, mimeType := range *rule1.MimeTypes { + mergeMimeTypeMap[mimeType] = struct{}{} + } + for _, mimeType := range *rule2.MimeTypes { + mergeMimeTypeMap[mimeType] = struct{}{} + } + mergedMimeTypeSlice := make([]string, 0, len(mergeMimeTypeMap)) + for mimeType := range mergeMimeTypeMap { + mergedMimeTypeSlice = append(mergedMimeTypeSlice, mimeType) + } + mergedRules.MimeTypes = &mergedMimeTypeSlice + } + + // MaxPayloadLength - choose the smallest value, as this is the maximum allowed + if rule1.MaxPayloadLength == nil { + mergedRules.MaxPayloadLength = rule2.MaxPayloadLength + } else if rule2.MaxPayloadLength == nil { + mergedRules.MaxPayloadLength = rule1.MaxPayloadLength + } else { + if *rule1.MaxPayloadLength < *rule2.MaxPayloadLength { + mergedRules.MaxPayloadLength = rule1.MaxPayloadLength + } else { + mergedRules.MaxPayloadLength = rule2.MaxPayloadLength + } + } + + // DropPartialPayloads - if any of the rules is set to drop, the merged rule will drop + if rule1.DropPartialPayloads == nil { + mergedRules.DropPartialPayloads = rule2.DropPartialPayloads + } else if rule2.DropPartialPayloads == nil { + mergedRules.DropPartialPayloads = rule1.DropPartialPayloads + } else { + mergedRules.DropPartialPayloads = boolPtr(*rule1.DropPartialPayloads || *rule2.DropPartialPayloads) + } + + return &mergedRules +} + +func mergeDbPayloadCollectionRules(rule1 *instrumentationrules.DbQueryPayloadCollection, rule2 *instrumentationrules.DbQueryPayloadCollection) *instrumentationrules.DbQueryPayloadCollection { + if rule1 == nil { + return rule2 + } else if rule2 == nil { + return rule1 + } + + mergedRules := instrumentationrules.DbQueryPayloadCollection{} + + // MaxPayloadLength - choose the smallest value, as this is the maximum allowed + if rule1.MaxPayloadLength == nil { + mergedRules.MaxPayloadLength = rule2.MaxPayloadLength + } else if rule2.MaxPayloadLength == nil { + mergedRules.MaxPayloadLength = rule1.MaxPayloadLength + } else { + if *rule1.MaxPayloadLength < *rule2.MaxPayloadLength { + mergedRules.MaxPayloadLength = rule1.MaxPayloadLength + } else { + mergedRules.MaxPayloadLength = rule2.MaxPayloadLength + } + } + + // DropPartialPayloads - if any of the rules is set to drop, the merged rule will drop + if rule1.DropPartialPayloads == nil { + mergedRules.DropPartialPayloads = rule2.DropPartialPayloads + } else if rule2.DropPartialPayloads == nil { + mergedRules.DropPartialPayloads = rule1.DropPartialPayloads + } else { + mergedRules.DropPartialPayloads = boolPtr(*rule1.DropPartialPayloads || *rule2.DropPartialPayloads) + } + + return &mergedRules +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/instrumentor/controllers/instrumentationconfig/common_test.go b/instrumentor/controllers/instrumentationconfig/common_test.go new file mode 100644 index 000000000..c924168b6 --- /dev/null +++ b/instrumentor/controllers/instrumentationconfig/common_test.go @@ -0,0 +1,738 @@ +package instrumentationconfig + +import ( + "testing" + + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/api/odigos/v1alpha1/instrumentationrules" + "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestUpdateInstrumentationConfigForWorkload_SingleLanguage(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{{ + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }}, + }, + } + rules := &odigosv1.InstrumentationRuleList{} + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + if ic.Spec.SdkConfigs[0].Language != common.JavascriptProgrammingLanguage { + t.Errorf("Expected language %s, got %s", common.JavascriptProgrammingLanguage, ic.Spec.SdkConfigs[0].Language) + } +} + +func TestUpdateInstrumentationConfigForWorkload_MultipleLanguages(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container-1", + Language: common.JavascriptProgrammingLanguage, + }, + { + ContainerName: "test-container-2", + Language: common.PythonProgrammingLanguage, + }, + }, + }, + } + rules := &odigosv1.InstrumentationRuleList{} + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 2 { + t.Errorf("Expected 2 sdk configs, got %d", len(ic.Spec.SdkConfigs)) + } + if ic.Spec.SdkConfigs[0].Language != common.JavascriptProgrammingLanguage { + t.Errorf("Expected language %s, got %s", common.JavascriptProgrammingLanguage, ic.Spec.SdkConfigs[0].Language) + } + if ic.Spec.SdkConfigs[1].Language != common.PythonProgrammingLanguage { + t.Errorf("Expected language %s, got %s", common.PythonProgrammingLanguage, ic.Spec.SdkConfigs[1].Language) + } +} + +func TestUpdateInstrumentationConfigForWorkload_IgnoreUnknownLanguage(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container-1", + Language: common.JavascriptProgrammingLanguage, + }, + { + ContainerName: "test-container-2", + Language: common.UnknownProgrammingLanguage, + }, + { + ContainerName: "test-container-3", + Language: common.IgnoredProgrammingLanguage, + }, + }, + }, + } + rules := &odigosv1.InstrumentationRuleList{} + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + if ic.Spec.SdkConfigs[0].Language != common.JavascriptProgrammingLanguage { + t.Errorf("Expected language %s, got %s", common.JavascriptProgrammingLanguage, ic.Spec.SdkConfigs[0].Language) + } +} + +func TestUpdateInstrumentationConfigForWorkload_NoLanguages(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{}, + }, + } + rules := &odigosv1.InstrumentationRuleList{} + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 0 { + t.Errorf("Expected 0 sdk configs, got %d", len(ic.Spec.SdkConfigs)) + } +} + +func TestUpdateInstrumentationConfigForWorkload_SameLanguageMultipleContainers(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container-1", + Language: common.JavascriptProgrammingLanguage, + }, + { + ContainerName: "test-container-2", + Language: common.JavascriptProgrammingLanguage, + }, + }, + }, + } + rules := &odigosv1.InstrumentationRuleList{} + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + if ic.Spec.SdkConfigs[0].Language != common.JavascriptProgrammingLanguage { + t.Errorf("Expected language %s, got %s", common.JavascriptProgrammingLanguage, ic.Spec.SdkConfigs[0].Language) + } +} + +func TestUpdateInstrumentationConfigForWorkload_SingleMatchingRule(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }, + }, + }, + } + rules := &odigosv1.InstrumentationRuleList{ + Items: []odigosv1.InstrumentationRule{ + { + Spec: odigosv1.InstrumentationRuleSpec{ + PayloadCollection: &instrumentationrules.PayloadCollection{ + HttpRequest: &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/json"}, + MaxPayloadLength: Int64Ptr(1234), + DropPartialPayloads: BoolPtr(true), + }, + }, + }, + }, + }, + } + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + if len(*ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MimeTypes) != 1 { + t.Errorf("Expected 1 mime type, got %d", len(*ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MimeTypes)) + } + if (*ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MimeTypes)[0] != "application/json" { + t.Errorf("Expected mime type %s, got %s", "application/json", (*ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MimeTypes)[0]) + } + if *ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MaxPayloadLength != 1234 { + t.Errorf("Expected max payload length %d, got %d", 1234, ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MaxPayloadLength) + } + if *ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.DropPartialPayloads != true { + t.Errorf("Expected drop partial payloads %t, got %t", true, *ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.DropPartialPayloads) + } +} + +func TestUpdateInstrumentationConfigForWorkload_InWorkloadList(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }, + }, + }, + } + + rules := &odigosv1.InstrumentationRuleList{ + Items: []odigosv1.InstrumentationRule{ + { + Spec: odigosv1.InstrumentationRuleSpec{ + Workloads: &[]workload.PodWorkload{ + { + Name: "test", + Kind: workload.WorkloadKindDeployment, + Namespace: "testns", + }, + }, + PayloadCollection: &instrumentationrules.PayloadCollection{ + HttpRequest: &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/json"}, + }, + }, + }, + }, + }, + } + + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + if len(*ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MimeTypes) != 1 { + t.Errorf("Expected 1 mime type, got %d", len(*ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MimeTypes)) + } +} + +func TestUpdateInstrumentationConfigForWorkload_NotInWorkloadList(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }, + }, + }, + } + + rules := &odigosv1.InstrumentationRuleList{ + Items: []odigosv1.InstrumentationRule{ + { + Spec: odigosv1.InstrumentationRuleSpec{ + Workloads: &[]workload.PodWorkload{ + { + Name: "someotherdeployment", + Kind: workload.WorkloadKindDeployment, + Namespace: "testns", + }, + }, + PayloadCollection: &instrumentationrules.PayloadCollection{ + HttpRequest: &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/json"}, + }, + }, + }, + }, + }, + } + + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 0 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + // rule should be ignored since "test" deployment is not in the workload list + if ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest != nil { + t.Errorf("Expected nil, got %v", ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest) + } +} + +func TestUpdateInstrumentationConfigForWorkload_DisabledRule(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }, + }, + }, + } + + rules := &odigosv1.InstrumentationRuleList{ + Items: []odigosv1.InstrumentationRule{ + { + Spec: odigosv1.InstrumentationRuleSpec{ + Disabled: true, + PayloadCollection: &instrumentationrules.PayloadCollection{ + HttpRequest: &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/json"}, + }, + }, + }, + }, + }, + } + + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + // rule should be ignored since it is disabled + if ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest != nil { + t.Errorf("Expected nil, got %v", ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest) + } +} + +func TestUpdateInstrumentationConfigForWorkload_MultipleDefaultRules(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }, + }, + }, + } + + rules := &odigosv1.InstrumentationRuleList{ + Items: []odigosv1.InstrumentationRule{ + { + Spec: odigosv1.InstrumentationRuleSpec{ + PayloadCollection: &instrumentationrules.PayloadCollection{ + HttpRequest: &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/json", "application/text"}, + MaxPayloadLength: Int64Ptr(1111), + DropPartialPayloads: BoolPtr(true), + }, + }, + }, + }, + { + Spec: odigosv1.InstrumentationRuleSpec{ + PayloadCollection: &instrumentationrules.PayloadCollection{ + HttpRequest: &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/xml", "application/json"}, + MaxPayloadLength: Int64Ptr(2222), + DropPartialPayloads: BoolPtr(false), + }, + }, + }, + }, + }, + } + + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + + // mime types should merge + mimeTypes := ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MimeTypes + if len(*mimeTypes) != 3 { + t.Errorf("Expected 2 mime types, got %d", len(*ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MimeTypes)) + } + expectedMimeTypes := map[string]bool{ + "application/json": true, + "application/xml": true, + "application/text": true, + } + for _, mt := range *mimeTypes { + if !expectedMimeTypes[mt] { + t.Errorf("Unexpected mime type %s", mt) + } + } + // Ensure all expected mime types are present + for expected := range expectedMimeTypes { + found := false + for _, actual := range *mimeTypes { + if actual == expected { + found = true + break + } + } + if !found { + t.Errorf("Missing expected mime type %s", expected) + } + } + + // smallest max payload length should be selected + if *ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MaxPayloadLength != 1111 { + t.Errorf("Expected max payload length %d, got %d", 1111, ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.MaxPayloadLength) + } + // one of the rules has drop partial payloads set to true, so it should be true + if *ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.DropPartialPayloads != true { + t.Errorf("Expected drop partial payloads %t, got %t", true, *ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest.DropPartialPayloads) + } +} + +func TestUpdateInstrumentationConfigForWorkload_RuleForLibrary(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }, + }, + }, + } + + rules := &odigosv1.InstrumentationRuleList{ + Items: []odigosv1.InstrumentationRule{ + { + Spec: odigosv1.InstrumentationRuleSpec{ + InstrumentationLibraries: &[]odigosv1.InstrumentationLibraryGlobalId{ + { + Name: "test-library", + Language: common.JavascriptProgrammingLanguage, + }, + }, + PayloadCollection: &instrumentationrules.PayloadCollection{ + HttpRequest: &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/json"}, + }, + }, + }, + }, + }, + } + + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + if ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest != nil { + t.Errorf("Expected nil, got %v", ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest) + } + if len(ic.Spec.SdkConfigs[0].InstrumentationLibraryConfigs) != 1 { + t.Errorf("Expected 1 library, got %d", len(ic.Spec.SdkConfigs[0].InstrumentationLibraryConfigs)) + } + if len(*ic.Spec.SdkConfigs[0].InstrumentationLibraryConfigs[0].PayloadCollection.HttpRequest.MimeTypes) != 1 { + t.Errorf("Expected 1 mime type, got %d", len(*ic.Spec.SdkConfigs[0].InstrumentationLibraryConfigs[0].PayloadCollection.HttpRequest.MimeTypes)) + } +} + +func TestUpdateInstrumentationConfigForWorkload_LibraryRuleOtherLanguage(t *testing.T) { + + ic := odigosv1.InstrumentationConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentationConfigSpec{}, + } + ia := odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-test", + Namespace: "testns", + }, + Spec: odigosv1.InstrumentedApplicationSpec{ + RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }, + }, + }, + } + + rules := &odigosv1.InstrumentationRuleList{ + Items: []odigosv1.InstrumentationRule{ + { + Spec: odigosv1.InstrumentationRuleSpec{ + Disabled: true, + InstrumentationLibraries: &[]odigosv1.InstrumentationLibraryGlobalId{ + { + Name: "test-library", + Language: common.PythonProgrammingLanguage, // Notice, the library is for python and sdk language is javascript + }, + }, + PayloadCollection: &instrumentationrules.PayloadCollection{ + HttpRequest: &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/json"}, + }, + }, + }, + }, + }, + } + + err := updateInstrumentationConfigForWorkload(&ic, &ia, rules) + if err != nil { + t.Errorf("Expected nil error, got %v", err) + } + if len(ic.Spec.SdkConfigs) != 1 { + t.Errorf("Expected 1 sdk config, got %d", len(ic.Spec.SdkConfigs)) + } + if ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest != nil { + t.Errorf("Expected nil, got %v", ic.Spec.SdkConfigs[0].DefaultPayloadCollection.HttpRequest) + } + if len(ic.Spec.SdkConfigs[0].InstrumentationLibraryConfigs) != 0 { // the library specified is for different language + t.Errorf("Expected 0 libraries, got %d", len(ic.Spec.SdkConfigs[0].InstrumentationLibraryConfigs)) + } +} + +func TestMergeHttpPayloadCollectionRules(t *testing.T) { + res := mergeHttpPayloadCollectionRules(&instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/json"}, + MaxPayloadLength: Int64Ptr(1234), + }, &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/xml"}, + DropPartialPayloads: BoolPtr(false), + }) + if len(*res.MimeTypes) != 2 { + t.Errorf("Expected 2 mime types, got %d", len(*res.MimeTypes)) + } + // Test MimeTypes without considering the order + expectedMimeTypes := map[string]bool{ + "application/json": true, + "application/xml": true, + } + for _, mt := range *res.MimeTypes { + if !expectedMimeTypes[mt] { + t.Errorf("Unexpected mime type %s", mt) + } + } + // Ensure all expected mime types are present + for expected := range expectedMimeTypes { + found := false + for _, actual := range *res.MimeTypes { + if actual == expected { + found = true + break + } + } + if !found { + t.Errorf("Missing expected mime type %s", expected) + } + } + + if *res.MaxPayloadLength != 1234 { + t.Errorf("Expected max payload length %d, got %d", 1234, *res.MaxPayloadLength) + } + if *res.DropPartialPayloads != false { + t.Errorf("Expected drop partial payloads %t, got %t", false, *res.DropPartialPayloads) + } +} + +func TestMergeHttpPayloadCollectionRules_BothNil(t *testing.T) { + res := mergeHttpPayloadCollectionRules(nil, nil) + if res != nil { + t.Errorf("Expected nil, got %v", res) + } +} + +func TestMergeHttpPayloadCollectionRules_FirstNil(t *testing.T) { + res := mergeHttpPayloadCollectionRules(nil, &instrumentationrules.HttpPayloadCollection{ + MimeTypes: &[]string{"application/xml"}, + DropPartialPayloads: BoolPtr(false), + }) + if len(*res.MimeTypes) != 1 { + t.Errorf("Expected 1 mime type, got %d", len(*res.MimeTypes)) + } + if (*res.MimeTypes)[0] != "application/xml" { + t.Errorf("Expected mime type %s, got %s", "application/xml", (*res.MimeTypes)[0]) + } + if res.MaxPayloadLength != nil { + t.Errorf("Expected nil, got %v", res.MaxPayloadLength) + } + if *res.DropPartialPayloads != false { + t.Errorf("Expected drop partial payloads %t, got %t", false, *res.DropPartialPayloads) + } +} + +func TestMergeHttpPayloadCollectionRules_SecondNil(t *testing.T) { + res := mergeHttpPayloadCollectionRules(&instrumentationrules.HttpPayloadCollection{ + MaxPayloadLength: Int64Ptr(1234), + }, nil) + if res.MimeTypes != nil { + t.Errorf("Expected nil, got %v", res.MimeTypes) + } + if *res.MaxPayloadLength != 1234 { + t.Errorf("Expected max payload length %d, got %d", 1234, *res.MaxPayloadLength) + } + if res.DropPartialPayloads != nil { + t.Errorf("Expected nil, got %v", res.DropPartialPayloads) + } +} + +func BoolPtr(b bool) *bool { + return &b +} + +func Int64Ptr(i int64) *int64 { + return &i +} diff --git a/instrumentor/controllers/instrumentationconfig/instrumentationrule_controller.go b/instrumentor/controllers/instrumentationconfig/instrumentationrule_controller.go new file mode 100644 index 000000000..a5971964d --- /dev/null +++ b/instrumentor/controllers/instrumentationconfig/instrumentationrule_controller.go @@ -0,0 +1,64 @@ +package instrumentationconfig + +import ( + "context" + + odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + 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" +) + +type InstrumentationRuleReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *InstrumentationRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + logger := log.FromContext(ctx) + + instrumentationRules := &odigosv1alpha1.InstrumentationRuleList{} + err := r.Client.List(ctx, instrumentationRules) + if err != nil { + return ctrl.Result{}, err + } + + instrumentedApplications := &odigosv1alpha1.InstrumentedApplicationList{} + err = r.Client.List(ctx, instrumentedApplications) + if err != nil { + return ctrl.Result{}, err + } + + for _, ia := range instrumentedApplications.Items { + ic := &odigosv1alpha1.InstrumentationConfig{} + err = r.Client.Get(ctx, client.ObjectKey{Name: ia.Name, Namespace: ia.Namespace}, ic) + if err != nil { + if apierrors.IsNotFound(err) { + continue + } else { + logger.Error(err, "error fetching instrumentation config", "workload", ia.Name) + return ctrl.Result{}, err + } + } + + err := updateInstrumentationConfigForWorkload(ic, &ia, instrumentationRules) + if err != nil { + logger.Error(err, "error updating instrumentation config", "workload", ia.Name) + continue + } + + err = r.Client.Update(ctx, ic) + if client.IgnoreNotFound(err) != nil { + logger.Error(err, "error updating instrumentation config", "workload", ia.Name) + return ctrl.Result{}, err + } + + logger.V(0).Info("Updated instrumentation config", "workload", ia.Name) + } + + logger.V(0).Info("Payload Collection Rules changed, recalculating instrumentation configs", "number of instrumentation rules", len(instrumentationRules.Items), "number of instrumented workloads", len(instrumentedApplications.Items)) + return ctrl.Result{}, nil +} diff --git a/instrumentor/controllers/instrumentationconfig/instrumentedapplication_controller.go b/instrumentor/controllers/instrumentationconfig/instrumentedapplication_controller.go new file mode 100644 index 000000000..c7f8faf05 --- /dev/null +++ b/instrumentor/controllers/instrumentationconfig/instrumentedapplication_controller.go @@ -0,0 +1,73 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package instrumentationconfig + +import ( + "context" + + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + 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" +) + +type InstrumentedApplicationReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + var ia odigosv1.InstrumentedApplication + err := r.Client.Get(ctx, req.NamespacedName, &ia) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + var ic odigosv1.InstrumentationConfig + err = r.Client.Get(ctx, req.NamespacedName, &ic) + if err != nil { + // each InstrumentedApplication should have a corresponding InstrumentationConfig + // but it might rarely happen that the InstrumentationConfig is deleted before the InstrumentedApplication + if apierrors.IsNotFound(err) { + logger.V(0).Info("Ignoring InstrumentedApplication without InstrumentationConfig", "runtime object name", ia.Name) + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + instrumentationRules := &odigosv1.InstrumentationRuleList{} + err = r.Client.List(ctx, instrumentationRules) + + err = updateInstrumentationConfigForWorkload(&ic, &ia, instrumentationRules) + if err != nil { + return ctrl.Result{}, err + } + + err = r.Client.Update(ctx, &ic) + if client.IgnoreNotFound(err) != nil { + logger.Error(err, "error updating instrumentation config", "workload", ia.Name) + return ctrl.Result{}, err + } + + logger.V(0).Info("Updated instrumentation config", "workload", ia.Name) + + return ctrl.Result{}, nil +} diff --git a/instrumentor/controllers/instrumentationconfig/manager.go b/instrumentor/controllers/instrumentationconfig/manager.go new file mode 100644 index 000000000..17b4ba3ec --- /dev/null +++ b/instrumentor/controllers/instrumentationconfig/manager.go @@ -0,0 +1,35 @@ +package instrumentationconfig + +import ( + odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" +) + +func SetupWithManager(mgr ctrl.Manager) error { + err := builder. + ControllerManagedBy(mgr). + Named("instrumentor-instrumentationconfig-instrumentationrule"). + For(&odigosv1alpha1.InstrumentationRule{}). + Complete(&InstrumentationRuleReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) + if err != nil { + return err + } + + err = builder. + ControllerManagedBy(mgr). + Named("instrumentor-instrumentationconfig-instrumentedapplication"). + For(&odigosv1alpha1.InstrumentedApplication{}). + Complete(&InstrumentedApplicationReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) + if err != nil { + return err + } + + return nil +} diff --git a/instrumentor/main.go b/instrumentor/main.go index 5bde75905..f892d1745 100644 --- a/instrumentor/main.go +++ b/instrumentor/main.go @@ -20,6 +20,7 @@ import ( "flag" "os" + "github.com/odigos-io/odigos/instrumentor/controllers/instrumentationconfig" "github.com/odigos-io/odigos/instrumentor/controllers/startlangdetection" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -196,6 +197,12 @@ func main() { os.Exit(1) } + err = instrumentationconfig.SetupWithManager(mgr) + if err != nil { + setupLog.Error(err, "unable to create controller for instrumentation rules") + os.Exit(1) + } + //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/k8sutils/pkg/workload/podworkload.go b/k8sutils/pkg/workload/podworkload.go index 96ace7ab5..57022cad4 100644 --- a/k8sutils/pkg/workload/podworkload.go +++ b/k8sutils/pkg/workload/podworkload.go @@ -7,7 +7,7 @@ package workload // that governs the lifecycle and behavior of a Pod, especially in contexts where // understanding the relationship between a Pod and its controlling workload is crucial. type PodWorkload struct { - Name string - Namespace string - Kind WorkloadKind + Name string `json:"name"` + Namespace string `json:"namespace"` + Kind WorkloadKind `json:"kind"` } diff --git a/scheduler/go.sum b/scheduler/go.sum index a1d3ce890..2ba030a27 100644 --- a/scheduler/go.sum +++ b/scheduler/go.sum @@ -156,6 +156,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=