From 4469aa8cba06e71bed691f741d6ccde21908279f Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 19 Dec 2024 09:44:34 -0500 Subject: [PATCH 001/259] Introduce Instrumentation CRD --- .../crd/bases/odigos.io_instrumentations.yaml | 145 ++++++++++++ .../odigos/v1alpha1/instrumentation.go | 224 ++++++++++++++++++ .../odigos/v1alpha1/instrumentationspec.go | 42 ++++ .../odigos/v1alpha1/instrumentationstatus.go | 47 ++++ .../odigos/applyconfiguration/utils.go | 6 + .../v1alpha1/fake/fake_instrumentation.go | 196 +++++++++++++++ .../v1alpha1/fake/fake_odigos_client.go | 4 + .../odigos/v1alpha1/generated_expansion.go | 2 + .../typed/odigos/v1alpha1/instrumentation.go | 72 ++++++ .../typed/odigos/v1alpha1/odigos_client.go | 5 + .../informers/externalversions/generic.go | 2 + .../odigos/v1alpha1/instrumentation.go | 89 +++++++ .../odigos/v1alpha1/interface.go | 7 + .../odigos/v1alpha1/expansion_generated.go | 8 + .../odigos/v1alpha1/instrumentation.go | 69 ++++++ api/odigos/v1alpha1/instrumentation_types.go | 69 ++++++ api/odigos/v1alpha1/zz_generated.deepcopy.go | 97 ++++++++ .../crds/odigos.io_instrumentations.yaml | 145 ++++++++++++ 18 files changed, 1229 insertions(+) create mode 100644 api/config/crd/bases/odigos.io_instrumentations.yaml create mode 100644 api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go create mode 100644 api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go create mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go create mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go create mode 100644 api/odigos/v1alpha1/instrumentation_types.go create mode 100644 helm/odigos/templates/crds/odigos.io_instrumentations.yaml diff --git a/api/config/crd/bases/odigos.io_instrumentations.yaml b/api/config/crd/bases/odigos.io_instrumentations.yaml new file mode 100644 index 000000000..80c24ac88 --- /dev/null +++ b/api/config/crd/bases/odigos.io_instrumentations.yaml @@ -0,0 +1,145 @@ +--- +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: instrumentations.odigos.io +spec: + group: odigos.io + names: + kind: Instrumentation + listKind: InstrumentationList + plural: instrumentations + singular: instrumentation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.workload.name + name: Workload + type: string + - jsonPath: .spec.workload.kind + name: Kind + type: string + - jsonPath: .spec.workload.namespace + name: Namespace + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Instrumentation configures an application for auto-instrumentation. + 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: + workload: + description: |- + Workload represents the workload or namespace to be instrumented. + This field is required upon creation and cannot be modified. + 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 + required: + - workload + 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/instrumentation.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go new file mode 100644 index 000000000..5e3c2990d --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.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" +) + +// InstrumentationApplyConfiguration represents a declarative configuration of the Instrumentation type for use +// with apply. +type InstrumentationApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *InstrumentationSpecApplyConfiguration `json:"spec,omitempty"` + Status *InstrumentationStatusApplyConfiguration `json:"status,omitempty"` +} + +// Instrumentation constructs a declarative configuration of the Instrumentation type for use with +// apply. +func Instrumentation(name, namespace string) *InstrumentationApplyConfiguration { + b := &InstrumentationApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("Instrumentation") + 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 *InstrumentationApplyConfiguration) WithKind(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithAPIVersion(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithName(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithGenerateName(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithNamespace(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithUID(value types.UID) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithResourceVersion(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithGeneration(value int64) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithCreationTimestamp(value metav1.Time) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithLabels(entries map[string]string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithAnnotations(entries map[string]string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithFinalizers(values ...string) *InstrumentationApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.Finalizers = append(b.Finalizers, values[i]) + } + return b +} + +func (b *InstrumentationApplyConfiguration) 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 *InstrumentationApplyConfiguration) WithSpec(value *InstrumentationSpecApplyConfiguration) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithStatus(value *InstrumentationStatusApplyConfiguration) *InstrumentationApplyConfiguration { + b.Status = value + return b +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *InstrumentationApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.Name +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go new file mode 100644 index 000000000..dab281b1d --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go @@ -0,0 +1,42 @@ +/* +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 ( + workload "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +// InstrumentationSpecApplyConfiguration represents a declarative configuration of the InstrumentationSpec type for use +// with apply. +type InstrumentationSpecApplyConfiguration struct { + Workload *workload.PodWorkload `json:"workload,omitempty"` +} + +// InstrumentationSpecApplyConfiguration constructs a declarative configuration of the InstrumentationSpec type for use with +// apply. +func InstrumentationSpec() *InstrumentationSpecApplyConfiguration { + return &InstrumentationSpecApplyConfiguration{} +} + +// WithWorkload sets the Workload 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 Workload field is set to the value of the last call. +func (b *InstrumentationSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) *InstrumentationSpecApplyConfiguration { + b.Workload = &value + return b +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go new file mode 100644 index 000000000..d3f06e54e --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.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" +) + +// InstrumentationStatusApplyConfiguration represents a declarative configuration of the InstrumentationStatus type for use +// with apply. +type InstrumentationStatusApplyConfiguration struct { + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` +} + +// InstrumentationStatusApplyConfiguration constructs a declarative configuration of the InstrumentationStatus type for use with +// apply. +func InstrumentationStatus() *InstrumentationStatusApplyConfiguration { + return &InstrumentationStatusApplyConfiguration{} +} + +// 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 *InstrumentationStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationStatusApplyConfiguration { + 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/utils.go b/api/generated/odigos/applyconfiguration/utils.go index c4383287b..6b1921839 100644 --- a/api/generated/odigos/applyconfiguration/utils.go +++ b/api/generated/odigos/applyconfiguration/utils.go @@ -59,6 +59,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.EnvVarApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("HeadSamplingConfig"): return &odigosv1alpha1.HeadSamplingConfigApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Instrumentation"): + return &odigosv1alpha1.InstrumentationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationConfig"): return &odigosv1alpha1.InstrumentationConfigApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationConfigSpec"): @@ -91,6 +93,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.InstrumentationRuleSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationRuleStatus"): return &odigosv1alpha1.InstrumentationRuleStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationSpec"): + return &odigosv1alpha1.InstrumentationSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationStatus"): + return &odigosv1alpha1.InstrumentationStatusApplyConfiguration{} 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_instrumentation.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go new file mode 100644 index 000000000..bee026a3e --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.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" +) + +// FakeInstrumentations implements InstrumentationInterface +type FakeInstrumentations struct { + Fake *FakeOdigosV1alpha1 + ns string +} + +var instrumentationsResource = v1alpha1.SchemeGroupVersion.WithResource("instrumentations") + +var instrumentationsKind = v1alpha1.SchemeGroupVersion.WithKind("Instrumentation") + +// Get takes name of the instrumentation, and returns the corresponding instrumentation object, and an error if there is any. +func (c *FakeInstrumentations) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(instrumentationsResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), err +} + +// List takes label and field selectors, and returns the list of Instrumentations that match those selectors. +func (c *FakeInstrumentations) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.InstrumentationList, err error) { + emptyResult := &v1alpha1.InstrumentationList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(instrumentationsResource, instrumentationsKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.InstrumentationList{ListMeta: obj.(*v1alpha1.InstrumentationList).ListMeta} + for _, item := range obj.(*v1alpha1.InstrumentationList).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 instrumentations. +func (c *FakeInstrumentations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(instrumentationsResource, c.ns, opts)) + +} + +// Create takes the representation of a instrumentation and creates it. Returns the server's representation of the instrumentation, and an error, if there is any. +func (c *FakeInstrumentations) Create(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.CreateOptions) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(instrumentationsResource, c.ns, instrumentation, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), err +} + +// Update takes the representation of a instrumentation and updates it. Returns the server's representation of the instrumentation, and an error, if there is any. +func (c *FakeInstrumentations) Update(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(instrumentationsResource, c.ns, instrumentation, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), 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 *FakeInstrumentations) UpdateStatus(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceActionWithOptions(instrumentationsResource, "status", c.ns, instrumentation, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), err +} + +// Delete takes name of the instrumentation and deletes it. Returns an error if one occurs. +func (c *FakeInstrumentations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(instrumentationsResource, c.ns, name, opts), &v1alpha1.Instrumentation{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeInstrumentations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(instrumentationsResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.InstrumentationList{}) + return err +} + +// Patch applies the patch and returns the patched instrumentation. +func (c *FakeInstrumentations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), err +} + +// Apply takes the given apply declarative configuration, applies it and returns the applied instrumentation. +func (c *FakeInstrumentations) Apply(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) { + if instrumentation == nil { + return nil, fmt.Errorf("instrumentation provided to Apply must not be nil") + } + data, err := json.Marshal(instrumentation) + if err != nil { + return nil, err + } + name := instrumentation.Name + if name == nil { + return nil, fmt.Errorf("instrumentation.Name must be provided to Apply") + } + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), 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 *FakeInstrumentations) ApplyStatus(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) { + if instrumentation == nil { + return nil, fmt.Errorf("instrumentation provided to Apply must not be nil") + } + data, err := json.Marshal(instrumentation) + if err != nil { + return nil, err + } + name := instrumentation.Name + if name == nil { + return nil, fmt.Errorf("instrumentation.Name must be provided to Apply") + } + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions(), "status"), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), 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 9f70f41c2..b9449792b 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 @@ -35,6 +35,10 @@ func (c *FakeOdigosV1alpha1) Destinations(namespace string) v1alpha1.Destination return &FakeDestinations{c, namespace} } +func (c *FakeOdigosV1alpha1) Instrumentations(namespace string) v1alpha1.InstrumentationInterface { + return &FakeInstrumentations{c, namespace} +} + func (c *FakeOdigosV1alpha1) InstrumentationConfigs(namespace string) v1alpha1.InstrumentationConfigInterface { return &FakeInstrumentationConfigs{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 a6b52f8da..ff0918d9d 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 @@ -21,6 +21,8 @@ type CollectorsGroupExpansion interface{} type DestinationExpansion interface{} +type InstrumentationExpansion interface{} + type InstrumentationConfigExpansion interface{} type InstrumentationInstanceExpansion interface{} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go new file mode 100644 index 000000000..7a77d533d --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.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" +) + +// InstrumentationsGetter has a method to return a InstrumentationInterface. +// A group's client should implement this interface. +type InstrumentationsGetter interface { + Instrumentations(namespace string) InstrumentationInterface +} + +// InstrumentationInterface has methods to work with Instrumentation resources. +type InstrumentationInterface interface { + Create(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.CreateOptions) (*v1alpha1.Instrumentation, error) + Update(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (*v1alpha1.Instrumentation, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (*v1alpha1.Instrumentation, 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.Instrumentation, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.InstrumentationList, 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.Instrumentation, err error) + Apply(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) + InstrumentationExpansion +} + +// instrumentations implements InstrumentationInterface +type instrumentations struct { + *gentype.ClientWithListAndApply[*v1alpha1.Instrumentation, *v1alpha1.InstrumentationList, *odigosv1alpha1.InstrumentationApplyConfiguration] +} + +// newInstrumentations returns a Instrumentations +func newInstrumentations(c *OdigosV1alpha1Client, namespace string) *instrumentations { + return &instrumentations{ + gentype.NewClientWithListAndApply[*v1alpha1.Instrumentation, *v1alpha1.InstrumentationList, *odigosv1alpha1.InstrumentationApplyConfiguration]( + "instrumentations", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1alpha1.Instrumentation { return &v1alpha1.Instrumentation{} }, + func() *v1alpha1.InstrumentationList { return &v1alpha1.InstrumentationList{} }), + } +} 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 06398b492..dc535257d 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 @@ -29,6 +29,7 @@ type OdigosV1alpha1Interface interface { RESTClient() rest.Interface CollectorsGroupsGetter DestinationsGetter + InstrumentationsGetter InstrumentationConfigsGetter InstrumentationInstancesGetter InstrumentationRulesGetter @@ -50,6 +51,10 @@ func (c *OdigosV1alpha1Client) Destinations(namespace string) DestinationInterfa return newDestinations(c, namespace) } +func (c *OdigosV1alpha1Client) Instrumentations(namespace string) InstrumentationInterface { + return newInstrumentations(c, namespace) +} + func (c *OdigosV1alpha1Client) InstrumentationConfigs(namespace string) InstrumentationConfigInterface { return newInstrumentationConfigs(c, namespace) } diff --git a/api/generated/odigos/informers/externalversions/generic.go b/api/generated/odigos/informers/externalversions/generic.go index 4e3bd2081..f56304bd2 100644 --- a/api/generated/odigos/informers/externalversions/generic.go +++ b/api/generated/odigos/informers/externalversions/generic.go @@ -56,6 +56,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().CollectorsGroups().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("destinations"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Destinations().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("instrumentations"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Instrumentations().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationconfigs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().InstrumentationConfigs().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationinstances"): diff --git a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go new file mode 100644 index 000000000..2b1e116dc --- /dev/null +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.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" +) + +// InstrumentationInformer provides access to a shared informer and lister for +// Instrumentations. +type InstrumentationInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.InstrumentationLister +} + +type instrumentationInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewInstrumentationInformer constructs a new informer for Instrumentation 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 NewInstrumentationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredInstrumentationInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredInstrumentationInformer constructs a new informer for Instrumentation 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 NewFilteredInstrumentationInformer(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().Instrumentations(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OdigosV1alpha1().Instrumentations(namespace).Watch(context.TODO(), options) + }, + }, + &odigosv1alpha1.Instrumentation{}, + resyncPeriod, + indexers, + ) +} + +func (f *instrumentationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredInstrumentationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *instrumentationInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&odigosv1alpha1.Instrumentation{}, f.defaultInformer) +} + +func (f *instrumentationInformer) Lister() v1alpha1.InstrumentationLister { + return v1alpha1.NewInstrumentationLister(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 52778ee8d..f896b99a6 100644 --- a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go @@ -27,6 +27,8 @@ type Interface interface { CollectorsGroups() CollectorsGroupInformer // Destinations returns a DestinationInformer. Destinations() DestinationInformer + // Instrumentations returns a InstrumentationInformer. + Instrumentations() InstrumentationInformer // InstrumentationConfigs returns a InstrumentationConfigInformer. InstrumentationConfigs() InstrumentationConfigInformer // InstrumentationInstances returns a InstrumentationInstanceInformer. @@ -62,6 +64,11 @@ func (v *version) Destinations() DestinationInformer { return &destinationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// Instrumentations returns a InstrumentationInformer. +func (v *version) Instrumentations() InstrumentationInformer { + return &instrumentationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // InstrumentationConfigs returns a InstrumentationConfigInformer. func (v *version) InstrumentationConfigs() InstrumentationConfigInformer { return &instrumentationConfigInformer{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 7adce7617..6bb59254f 100644 --- a/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go +++ b/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go @@ -33,6 +33,14 @@ type DestinationListerExpansion interface{} // DestinationNamespaceLister. type DestinationNamespaceListerExpansion interface{} +// InstrumentationListerExpansion allows custom methods to be added to +// InstrumentationLister. +type InstrumentationListerExpansion interface{} + +// InstrumentationNamespaceListerExpansion allows custom methods to be added to +// InstrumentationNamespaceLister. +type InstrumentationNamespaceListerExpansion interface{} + // InstrumentationConfigListerExpansion allows custom methods to be added to // InstrumentationConfigLister. type InstrumentationConfigListerExpansion interface{} diff --git a/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go new file mode 100644 index 000000000..d537b568c --- /dev/null +++ b/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.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" +) + +// InstrumentationLister helps list Instrumentations. +// All objects returned here must be treated as read-only. +type InstrumentationLister interface { + // List lists all Instrumentations in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Instrumentation, err error) + // Instrumentations returns an object that can list and get Instrumentations. + Instrumentations(namespace string) InstrumentationNamespaceLister + InstrumentationListerExpansion +} + +// instrumentationLister implements the InstrumentationLister interface. +type instrumentationLister struct { + listers.ResourceIndexer[*v1alpha1.Instrumentation] +} + +// NewInstrumentationLister returns a new InstrumentationLister. +func NewInstrumentationLister(indexer cache.Indexer) InstrumentationLister { + return &instrumentationLister{listers.New[*v1alpha1.Instrumentation](indexer, v1alpha1.Resource("instrumentation"))} +} + +// Instrumentations returns an object that can list and get Instrumentations. +func (s *instrumentationLister) Instrumentations(namespace string) InstrumentationNamespaceLister { + return instrumentationNamespaceLister{listers.NewNamespaced[*v1alpha1.Instrumentation](s.ResourceIndexer, namespace)} +} + +// InstrumentationNamespaceLister helps list and get Instrumentations. +// All objects returned here must be treated as read-only. +type InstrumentationNamespaceLister interface { + // List lists all Instrumentations in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Instrumentation, err error) + // Get retrieves the Instrumentation from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Instrumentation, error) + InstrumentationNamespaceListerExpansion +} + +// instrumentationNamespaceLister implements the InstrumentationNamespaceLister +// interface. +type instrumentationNamespaceLister struct { + listers.ResourceIndexer[*v1alpha1.Instrumentation] +} diff --git a/api/odigos/v1alpha1/instrumentation_types.go b/api/odigos/v1alpha1/instrumentation_types.go new file mode 100644 index 000000000..21c70de65 --- /dev/null +++ b/api/odigos/v1alpha1/instrumentation_types.go @@ -0,0 +1,69 @@ +/* +Copyright 2024. + +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 ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +// Instrumentation configures an application for auto-instrumentation. +// +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 +// +kubebuilder:printcolumn:name="Workload",type=string,JSONPath=`.spec.workload.name` +// +kubebuilder:printcolumn:name="Kind",type=string,JSONPath=`.spec.workload.kind` +// +kubebuilder:printcolumn:name="Namespace",type=string,JSONPath=`.spec.workload.namespace` +type Instrumentation struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec InstrumentationSpec `json:"spec,omitempty"` + Status InstrumentationStatus `json:"status,omitempty"` +} + +type InstrumentationSpec struct { + // Workload represents the workload or namespace to be instrumented. + // This field is required upon creation and cannot be modified. + // +kubebuilder:validation:Required + Workload workload.PodWorkload `json:"workload,omitempty"` +} + +type InstrumentationStatus 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"` +} + +//+kubebuilder:object:root=true + +type InstrumentationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Instrumentation `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Instrumentation{}, &InstrumentationList{}) +} diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index bc2dbb747..2a9681af6 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -386,6 +386,33 @@ func (in *HeadSamplingConfig) DeepCopy() *HeadSamplingConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Instrumentation) DeepCopyInto(out *Instrumentation) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Instrumentation. +func (in *Instrumentation) DeepCopy() *Instrumentation { + if in == nil { + return nil + } + out := new(Instrumentation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Instrumentation) 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 *InstrumentationConfig) DeepCopyInto(out *InstrumentationConfig) { *out = *in @@ -750,6 +777,38 @@ 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 *InstrumentationList) DeepCopyInto(out *InstrumentationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Instrumentation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationList. +func (in *InstrumentationList) DeepCopy() *InstrumentationList { + if in == nil { + return nil + } + out := new(InstrumentationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InstrumentationList) 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 *InstrumentationRule) DeepCopyInto(out *InstrumentationRule) { *out = *in @@ -874,6 +933,44 @@ func (in *InstrumentationRuleStatus) DeepCopy() *InstrumentationRuleStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { + *out = *in + out.Workload = in.Workload +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationSpec. +func (in *InstrumentationSpec) DeepCopy() *InstrumentationSpec { + if in == nil { + return nil + } + out := new(InstrumentationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationStatus) DeepCopyInto(out *InstrumentationStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationStatus. +func (in *InstrumentationStatus) DeepCopy() *InstrumentationStatus { + if in == nil { + return nil + } + out := new(InstrumentationStatus) + 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 diff --git a/helm/odigos/templates/crds/odigos.io_instrumentations.yaml b/helm/odigos/templates/crds/odigos.io_instrumentations.yaml new file mode 100644 index 000000000..80c24ac88 --- /dev/null +++ b/helm/odigos/templates/crds/odigos.io_instrumentations.yaml @@ -0,0 +1,145 @@ +--- +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: instrumentations.odigos.io +spec: + group: odigos.io + names: + kind: Instrumentation + listKind: InstrumentationList + plural: instrumentations + singular: instrumentation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.workload.name + name: Workload + type: string + - jsonPath: .spec.workload.kind + name: Kind + type: string + - jsonPath: .spec.workload.namespace + name: Namespace + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Instrumentation configures an application for auto-instrumentation. + 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: + workload: + description: |- + Workload represents the workload or namespace to be instrumented. + This field is required upon creation and cannot be modified. + 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 + required: + - workload + 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: {} From 28acc088a27e871fb4b9c53f226beaf0c09ec7a6 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 09:59:25 -0500 Subject: [PATCH 002/259] Rename to Source, remove omitempty on workload field --- ...mentations.yaml => odigos.io_sources.yaml} | 16 +- .../{instrumentation.go => source.go} | 54 ++--- .../{instrumentationspec.go => sourcespec.go} | 12 +- ...strumentationstatus.go => sourcestatus.go} | 12 +- .../odigos/applyconfiguration/utils.go | 12 +- .../v1alpha1/fake/fake_instrumentation.go | 196 ------------------ .../v1alpha1/fake/fake_odigos_client.go | 8 +- .../typed/odigos/v1alpha1/fake/fake_source.go | 196 ++++++++++++++++++ .../odigos/v1alpha1/generated_expansion.go | 4 +- .../typed/odigos/v1alpha1/instrumentation.go | 72 ------- .../typed/odigos/v1alpha1/odigos_client.go | 10 +- .../versioned/typed/odigos/v1alpha1/source.go | 72 +++++++ .../informers/externalversions/generic.go | 4 +- .../odigos/v1alpha1/interface.go | 14 +- .../{instrumentation.go => source.go} | 38 ++-- .../odigos/v1alpha1/expansion_generated.go | 16 +- .../odigos/v1alpha1/instrumentation.go | 69 ------ .../odigos/listers/odigos/v1alpha1/source.go | 69 ++++++ ...strumentation_types.go => source_types.go} | 22 +- api/odigos/v1alpha1/zz_generated.deepcopy.go | 194 ++++++++--------- ...mentations.yaml => odigos.io_sources.yaml} | 16 +- 21 files changed, 555 insertions(+), 551 deletions(-) rename api/config/crd/bases/{odigos.io_instrumentations.yaml => odigos.io_sources.yaml} (94%) rename api/generated/odigos/applyconfiguration/odigos/v1alpha1/{instrumentation.go => source.go} (74%) rename api/generated/odigos/applyconfiguration/odigos/v1alpha1/{instrumentationspec.go => sourcespec.go} (66%) rename api/generated/odigos/applyconfiguration/odigos/v1alpha1/{instrumentationstatus.go => sourcestatus.go} (68%) delete mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go create mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_source.go delete mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/source.go rename api/generated/odigos/informers/externalversions/odigos/v1alpha1/{instrumentation.go => source.go} (55%) delete mode 100644 api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/listers/odigos/v1alpha1/source.go rename api/odigos/v1alpha1/{instrumentation_types.go => source_types.go} (76%) rename helm/odigos/templates/crds/{odigos.io_instrumentations.yaml => odigos.io_sources.yaml} (94%) diff --git a/api/config/crd/bases/odigos.io_instrumentations.yaml b/api/config/crd/bases/odigos.io_sources.yaml similarity index 94% rename from api/config/crd/bases/odigos.io_instrumentations.yaml rename to api/config/crd/bases/odigos.io_sources.yaml index 80c24ac88..cda0e2b88 100644 --- a/api/config/crd/bases/odigos.io_instrumentations.yaml +++ b/api/config/crd/bases/odigos.io_sources.yaml @@ -7,14 +7,14 @@ metadata: labels: metadata.labels.odigos.io/config: "1" metadata.labels.odigos.io/system-object: "true" - name: instrumentations.odigos.io + name: sources.odigos.io spec: group: odigos.io names: - kind: Instrumentation - listKind: InstrumentationList - plural: instrumentations - singular: instrumentation + kind: Source + listKind: SourceList + plural: sources + singular: source scope: Namespaced versions: - additionalPrinterColumns: @@ -30,7 +30,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: Instrumentation configures an application for auto-instrumentation. + description: Source configures an application for auto-instrumentation. properties: apiVersion: description: |- @@ -77,7 +77,7 @@ spec: properties: conditions: description: |- - Represents the observations of a instrumentationrule's current state. + Represents the observations of a source's current state. Known .status.conditions.type are: "Available", "Progressing" items: description: Condition contains details for one aspect of the current @@ -138,6 +138,8 @@ spec: - type x-kubernetes-list-type: map type: object + required: + - spec type: object served: true storage: true diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/source.go similarity index 74% rename from api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go rename to api/generated/odigos/applyconfiguration/odigos/v1alpha1/source.go index 5e3c2990d..4a706aee6 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/source.go @@ -23,22 +23,22 @@ import ( v1 "k8s.io/client-go/applyconfigurations/meta/v1" ) -// InstrumentationApplyConfiguration represents a declarative configuration of the Instrumentation type for use +// SourceApplyConfiguration represents a declarative configuration of the Source type for use // with apply. -type InstrumentationApplyConfiguration struct { +type SourceApplyConfiguration struct { v1.TypeMetaApplyConfiguration `json:",inline"` *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` - Spec *InstrumentationSpecApplyConfiguration `json:"spec,omitempty"` - Status *InstrumentationStatusApplyConfiguration `json:"status,omitempty"` + Spec *SourceSpecApplyConfiguration `json:"spec,omitempty"` + Status *SourceStatusApplyConfiguration `json:"status,omitempty"` } -// Instrumentation constructs a declarative configuration of the Instrumentation type for use with +// Source constructs a declarative configuration of the Source type for use with // apply. -func Instrumentation(name, namespace string) *InstrumentationApplyConfiguration { - b := &InstrumentationApplyConfiguration{} +func Source(name, namespace string) *SourceApplyConfiguration { + b := &SourceApplyConfiguration{} b.WithName(name) b.WithNamespace(namespace) - b.WithKind("Instrumentation") + b.WithKind("Source") b.WithAPIVersion("odigos.io/v1alpha1") return b } @@ -46,7 +46,7 @@ func Instrumentation(name, namespace string) *InstrumentationApplyConfiguration // 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 *InstrumentationApplyConfiguration) WithKind(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithKind(value string) *SourceApplyConfiguration { b.Kind = &value return b } @@ -54,7 +54,7 @@ func (b *InstrumentationApplyConfiguration) WithKind(value string) *Instrumentat // 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 *InstrumentationApplyConfiguration) WithAPIVersion(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithAPIVersion(value string) *SourceApplyConfiguration { b.APIVersion = &value return b } @@ -62,7 +62,7 @@ func (b *InstrumentationApplyConfiguration) WithAPIVersion(value string) *Instru // 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 *InstrumentationApplyConfiguration) WithName(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithName(value string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.Name = &value return b @@ -71,7 +71,7 @@ func (b *InstrumentationApplyConfiguration) WithName(value string) *Instrumentat // 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 *InstrumentationApplyConfiguration) WithGenerateName(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithGenerateName(value string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.GenerateName = &value return b @@ -80,7 +80,7 @@ func (b *InstrumentationApplyConfiguration) WithGenerateName(value string) *Inst // 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 *InstrumentationApplyConfiguration) WithNamespace(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithNamespace(value string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.Namespace = &value return b @@ -89,7 +89,7 @@ func (b *InstrumentationApplyConfiguration) WithNamespace(value string) *Instrum // 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 *InstrumentationApplyConfiguration) WithUID(value types.UID) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithUID(value types.UID) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.UID = &value return b @@ -98,7 +98,7 @@ func (b *InstrumentationApplyConfiguration) WithUID(value types.UID) *Instrument // 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 *InstrumentationApplyConfiguration) WithResourceVersion(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithResourceVersion(value string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ResourceVersion = &value return b @@ -107,7 +107,7 @@ func (b *InstrumentationApplyConfiguration) WithResourceVersion(value string) *I // 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 *InstrumentationApplyConfiguration) WithGeneration(value int64) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithGeneration(value int64) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.Generation = &value return b @@ -116,7 +116,7 @@ func (b *InstrumentationApplyConfiguration) WithGeneration(value int64) *Instrum // 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 *InstrumentationApplyConfiguration) WithCreationTimestamp(value metav1.Time) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithCreationTimestamp(value metav1.Time) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.CreationTimestamp = &value return b @@ -125,7 +125,7 @@ func (b *InstrumentationApplyConfiguration) WithCreationTimestamp(value metav1.T // 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 *InstrumentationApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.DeletionTimestamp = &value return b @@ -134,7 +134,7 @@ func (b *InstrumentationApplyConfiguration) WithDeletionTimestamp(value metav1.T // 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 *InstrumentationApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.DeletionGracePeriodSeconds = &value return b @@ -144,7 +144,7 @@ func (b *InstrumentationApplyConfiguration) WithDeletionGracePeriodSeconds(value // 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 *InstrumentationApplyConfiguration) WithLabels(entries map[string]string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithLabels(entries map[string]string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() if b.Labels == nil && len(entries) > 0 { b.Labels = make(map[string]string, len(entries)) @@ -159,7 +159,7 @@ func (b *InstrumentationApplyConfiguration) WithLabels(entries map[string]string // 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 *InstrumentationApplyConfiguration) WithAnnotations(entries map[string]string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithAnnotations(entries map[string]string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() if b.Annotations == nil && len(entries) > 0 { b.Annotations = make(map[string]string, len(entries)) @@ -173,7 +173,7 @@ func (b *InstrumentationApplyConfiguration) WithAnnotations(entries map[string]s // 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 *InstrumentationApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() for i := range values { if values[i] == nil { @@ -187,7 +187,7 @@ func (b *InstrumentationApplyConfiguration) WithOwnerReferences(values ...*v1.Ow // 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 *InstrumentationApplyConfiguration) WithFinalizers(values ...string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithFinalizers(values ...string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() for i := range values { b.Finalizers = append(b.Finalizers, values[i]) @@ -195,7 +195,7 @@ func (b *InstrumentationApplyConfiguration) WithFinalizers(values ...string) *In return b } -func (b *InstrumentationApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { +func (b *SourceApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { if b.ObjectMetaApplyConfiguration == nil { b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} } @@ -204,7 +204,7 @@ func (b *InstrumentationApplyConfiguration) ensureObjectMetaApplyConfigurationEx // 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 *InstrumentationApplyConfiguration) WithSpec(value *InstrumentationSpecApplyConfiguration) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithSpec(value *SourceSpecApplyConfiguration) *SourceApplyConfiguration { b.Spec = value return b } @@ -212,13 +212,13 @@ func (b *InstrumentationApplyConfiguration) WithSpec(value *InstrumentationSpecA // 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 *InstrumentationApplyConfiguration) WithStatus(value *InstrumentationStatusApplyConfiguration) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithStatus(value *SourceStatusApplyConfiguration) *SourceApplyConfiguration { b.Status = value return b } // GetName retrieves the value of the Name field in the declarative configuration. -func (b *InstrumentationApplyConfiguration) GetName() *string { +func (b *SourceApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.Name } diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go similarity index 66% rename from api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go rename to api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go index dab281b1d..ae7774ba8 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go @@ -21,22 +21,22 @@ import ( workload "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -// InstrumentationSpecApplyConfiguration represents a declarative configuration of the InstrumentationSpec type for use +// SourceSpecApplyConfiguration represents a declarative configuration of the SourceSpec type for use // with apply. -type InstrumentationSpecApplyConfiguration struct { +type SourceSpecApplyConfiguration struct { Workload *workload.PodWorkload `json:"workload,omitempty"` } -// InstrumentationSpecApplyConfiguration constructs a declarative configuration of the InstrumentationSpec type for use with +// SourceSpecApplyConfiguration constructs a declarative configuration of the SourceSpec type for use with // apply. -func InstrumentationSpec() *InstrumentationSpecApplyConfiguration { - return &InstrumentationSpecApplyConfiguration{} +func SourceSpec() *SourceSpecApplyConfiguration { + return &SourceSpecApplyConfiguration{} } // WithWorkload sets the Workload 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 Workload field is set to the value of the last call. -func (b *InstrumentationSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) *InstrumentationSpecApplyConfiguration { +func (b *SourceSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) *SourceSpecApplyConfiguration { b.Workload = &value return b } diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcestatus.go similarity index 68% rename from api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go rename to api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcestatus.go index d3f06e54e..c188eb75c 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcestatus.go @@ -21,22 +21,22 @@ import ( v1 "k8s.io/client-go/applyconfigurations/meta/v1" ) -// InstrumentationStatusApplyConfiguration represents a declarative configuration of the InstrumentationStatus type for use +// SourceStatusApplyConfiguration represents a declarative configuration of the SourceStatus type for use // with apply. -type InstrumentationStatusApplyConfiguration struct { +type SourceStatusApplyConfiguration struct { Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` } -// InstrumentationStatusApplyConfiguration constructs a declarative configuration of the InstrumentationStatus type for use with +// SourceStatusApplyConfiguration constructs a declarative configuration of the SourceStatus type for use with // apply. -func InstrumentationStatus() *InstrumentationStatusApplyConfiguration { - return &InstrumentationStatusApplyConfiguration{} +func SourceStatus() *SourceStatusApplyConfiguration { + return &SourceStatusApplyConfiguration{} } // 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 *InstrumentationStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationStatusApplyConfiguration { +func (b *SourceStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *SourceStatusApplyConfiguration { for i := range values { if values[i] == nil { panic("nil value passed to WithConditions") diff --git a/api/generated/odigos/applyconfiguration/utils.go b/api/generated/odigos/applyconfiguration/utils.go index 6b1921839..4d87e42fc 100644 --- a/api/generated/odigos/applyconfiguration/utils.go +++ b/api/generated/odigos/applyconfiguration/utils.go @@ -59,8 +59,6 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.EnvVarApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("HeadSamplingConfig"): return &odigosv1alpha1.HeadSamplingConfigApplyConfiguration{} - case v1alpha1.SchemeGroupVersion.WithKind("Instrumentation"): - return &odigosv1alpha1.InstrumentationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationConfig"): return &odigosv1alpha1.InstrumentationConfigApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationConfigSpec"): @@ -93,10 +91,6 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.InstrumentationRuleSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationRuleStatus"): return &odigosv1alpha1.InstrumentationRuleStatusApplyConfiguration{} - case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationSpec"): - return &odigosv1alpha1.InstrumentationSpecApplyConfiguration{} - case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationStatus"): - return &odigosv1alpha1.InstrumentationStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentedApplication"): return &odigosv1alpha1.InstrumentedApplicationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentedApplicationSpec"): @@ -119,6 +113,12 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.RuntimeDetailsByContainerApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SdkConfig"): return &odigosv1alpha1.SdkConfigApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Source"): + return &odigosv1alpha1.SourceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SourceSpec"): + return &odigosv1alpha1.SourceSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SourceStatus"): + return &odigosv1alpha1.SourceStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("WorkloadInstrumentationConfig"): return &odigosv1alpha1.WorkloadInstrumentationConfigApplyConfiguration{} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go deleted file mode 100644 index bee026a3e..000000000 --- a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -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" -) - -// FakeInstrumentations implements InstrumentationInterface -type FakeInstrumentations struct { - Fake *FakeOdigosV1alpha1 - ns string -} - -var instrumentationsResource = v1alpha1.SchemeGroupVersion.WithResource("instrumentations") - -var instrumentationsKind = v1alpha1.SchemeGroupVersion.WithKind("Instrumentation") - -// Get takes name of the instrumentation, and returns the corresponding instrumentation object, and an error if there is any. -func (c *FakeInstrumentations) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewGetActionWithOptions(instrumentationsResource, c.ns, name, options), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), err -} - -// List takes label and field selectors, and returns the list of Instrumentations that match those selectors. -func (c *FakeInstrumentations) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.InstrumentationList, err error) { - emptyResult := &v1alpha1.InstrumentationList{} - obj, err := c.Fake. - Invokes(testing.NewListActionWithOptions(instrumentationsResource, instrumentationsKind, c.ns, opts), emptyResult) - - if obj == nil { - return emptyResult, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.InstrumentationList{ListMeta: obj.(*v1alpha1.InstrumentationList).ListMeta} - for _, item := range obj.(*v1alpha1.InstrumentationList).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 instrumentations. -func (c *FakeInstrumentations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchActionWithOptions(instrumentationsResource, c.ns, opts)) - -} - -// Create takes the representation of a instrumentation and creates it. Returns the server's representation of the instrumentation, and an error, if there is any. -func (c *FakeInstrumentations) Create(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.CreateOptions) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewCreateActionWithOptions(instrumentationsResource, c.ns, instrumentation, opts), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), err -} - -// Update takes the representation of a instrumentation and updates it. Returns the server's representation of the instrumentation, and an error, if there is any. -func (c *FakeInstrumentations) Update(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewUpdateActionWithOptions(instrumentationsResource, c.ns, instrumentation, opts), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), 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 *FakeInstrumentations) UpdateStatus(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceActionWithOptions(instrumentationsResource, "status", c.ns, instrumentation, opts), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), err -} - -// Delete takes name of the instrumentation and deletes it. Returns an error if one occurs. -func (c *FakeInstrumentations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(instrumentationsResource, c.ns, name, opts), &v1alpha1.Instrumentation{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeInstrumentations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionActionWithOptions(instrumentationsResource, c.ns, opts, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.InstrumentationList{}) - return err -} - -// Patch applies the patch and returns the patched instrumentation. -func (c *FakeInstrumentations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, name, pt, data, opts, subresources...), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), err -} - -// Apply takes the given apply declarative configuration, applies it and returns the applied instrumentation. -func (c *FakeInstrumentations) Apply(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) { - if instrumentation == nil { - return nil, fmt.Errorf("instrumentation provided to Apply must not be nil") - } - data, err := json.Marshal(instrumentation) - if err != nil { - return nil, err - } - name := instrumentation.Name - if name == nil { - return nil, fmt.Errorf("instrumentation.Name must be provided to Apply") - } - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), 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 *FakeInstrumentations) ApplyStatus(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) { - if instrumentation == nil { - return nil, fmt.Errorf("instrumentation provided to Apply must not be nil") - } - data, err := json.Marshal(instrumentation) - if err != nil { - return nil, err - } - name := instrumentation.Name - if name == nil { - return nil, fmt.Errorf("instrumentation.Name must be provided to Apply") - } - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions(), "status"), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), 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 b9449792b..37a6bb012 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 @@ -35,10 +35,6 @@ func (c *FakeOdigosV1alpha1) Destinations(namespace string) v1alpha1.Destination return &FakeDestinations{c, namespace} } -func (c *FakeOdigosV1alpha1) Instrumentations(namespace string) v1alpha1.InstrumentationInterface { - return &FakeInstrumentations{c, namespace} -} - func (c *FakeOdigosV1alpha1) InstrumentationConfigs(namespace string) v1alpha1.InstrumentationConfigInterface { return &FakeInstrumentationConfigs{c, namespace} } @@ -63,6 +59,10 @@ func (c *FakeOdigosV1alpha1) Processors(namespace string) v1alpha1.ProcessorInte return &FakeProcessors{c, namespace} } +func (c *FakeOdigosV1alpha1) Sources(namespace string) v1alpha1.SourceInterface { + return &FakeSources{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeOdigosV1alpha1) RESTClient() rest.Interface { diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_source.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_source.go new file mode 100644 index 000000000..58451ba26 --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_source.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" +) + +// FakeSources implements SourceInterface +type FakeSources struct { + Fake *FakeOdigosV1alpha1 + ns string +} + +var sourcesResource = v1alpha1.SchemeGroupVersion.WithResource("sources") + +var sourcesKind = v1alpha1.SchemeGroupVersion.WithKind("Source") + +// Get takes name of the source, and returns the corresponding source object, and an error if there is any. +func (c *FakeSources) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(sourcesResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} + +// List takes label and field selectors, and returns the list of Sources that match those selectors. +func (c *FakeSources) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.SourceList, err error) { + emptyResult := &v1alpha1.SourceList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(sourcesResource, sourcesKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.SourceList{ListMeta: obj.(*v1alpha1.SourceList).ListMeta} + for _, item := range obj.(*v1alpha1.SourceList).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 sources. +func (c *FakeSources) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(sourcesResource, c.ns, opts)) + +} + +// Create takes the representation of a source and creates it. Returns the server's representation of the source, and an error, if there is any. +func (c *FakeSources) Create(ctx context.Context, source *v1alpha1.Source, opts v1.CreateOptions) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(sourcesResource, c.ns, source, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} + +// Update takes the representation of a source and updates it. Returns the server's representation of the source, and an error, if there is any. +func (c *FakeSources) Update(ctx context.Context, source *v1alpha1.Source, opts v1.UpdateOptions) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(sourcesResource, c.ns, source, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), 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 *FakeSources) UpdateStatus(ctx context.Context, source *v1alpha1.Source, opts v1.UpdateOptions) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceActionWithOptions(sourcesResource, "status", c.ns, source, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} + +// Delete takes name of the source and deletes it. Returns an error if one occurs. +func (c *FakeSources) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(sourcesResource, c.ns, name, opts), &v1alpha1.Source{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeSources) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(sourcesResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.SourceList{}) + return err +} + +// Patch applies the patch and returns the patched source. +func (c *FakeSources) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(sourcesResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} + +// Apply takes the given apply declarative configuration, applies it and returns the applied source. +func (c *FakeSources) Apply(ctx context.Context, source *odigosv1alpha1.SourceApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Source, err error) { + if source == nil { + return nil, fmt.Errorf("source provided to Apply must not be nil") + } + data, err := json.Marshal(source) + if err != nil { + return nil, err + } + name := source.Name + if name == nil { + return nil, fmt.Errorf("source.Name must be provided to Apply") + } + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(sourcesResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), 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 *FakeSources) ApplyStatus(ctx context.Context, source *odigosv1alpha1.SourceApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Source, err error) { + if source == nil { + return nil, fmt.Errorf("source provided to Apply must not be nil") + } + data, err := json.Marshal(source) + if err != nil { + return nil, err + } + name := source.Name + if name == nil { + return nil, fmt.Errorf("source.Name must be provided to Apply") + } + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(sourcesResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions(), "status"), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} 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 ff0918d9d..15777d780 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 @@ -21,8 +21,6 @@ type CollectorsGroupExpansion interface{} type DestinationExpansion interface{} -type InstrumentationExpansion interface{} - type InstrumentationConfigExpansion interface{} type InstrumentationInstanceExpansion interface{} @@ -34,3 +32,5 @@ type InstrumentedApplicationExpansion interface{} type OdigosConfigurationExpansion interface{} type ProcessorExpansion interface{} + +type SourceExpansion interface{} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go deleted file mode 100644 index 7a77d533d..000000000 --- a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -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" -) - -// InstrumentationsGetter has a method to return a InstrumentationInterface. -// A group's client should implement this interface. -type InstrumentationsGetter interface { - Instrumentations(namespace string) InstrumentationInterface -} - -// InstrumentationInterface has methods to work with Instrumentation resources. -type InstrumentationInterface interface { - Create(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.CreateOptions) (*v1alpha1.Instrumentation, error) - Update(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (*v1alpha1.Instrumentation, error) - // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - UpdateStatus(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (*v1alpha1.Instrumentation, 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.Instrumentation, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.InstrumentationList, 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.Instrumentation, err error) - Apply(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) - // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). - ApplyStatus(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) - InstrumentationExpansion -} - -// instrumentations implements InstrumentationInterface -type instrumentations struct { - *gentype.ClientWithListAndApply[*v1alpha1.Instrumentation, *v1alpha1.InstrumentationList, *odigosv1alpha1.InstrumentationApplyConfiguration] -} - -// newInstrumentations returns a Instrumentations -func newInstrumentations(c *OdigosV1alpha1Client, namespace string) *instrumentations { - return &instrumentations{ - gentype.NewClientWithListAndApply[*v1alpha1.Instrumentation, *v1alpha1.InstrumentationList, *odigosv1alpha1.InstrumentationApplyConfiguration]( - "instrumentations", - c.RESTClient(), - scheme.ParameterCodec, - namespace, - func() *v1alpha1.Instrumentation { return &v1alpha1.Instrumentation{} }, - func() *v1alpha1.InstrumentationList { return &v1alpha1.InstrumentationList{} }), - } -} 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 dc535257d..6e7b3013c 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 @@ -29,13 +29,13 @@ type OdigosV1alpha1Interface interface { RESTClient() rest.Interface CollectorsGroupsGetter DestinationsGetter - InstrumentationsGetter InstrumentationConfigsGetter InstrumentationInstancesGetter InstrumentationRulesGetter InstrumentedApplicationsGetter OdigosConfigurationsGetter ProcessorsGetter + SourcesGetter } // OdigosV1alpha1Client is used to interact with features provided by the odigos.io group. @@ -51,10 +51,6 @@ func (c *OdigosV1alpha1Client) Destinations(namespace string) DestinationInterfa return newDestinations(c, namespace) } -func (c *OdigosV1alpha1Client) Instrumentations(namespace string) InstrumentationInterface { - return newInstrumentations(c, namespace) -} - func (c *OdigosV1alpha1Client) InstrumentationConfigs(namespace string) InstrumentationConfigInterface { return newInstrumentationConfigs(c, namespace) } @@ -79,6 +75,10 @@ func (c *OdigosV1alpha1Client) Processors(namespace string) ProcessorInterface { return newProcessors(c, namespace) } +func (c *OdigosV1alpha1Client) Sources(namespace string) SourceInterface { + return newSources(c, namespace) +} + // NewForConfig creates a new OdigosV1alpha1Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/source.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/source.go new file mode 100644 index 000000000..3245a184e --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/source.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" +) + +// SourcesGetter has a method to return a SourceInterface. +// A group's client should implement this interface. +type SourcesGetter interface { + Sources(namespace string) SourceInterface +} + +// SourceInterface has methods to work with Source resources. +type SourceInterface interface { + Create(ctx context.Context, source *v1alpha1.Source, opts v1.CreateOptions) (*v1alpha1.Source, error) + Update(ctx context.Context, source *v1alpha1.Source, opts v1.UpdateOptions) (*v1alpha1.Source, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, source *v1alpha1.Source, opts v1.UpdateOptions) (*v1alpha1.Source, 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.Source, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.SourceList, 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.Source, err error) + Apply(ctx context.Context, source *odigosv1alpha1.SourceApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Source, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, source *odigosv1alpha1.SourceApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Source, err error) + SourceExpansion +} + +// sources implements SourceInterface +type sources struct { + *gentype.ClientWithListAndApply[*v1alpha1.Source, *v1alpha1.SourceList, *odigosv1alpha1.SourceApplyConfiguration] +} + +// newSources returns a Sources +func newSources(c *OdigosV1alpha1Client, namespace string) *sources { + return &sources{ + gentype.NewClientWithListAndApply[*v1alpha1.Source, *v1alpha1.SourceList, *odigosv1alpha1.SourceApplyConfiguration]( + "sources", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1alpha1.Source { return &v1alpha1.Source{} }, + func() *v1alpha1.SourceList { return &v1alpha1.SourceList{} }), + } +} diff --git a/api/generated/odigos/informers/externalversions/generic.go b/api/generated/odigos/informers/externalversions/generic.go index f56304bd2..0ce00ff04 100644 --- a/api/generated/odigos/informers/externalversions/generic.go +++ b/api/generated/odigos/informers/externalversions/generic.go @@ -56,8 +56,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().CollectorsGroups().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("destinations"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Destinations().Informer()}, nil - case v1alpha1.SchemeGroupVersion.WithResource("instrumentations"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Instrumentations().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationconfigs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().InstrumentationConfigs().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationinstances"): @@ -70,6 +68,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().OdigosConfigurations().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("processors"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Processors().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("sources"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Sources().Informer()}, nil } diff --git a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go index f896b99a6..160277736 100644 --- a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go @@ -27,8 +27,6 @@ type Interface interface { CollectorsGroups() CollectorsGroupInformer // Destinations returns a DestinationInformer. Destinations() DestinationInformer - // Instrumentations returns a InstrumentationInformer. - Instrumentations() InstrumentationInformer // InstrumentationConfigs returns a InstrumentationConfigInformer. InstrumentationConfigs() InstrumentationConfigInformer // InstrumentationInstances returns a InstrumentationInstanceInformer. @@ -41,6 +39,8 @@ type Interface interface { OdigosConfigurations() OdigosConfigurationInformer // Processors returns a ProcessorInformer. Processors() ProcessorInformer + // Sources returns a SourceInformer. + Sources() SourceInformer } type version struct { @@ -64,11 +64,6 @@ func (v *version) Destinations() DestinationInformer { return &destinationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } -// Instrumentations returns a InstrumentationInformer. -func (v *version) Instrumentations() InstrumentationInformer { - return &instrumentationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - // InstrumentationConfigs returns a InstrumentationConfigInformer. func (v *version) InstrumentationConfigs() InstrumentationConfigInformer { return &instrumentationConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} @@ -98,3 +93,8 @@ func (v *version) OdigosConfigurations() OdigosConfigurationInformer { func (v *version) Processors() ProcessorInformer { return &processorInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// Sources returns a SourceInformer. +func (v *version) Sources() SourceInformer { + return &sourceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/source.go similarity index 55% rename from api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go rename to api/generated/odigos/informers/externalversions/odigos/v1alpha1/source.go index 2b1e116dc..a74144c7b 100644 --- a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/source.go @@ -31,59 +31,59 @@ import ( cache "k8s.io/client-go/tools/cache" ) -// InstrumentationInformer provides access to a shared informer and lister for -// Instrumentations. -type InstrumentationInformer interface { +// SourceInformer provides access to a shared informer and lister for +// Sources. +type SourceInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.InstrumentationLister + Lister() v1alpha1.SourceLister } -type instrumentationInformer struct { +type sourceInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc namespace string } -// NewInstrumentationInformer constructs a new informer for Instrumentation type. +// NewSourceInformer constructs a new informer for Source 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 NewInstrumentationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredInstrumentationInformer(client, namespace, resyncPeriod, indexers, nil) +func NewSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredSourceInformer(client, namespace, resyncPeriod, indexers, nil) } -// NewFilteredInstrumentationInformer constructs a new informer for Instrumentation type. +// NewFilteredSourceInformer constructs a new informer for Source 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 NewFilteredInstrumentationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredSourceInformer(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().Instrumentations(namespace).List(context.TODO(), options) + return client.OdigosV1alpha1().Sources(namespace).List(context.TODO(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.OdigosV1alpha1().Instrumentations(namespace).Watch(context.TODO(), options) + return client.OdigosV1alpha1().Sources(namespace).Watch(context.TODO(), options) }, }, - &odigosv1alpha1.Instrumentation{}, + &odigosv1alpha1.Source{}, resyncPeriod, indexers, ) } -func (f *instrumentationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredInstrumentationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *sourceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredSourceInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *instrumentationInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&odigosv1alpha1.Instrumentation{}, f.defaultInformer) +func (f *sourceInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&odigosv1alpha1.Source{}, f.defaultInformer) } -func (f *instrumentationInformer) Lister() v1alpha1.InstrumentationLister { - return v1alpha1.NewInstrumentationLister(f.Informer().GetIndexer()) +func (f *sourceInformer) Lister() v1alpha1.SourceLister { + return v1alpha1.NewSourceLister(f.Informer().GetIndexer()) } diff --git a/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go b/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go index 6bb59254f..fea348a49 100644 --- a/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go +++ b/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go @@ -33,14 +33,6 @@ type DestinationListerExpansion interface{} // DestinationNamespaceLister. type DestinationNamespaceListerExpansion interface{} -// InstrumentationListerExpansion allows custom methods to be added to -// InstrumentationLister. -type InstrumentationListerExpansion interface{} - -// InstrumentationNamespaceListerExpansion allows custom methods to be added to -// InstrumentationNamespaceLister. -type InstrumentationNamespaceListerExpansion interface{} - // InstrumentationConfigListerExpansion allows custom methods to be added to // InstrumentationConfigLister. type InstrumentationConfigListerExpansion interface{} @@ -88,3 +80,11 @@ type ProcessorListerExpansion interface{} // ProcessorNamespaceListerExpansion allows custom methods to be added to // ProcessorNamespaceLister. type ProcessorNamespaceListerExpansion interface{} + +// SourceListerExpansion allows custom methods to be added to +// SourceLister. +type SourceListerExpansion interface{} + +// SourceNamespaceListerExpansion allows custom methods to be added to +// SourceNamespaceLister. +type SourceNamespaceListerExpansion interface{} diff --git a/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go deleted file mode 100644 index d537b568c..000000000 --- a/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -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" -) - -// InstrumentationLister helps list Instrumentations. -// All objects returned here must be treated as read-only. -type InstrumentationLister interface { - // List lists all Instrumentations in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.Instrumentation, err error) - // Instrumentations returns an object that can list and get Instrumentations. - Instrumentations(namespace string) InstrumentationNamespaceLister - InstrumentationListerExpansion -} - -// instrumentationLister implements the InstrumentationLister interface. -type instrumentationLister struct { - listers.ResourceIndexer[*v1alpha1.Instrumentation] -} - -// NewInstrumentationLister returns a new InstrumentationLister. -func NewInstrumentationLister(indexer cache.Indexer) InstrumentationLister { - return &instrumentationLister{listers.New[*v1alpha1.Instrumentation](indexer, v1alpha1.Resource("instrumentation"))} -} - -// Instrumentations returns an object that can list and get Instrumentations. -func (s *instrumentationLister) Instrumentations(namespace string) InstrumentationNamespaceLister { - return instrumentationNamespaceLister{listers.NewNamespaced[*v1alpha1.Instrumentation](s.ResourceIndexer, namespace)} -} - -// InstrumentationNamespaceLister helps list and get Instrumentations. -// All objects returned here must be treated as read-only. -type InstrumentationNamespaceLister interface { - // List lists all Instrumentations in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.Instrumentation, err error) - // Get retrieves the Instrumentation from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.Instrumentation, error) - InstrumentationNamespaceListerExpansion -} - -// instrumentationNamespaceLister implements the InstrumentationNamespaceLister -// interface. -type instrumentationNamespaceLister struct { - listers.ResourceIndexer[*v1alpha1.Instrumentation] -} diff --git a/api/generated/odigos/listers/odigos/v1alpha1/source.go b/api/generated/odigos/listers/odigos/v1alpha1/source.go new file mode 100644 index 000000000..36dd5f7b3 --- /dev/null +++ b/api/generated/odigos/listers/odigos/v1alpha1/source.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" +) + +// SourceLister helps list Sources. +// All objects returned here must be treated as read-only. +type SourceLister interface { + // List lists all Sources in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Source, err error) + // Sources returns an object that can list and get Sources. + Sources(namespace string) SourceNamespaceLister + SourceListerExpansion +} + +// sourceLister implements the SourceLister interface. +type sourceLister struct { + listers.ResourceIndexer[*v1alpha1.Source] +} + +// NewSourceLister returns a new SourceLister. +func NewSourceLister(indexer cache.Indexer) SourceLister { + return &sourceLister{listers.New[*v1alpha1.Source](indexer, v1alpha1.Resource("source"))} +} + +// Sources returns an object that can list and get Sources. +func (s *sourceLister) Sources(namespace string) SourceNamespaceLister { + return sourceNamespaceLister{listers.NewNamespaced[*v1alpha1.Source](s.ResourceIndexer, namespace)} +} + +// SourceNamespaceLister helps list and get Sources. +// All objects returned here must be treated as read-only. +type SourceNamespaceLister interface { + // List lists all Sources in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Source, err error) + // Get retrieves the Source from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Source, error) + SourceNamespaceListerExpansion +} + +// sourceNamespaceLister implements the SourceNamespaceLister +// interface. +type sourceNamespaceLister struct { + listers.ResourceIndexer[*v1alpha1.Source] +} diff --git a/api/odigos/v1alpha1/instrumentation_types.go b/api/odigos/v1alpha1/source_types.go similarity index 76% rename from api/odigos/v1alpha1/instrumentation_types.go rename to api/odigos/v1alpha1/source_types.go index 21c70de65..10886b2a0 100644 --- a/api/odigos/v1alpha1/instrumentation_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -22,7 +22,7 @@ import ( "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -// Instrumentation configures an application for auto-instrumentation. +// Source configures an application for auto-instrumentation. // +genclient // +kubebuilder:object:root=true // +kubebuilder:subresource:status @@ -31,23 +31,23 @@ import ( // +kubebuilder:printcolumn:name="Workload",type=string,JSONPath=`.spec.workload.name` // +kubebuilder:printcolumn:name="Kind",type=string,JSONPath=`.spec.workload.kind` // +kubebuilder:printcolumn:name="Namespace",type=string,JSONPath=`.spec.workload.namespace` -type Instrumentation struct { +type Source struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec InstrumentationSpec `json:"spec,omitempty"` - Status InstrumentationStatus `json:"status,omitempty"` + Spec SourceSpec `json:"spec"` + Status SourceStatus `json:"status,omitempty"` } -type InstrumentationSpec struct { +type SourceSpec struct { // Workload represents the workload or namespace to be instrumented. // This field is required upon creation and cannot be modified. // +kubebuilder:validation:Required - Workload workload.PodWorkload `json:"workload,omitempty"` + Workload workload.PodWorkload `json:"workload"` } -type InstrumentationStatus struct { - // Represents the observations of a instrumentationrule's current state. +type SourceStatus struct { + // Represents the observations of a source's current state. // Known .status.conditions.type are: "Available", "Progressing" // +patchMergeKey=type // +patchStrategy=merge @@ -58,12 +58,12 @@ type InstrumentationStatus struct { //+kubebuilder:object:root=true -type InstrumentationList struct { +type SourceList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []Instrumentation `json:"items"` + Items []Source `json:"items"` } func init() { - SchemeBuilder.Register(&Instrumentation{}, &InstrumentationList{}) + SchemeBuilder.Register(&Source{}, &SourceList{}) } diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 2a9681af6..96b4a7d74 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -386,33 +386,6 @@ func (in *HeadSamplingConfig) DeepCopy() *HeadSamplingConfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Instrumentation) DeepCopyInto(out *Instrumentation) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Instrumentation. -func (in *Instrumentation) DeepCopy() *Instrumentation { - if in == nil { - return nil - } - out := new(Instrumentation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Instrumentation) 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 *InstrumentationConfig) DeepCopyInto(out *InstrumentationConfig) { *out = *in @@ -777,38 +750,6 @@ 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 *InstrumentationList) DeepCopyInto(out *InstrumentationList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Instrumentation, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationList. -func (in *InstrumentationList) DeepCopy() *InstrumentationList { - if in == nil { - return nil - } - out := new(InstrumentationList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *InstrumentationList) 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 *InstrumentationRule) DeepCopyInto(out *InstrumentationRule) { *out = *in @@ -933,44 +874,6 @@ func (in *InstrumentationRuleStatus) DeepCopy() *InstrumentationRuleStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { - *out = *in - out.Workload = in.Workload -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationSpec. -func (in *InstrumentationSpec) DeepCopy() *InstrumentationSpec { - if in == nil { - return nil - } - out := new(InstrumentationSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InstrumentationStatus) DeepCopyInto(out *InstrumentationStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationStatus. -func (in *InstrumentationStatus) DeepCopy() *InstrumentationStatus { - if in == nil { - return nil - } - out := new(InstrumentationStatus) - 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 @@ -1368,6 +1271,103 @@ func (in *SdkConfig) DeepCopy() *SdkConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Source) DeepCopyInto(out *Source) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Source. +func (in *Source) DeepCopy() *Source { + if in == nil { + return nil + } + out := new(Source) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Source) 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 *SourceList) DeepCopyInto(out *SourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Source, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceList. +func (in *SourceList) DeepCopy() *SourceList { + if in == nil { + return nil + } + out := new(SourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SourceList) 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 *SourceSpec) DeepCopyInto(out *SourceSpec) { + *out = *in + out.Workload = in.Workload +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. +func (in *SourceSpec) DeepCopy() *SourceSpec { + if in == nil { + return nil + } + out := new(SourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceStatus) DeepCopyInto(out *SourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceStatus. +func (in *SourceStatus) DeepCopy() *SourceStatus { + if in == nil { + return nil + } + out := new(SourceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkloadInstrumentationConfig) DeepCopyInto(out *WorkloadInstrumentationConfig) { *out = *in diff --git a/helm/odigos/templates/crds/odigos.io_instrumentations.yaml b/helm/odigos/templates/crds/odigos.io_sources.yaml similarity index 94% rename from helm/odigos/templates/crds/odigos.io_instrumentations.yaml rename to helm/odigos/templates/crds/odigos.io_sources.yaml index 80c24ac88..cda0e2b88 100644 --- a/helm/odigos/templates/crds/odigos.io_instrumentations.yaml +++ b/helm/odigos/templates/crds/odigos.io_sources.yaml @@ -7,14 +7,14 @@ metadata: labels: metadata.labels.odigos.io/config: "1" metadata.labels.odigos.io/system-object: "true" - name: instrumentations.odigos.io + name: sources.odigos.io spec: group: odigos.io names: - kind: Instrumentation - listKind: InstrumentationList - plural: instrumentations - singular: instrumentation + kind: Source + listKind: SourceList + plural: sources + singular: source scope: Namespaced versions: - additionalPrinterColumns: @@ -30,7 +30,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: Instrumentation configures an application for auto-instrumentation. + description: Source configures an application for auto-instrumentation. properties: apiVersion: description: |- @@ -77,7 +77,7 @@ spec: properties: conditions: description: |- - Represents the observations of a instrumentationrule's current state. + Represents the observations of a source's current state. Known .status.conditions.type are: "Available", "Progressing" items: description: Condition contains details for one aspect of the current @@ -138,6 +138,8 @@ spec: - type x-kubernetes-list-type: map type: object + required: + - spec type: object served: true storage: true From 58d208db1484d821b658b25df47cf3b03edc1e82 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:26:27 -0500 Subject: [PATCH 003/259] Add SourceReconciler and create InstrumentationConfig --- .../controllers/startlangdetection/manager.go | 16 +++++++++- .../startlangdetection/source_controller.go | 31 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 instrumentor/controllers/startlangdetection/source_controller.go diff --git a/instrumentor/controllers/startlangdetection/manager.go b/instrumentor/controllers/startlangdetection/manager.go index 2be2646cd..0528db3ae 100644 --- a/instrumentor/controllers/startlangdetection/manager.go +++ b/instrumentor/controllers/startlangdetection/manager.go @@ -1,12 +1,14 @@ package startlangdetection import ( - odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/predicate" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" ) func SetupWithManager(mgr ctrl.Manager) error { @@ -74,5 +76,17 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } + err = builder. + ControllerManagedBy(mgr). + Named("startlangdetection-source"). + For(&v1alpha1.Source{}). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) + if err != nil { + return err + } + return nil } diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go new file mode 100644 index 000000000..b0f907144 --- /dev/null +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -0,0 +1,31 @@ +package startlangdetection + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +type SourceReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (s *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var source v1alpha1.Source + err := s.Get(ctx, req.NamespacedName, &source) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + + err = requestOdigletsToCalculateRuntimeDetails(ctx, s.Client, instConfigName, req.Namespace, obj, s.Scheme) + return ctrl.Result{}, err +} From de6df7685dd9b5e0424c5db7c3a257c6829a3478 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:37:02 -0500 Subject: [PATCH 004/259] Add finalizer for uninstrumentation --- .../startlangdetection/source_controller.go | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index b0f907144..fc742bdaa 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -6,26 +6,55 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) +var sourceFinalizer = "odigos.io/source-finalizer" + type SourceReconciler struct { client.Client Scheme *runtime.Scheme } -func (s *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - var source v1alpha1.Source - err := s.Get(ctx, req.NamespacedName, &source) +func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + source := &v1alpha1.Source{} + err := r.Get(ctx, req.NamespacedName, source) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } - instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // Deleted objects should be filtered in the event filter + return ctrl.Result{}, err + } + + if source.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { + controllerutil.AddFinalizer(source, sourceFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + + instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) + err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) + return ctrl.Result{}, err + } + } else { + // Source is being deleted + if controllerutil.ContainsFinalizer(source, sourceFinalizer) { + // TODO: delete resources + + controllerutil.RemoveFinalizer(source, sourceFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + } + } - err = requestOdigletsToCalculateRuntimeDetails(ctx, s.Client, instConfigName, req.Namespace, obj, s.Scheme) return ctrl.Result{}, err } From db5f24bdb03363b3f44e78697b8ba8585800931b Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:48:46 -0500 Subject: [PATCH 005/259] Delete InstrumentationConfig and remove Finalizer when Source is deleted --- .../startlangdetection/source_controller.go | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index fc742bdaa..8660fb7eb 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -4,6 +4,7 @@ import ( "context" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -29,9 +30,10 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) if err != nil { - // Deleted objects should be filtered in the event filter + // TODO: Deleted objects should be filtered in the event filter return ctrl.Result{}, err } + instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { @@ -40,19 +42,29 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } - instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) return ctrl.Result{}, err } } else { // Source is being deleted if controllerutil.ContainsFinalizer(source, sourceFinalizer) { - // TODO: delete resources - + // Remove the finalizer first, because if the InstrumentationConfig is not found we + // will deadlock on the finalizer never getting removed. + // On the other hand, this could end up deleting a Source with an orphaned InstrumentationConfig. controllerutil.RemoveFinalizer(source, sourceFinalizer) if err := r.Update(ctx, source); err != nil { return ctrl.Result{}, err } + + instConfig := &v1alpha1.InstrumentationConfig{} + err = r.Client.Get(ctx, types.NamespacedName{Name: instConfigName, Namespace: req.Namespace}, instConfig) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + err = r.Client.Delete(ctx, instConfig) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } } } From 8d013b7c35b3be17f74fc5226ea121a23b4e2f84 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:52:32 -0500 Subject: [PATCH 006/259] Add Sources to Instrumentor ClusterRole --- cli/cmd/resources/instrumentor.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cli/cmd/resources/instrumentor.go b/cli/cmd/resources/instrumentor.go index e2e972306..e107c86c2 100644 --- a/cli/cmd/resources/instrumentor.go +++ b/cli/cmd/resources/instrumentor.go @@ -193,6 +193,16 @@ func NewInstrumentorClusterRole() *rbacv1.ClusterRole { Resources: []string{"instrumentationrules"}, Verbs: []string{"get", "list", "watch"}, }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources"}, + Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, + }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources/finalizers"}, + Verbs: []string{"update"}, + }, }, } } From 623f7d35021bbff4db79931457ecd589ad1dc321 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 14:11:24 -0500 Subject: [PATCH 007/259] Add workload labels to Source --- .../startlangdetection/source_controller.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 8660fb7eb..2b3f39ba7 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -13,7 +13,13 @@ import ( "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -var sourceFinalizer = "odigos.io/source-finalizer" +var ( + sourceFinalizer = "odigos.io/source-finalizer" + + workloadNameLabel = "odigos.io/workload-name" + workloadNamespaceLabel = "odigos.io/workload-namespace" + workloadKindLabel = "odigos.io/workload-kind" +) type SourceReconciler struct { client.Client @@ -38,6 +44,14 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { controllerutil.AddFinalizer(source, sourceFinalizer) + + if source.Labels == nil { + source.Labels = make(map[string]string) + } + source.Labels[workloadNameLabel] = source.Spec.Workload.Name + source.Labels[workloadNamespaceLabel] = source.Spec.Workload.Namespace + source.Labels[workloadKindLabel] = string(source.Spec.Workload.Kind) + if err := r.Update(ctx, source); err != nil { return ctrl.Result{}, err } From 32170a50d115c441f9fe59c915c4396fb90e39d2 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:04:14 -0500 Subject: [PATCH 008/259] Update Uninstall command to delete Source finalizers --- Makefile | 7 ++++++- cli/cmd/uninstall.go | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 06f564c9d..67d0afb4b 100644 --- a/Makefile +++ b/Makefile @@ -200,6 +200,11 @@ cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) +.PHONY: cli-uninstall +cli-uninstall: + @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" + cd ./cli ; go run -tags=embed_manifests . uninstall + .PHONY: cli-upgrade cli-upgrade: @echo "Upgrading odigos from source. version: $(ODIGOS_CLI_VERSION)" @@ -255,4 +260,4 @@ dev-nop-destination: .PHONY: dev-add-backpressue-destination dev-backpressue-destination: - kubectl apply -f ./tests/backpressure-exporter.yaml \ No newline at end of file + kubectl apply -f ./tests/backpressure-exporter.yaml diff --git a/cli/cmd/uninstall.go b/cli/cmd/uninstall.go index c7998609f..608fa981f 100644 --- a/cli/cmd/uninstall.go +++ b/cli/cmd/uninstall.go @@ -387,6 +387,20 @@ func uninstallCRDs(ctx context.Context, cmd *cobra.Command, client *kube.Client, return err } + // Clear finalizers from Source objects so they can be uninstalled + sources, err := client.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) + for _, i := range sources.Items { + source, err := client.OdigosClient.Sources(i.Namespace).Get(ctx, i.Name, metav1.GetOptions{}) + if err != nil { + return err + } + source.SetFinalizers([]string{}) + _, err = client.OdigosClient.Sources(i.Namespace).Update(ctx, source, metav1.UpdateOptions{}) + if err != nil { + return err + } + } + for _, i := range list.Items { err = client.ApiExtensions.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, i.Name, metav1.DeleteOptions{}) if err != nil { From 0172989be9140cc6c63bafec1a29d67bc5e47c71 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:25:29 -0500 Subject: [PATCH 009/259] Add logger to SourceReconciler --- .../controllers/startlangdetection/source_controller.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 2b3f39ba7..fbc7be22f 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -8,6 +8,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -27,6 +28,8 @@ type SourceReconciler struct { } func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling Source object", "name", req.Name, "namespace", req.Namespace) source := &v1alpha1.Source{} err := r.Get(ctx, req.NamespacedName, source) if err != nil { From bf67a307a3a85f25aa30b4ec2e3d754680b65c97 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:42:26 -0500 Subject: [PATCH 010/259] Add Source checks to Instrumentor --- .../deleteinstrumentedapplication/common.go | 17 ++++++++++++- .../instrumentedapplication_controller.go | 24 +++++++++++++++---- .../workload_controllers.go | 17 ++++++++++++- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/instrumentor/controllers/deleteinstrumentedapplication/common.go b/instrumentor/controllers/deleteinstrumentedapplication/common.go index d70b9c2f8..9bfe973c6 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/common.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/common.go @@ -7,6 +7,7 @@ import ( "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -21,7 +22,21 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work } if instEffectiveEnabled { - return nil + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadObject.GetName(), + "odigos.io/workload-namespace": workloadObject.GetNamespace(), + "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, + }) + err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return err + } + if len(sourceList.Items) == 0 { + return nil + } } if err := deleteWorkloadInstrumentedApplication(ctx, kubeClient, workloadObject); err != nil { diff --git a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go index a3c504cca..71d1b65c1 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go @@ -20,11 +20,12 @@ import ( "context" "fmt" + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -90,9 +91,24 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c } if !instEffectiveEnabled { - logger.Info("Deleting instrumented application for non-enabled workload") - err := r.Client.Delete(ctx, &instrumentedApplication) - return ctrl.Result{}, client.IgnoreNotFound(err) + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadObject.GetName(), + "odigos.io/workload-namespace": workloadObject.GetNamespace(), + "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, + }) + err := r.Client.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return ctrl.Result{}, err + } + if len(sourceList.Items) == 0 { + logger.Info("Deleting instrumented application for non-enabled workload") + err := r.Client.Delete(ctx, &instrumentedApplication) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + } return ctrl.Result{}, nil diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index b8085d4dc..a197780b0 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -4,6 +4,7 @@ import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/yaml" "k8s.io/apimachinery/pkg/runtime" @@ -59,7 +60,21 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor } if !instrumented { - return ctrl.Result{}, nil + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": obj.GetName(), + "odigos.io/workload-namespace": obj.GetNamespace(), + "odigos.io/workload-kind": obj.GetObjectKind().GroupVersionKind().Kind, + }) + err := k8sClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return ctrl.Result{}, err + } + if len(sourceList.Items) == 0 { + return ctrl.Result{}, nil + } } err = requestOdigletsToCalculateRuntimeDetails(ctx, k8sClient, instConfigName, req.Namespace, obj, scheme) From 847c9c1c8cdbf3cd4703a00336e1c0187df2d3f2 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 19 Dec 2024 09:44:34 -0500 Subject: [PATCH 011/259] Introduce Instrumentation CRD --- .../crd/bases/odigos.io_instrumentations.yaml | 145 ++++++++++++ .../odigos/v1alpha1/instrumentation.go | 224 ++++++++++++++++++ .../odigos/v1alpha1/instrumentationspec.go | 42 ++++ .../odigos/v1alpha1/instrumentationstatus.go | 47 ++++ .../odigos/applyconfiguration/utils.go | 6 + .../v1alpha1/fake/fake_instrumentation.go | 196 +++++++++++++++ .../v1alpha1/fake/fake_odigos_client.go | 4 + .../odigos/v1alpha1/generated_expansion.go | 2 + .../typed/odigos/v1alpha1/instrumentation.go | 72 ++++++ .../typed/odigos/v1alpha1/odigos_client.go | 5 + .../informers/externalversions/generic.go | 2 + .../odigos/v1alpha1/instrumentation.go | 89 +++++++ .../odigos/v1alpha1/interface.go | 7 + .../odigos/v1alpha1/expansion_generated.go | 8 + .../odigos/v1alpha1/instrumentation.go | 69 ++++++ api/odigos/v1alpha1/instrumentation_types.go | 69 ++++++ api/odigos/v1alpha1/zz_generated.deepcopy.go | 97 ++++++++ .../crds/odigos.io_instrumentations.yaml | 145 ++++++++++++ 18 files changed, 1229 insertions(+) create mode 100644 api/config/crd/bases/odigos.io_instrumentations.yaml create mode 100644 api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go create mode 100644 api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go create mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go create mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go create mode 100644 api/odigos/v1alpha1/instrumentation_types.go create mode 100644 helm/odigos/templates/crds/odigos.io_instrumentations.yaml diff --git a/api/config/crd/bases/odigos.io_instrumentations.yaml b/api/config/crd/bases/odigos.io_instrumentations.yaml new file mode 100644 index 000000000..80c24ac88 --- /dev/null +++ b/api/config/crd/bases/odigos.io_instrumentations.yaml @@ -0,0 +1,145 @@ +--- +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: instrumentations.odigos.io +spec: + group: odigos.io + names: + kind: Instrumentation + listKind: InstrumentationList + plural: instrumentations + singular: instrumentation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.workload.name + name: Workload + type: string + - jsonPath: .spec.workload.kind + name: Kind + type: string + - jsonPath: .spec.workload.namespace + name: Namespace + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Instrumentation configures an application for auto-instrumentation. + 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: + workload: + description: |- + Workload represents the workload or namespace to be instrumented. + This field is required upon creation and cannot be modified. + 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 + required: + - workload + 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/instrumentation.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go new file mode 100644 index 000000000..5e3c2990d --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.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" +) + +// InstrumentationApplyConfiguration represents a declarative configuration of the Instrumentation type for use +// with apply. +type InstrumentationApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *InstrumentationSpecApplyConfiguration `json:"spec,omitempty"` + Status *InstrumentationStatusApplyConfiguration `json:"status,omitempty"` +} + +// Instrumentation constructs a declarative configuration of the Instrumentation type for use with +// apply. +func Instrumentation(name, namespace string) *InstrumentationApplyConfiguration { + b := &InstrumentationApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("Instrumentation") + 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 *InstrumentationApplyConfiguration) WithKind(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithAPIVersion(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithName(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithGenerateName(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithNamespace(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithUID(value types.UID) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithResourceVersion(value string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithGeneration(value int64) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithCreationTimestamp(value metav1.Time) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithLabels(entries map[string]string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithAnnotations(entries map[string]string) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithFinalizers(values ...string) *InstrumentationApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.Finalizers = append(b.Finalizers, values[i]) + } + return b +} + +func (b *InstrumentationApplyConfiguration) 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 *InstrumentationApplyConfiguration) WithSpec(value *InstrumentationSpecApplyConfiguration) *InstrumentationApplyConfiguration { + 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 *InstrumentationApplyConfiguration) WithStatus(value *InstrumentationStatusApplyConfiguration) *InstrumentationApplyConfiguration { + b.Status = value + return b +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *InstrumentationApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.Name +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go new file mode 100644 index 000000000..dab281b1d --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go @@ -0,0 +1,42 @@ +/* +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 ( + workload "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +// InstrumentationSpecApplyConfiguration represents a declarative configuration of the InstrumentationSpec type for use +// with apply. +type InstrumentationSpecApplyConfiguration struct { + Workload *workload.PodWorkload `json:"workload,omitempty"` +} + +// InstrumentationSpecApplyConfiguration constructs a declarative configuration of the InstrumentationSpec type for use with +// apply. +func InstrumentationSpec() *InstrumentationSpecApplyConfiguration { + return &InstrumentationSpecApplyConfiguration{} +} + +// WithWorkload sets the Workload 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 Workload field is set to the value of the last call. +func (b *InstrumentationSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) *InstrumentationSpecApplyConfiguration { + b.Workload = &value + return b +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go new file mode 100644 index 000000000..d3f06e54e --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.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" +) + +// InstrumentationStatusApplyConfiguration represents a declarative configuration of the InstrumentationStatus type for use +// with apply. +type InstrumentationStatusApplyConfiguration struct { + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` +} + +// InstrumentationStatusApplyConfiguration constructs a declarative configuration of the InstrumentationStatus type for use with +// apply. +func InstrumentationStatus() *InstrumentationStatusApplyConfiguration { + return &InstrumentationStatusApplyConfiguration{} +} + +// 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 *InstrumentationStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationStatusApplyConfiguration { + 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/utils.go b/api/generated/odigos/applyconfiguration/utils.go index c4383287b..6b1921839 100644 --- a/api/generated/odigos/applyconfiguration/utils.go +++ b/api/generated/odigos/applyconfiguration/utils.go @@ -59,6 +59,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.EnvVarApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("HeadSamplingConfig"): return &odigosv1alpha1.HeadSamplingConfigApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Instrumentation"): + return &odigosv1alpha1.InstrumentationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationConfig"): return &odigosv1alpha1.InstrumentationConfigApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationConfigSpec"): @@ -91,6 +93,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.InstrumentationRuleSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationRuleStatus"): return &odigosv1alpha1.InstrumentationRuleStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationSpec"): + return &odigosv1alpha1.InstrumentationSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationStatus"): + return &odigosv1alpha1.InstrumentationStatusApplyConfiguration{} 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_instrumentation.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go new file mode 100644 index 000000000..bee026a3e --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.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" +) + +// FakeInstrumentations implements InstrumentationInterface +type FakeInstrumentations struct { + Fake *FakeOdigosV1alpha1 + ns string +} + +var instrumentationsResource = v1alpha1.SchemeGroupVersion.WithResource("instrumentations") + +var instrumentationsKind = v1alpha1.SchemeGroupVersion.WithKind("Instrumentation") + +// Get takes name of the instrumentation, and returns the corresponding instrumentation object, and an error if there is any. +func (c *FakeInstrumentations) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(instrumentationsResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), err +} + +// List takes label and field selectors, and returns the list of Instrumentations that match those selectors. +func (c *FakeInstrumentations) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.InstrumentationList, err error) { + emptyResult := &v1alpha1.InstrumentationList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(instrumentationsResource, instrumentationsKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.InstrumentationList{ListMeta: obj.(*v1alpha1.InstrumentationList).ListMeta} + for _, item := range obj.(*v1alpha1.InstrumentationList).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 instrumentations. +func (c *FakeInstrumentations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(instrumentationsResource, c.ns, opts)) + +} + +// Create takes the representation of a instrumentation and creates it. Returns the server's representation of the instrumentation, and an error, if there is any. +func (c *FakeInstrumentations) Create(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.CreateOptions) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(instrumentationsResource, c.ns, instrumentation, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), err +} + +// Update takes the representation of a instrumentation and updates it. Returns the server's representation of the instrumentation, and an error, if there is any. +func (c *FakeInstrumentations) Update(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(instrumentationsResource, c.ns, instrumentation, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), 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 *FakeInstrumentations) UpdateStatus(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceActionWithOptions(instrumentationsResource, "status", c.ns, instrumentation, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), err +} + +// Delete takes name of the instrumentation and deletes it. Returns an error if one occurs. +func (c *FakeInstrumentations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(instrumentationsResource, c.ns, name, opts), &v1alpha1.Instrumentation{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeInstrumentations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(instrumentationsResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.InstrumentationList{}) + return err +} + +// Patch applies the patch and returns the patched instrumentation. +func (c *FakeInstrumentations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Instrumentation, err error) { + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), err +} + +// Apply takes the given apply declarative configuration, applies it and returns the applied instrumentation. +func (c *FakeInstrumentations) Apply(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) { + if instrumentation == nil { + return nil, fmt.Errorf("instrumentation provided to Apply must not be nil") + } + data, err := json.Marshal(instrumentation) + if err != nil { + return nil, err + } + name := instrumentation.Name + if name == nil { + return nil, fmt.Errorf("instrumentation.Name must be provided to Apply") + } + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), 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 *FakeInstrumentations) ApplyStatus(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) { + if instrumentation == nil { + return nil, fmt.Errorf("instrumentation provided to Apply must not be nil") + } + data, err := json.Marshal(instrumentation) + if err != nil { + return nil, err + } + name := instrumentation.Name + if name == nil { + return nil, fmt.Errorf("instrumentation.Name must be provided to Apply") + } + emptyResult := &v1alpha1.Instrumentation{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions(), "status"), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Instrumentation), 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 9f70f41c2..b9449792b 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 @@ -35,6 +35,10 @@ func (c *FakeOdigosV1alpha1) Destinations(namespace string) v1alpha1.Destination return &FakeDestinations{c, namespace} } +func (c *FakeOdigosV1alpha1) Instrumentations(namespace string) v1alpha1.InstrumentationInterface { + return &FakeInstrumentations{c, namespace} +} + func (c *FakeOdigosV1alpha1) InstrumentationConfigs(namespace string) v1alpha1.InstrumentationConfigInterface { return &FakeInstrumentationConfigs{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 a6b52f8da..ff0918d9d 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 @@ -21,6 +21,8 @@ type CollectorsGroupExpansion interface{} type DestinationExpansion interface{} +type InstrumentationExpansion interface{} + type InstrumentationConfigExpansion interface{} type InstrumentationInstanceExpansion interface{} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go new file mode 100644 index 000000000..7a77d533d --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.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" +) + +// InstrumentationsGetter has a method to return a InstrumentationInterface. +// A group's client should implement this interface. +type InstrumentationsGetter interface { + Instrumentations(namespace string) InstrumentationInterface +} + +// InstrumentationInterface has methods to work with Instrumentation resources. +type InstrumentationInterface interface { + Create(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.CreateOptions) (*v1alpha1.Instrumentation, error) + Update(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (*v1alpha1.Instrumentation, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (*v1alpha1.Instrumentation, 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.Instrumentation, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.InstrumentationList, 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.Instrumentation, err error) + Apply(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) + InstrumentationExpansion +} + +// instrumentations implements InstrumentationInterface +type instrumentations struct { + *gentype.ClientWithListAndApply[*v1alpha1.Instrumentation, *v1alpha1.InstrumentationList, *odigosv1alpha1.InstrumentationApplyConfiguration] +} + +// newInstrumentations returns a Instrumentations +func newInstrumentations(c *OdigosV1alpha1Client, namespace string) *instrumentations { + return &instrumentations{ + gentype.NewClientWithListAndApply[*v1alpha1.Instrumentation, *v1alpha1.InstrumentationList, *odigosv1alpha1.InstrumentationApplyConfiguration]( + "instrumentations", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1alpha1.Instrumentation { return &v1alpha1.Instrumentation{} }, + func() *v1alpha1.InstrumentationList { return &v1alpha1.InstrumentationList{} }), + } +} 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 06398b492..dc535257d 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 @@ -29,6 +29,7 @@ type OdigosV1alpha1Interface interface { RESTClient() rest.Interface CollectorsGroupsGetter DestinationsGetter + InstrumentationsGetter InstrumentationConfigsGetter InstrumentationInstancesGetter InstrumentationRulesGetter @@ -50,6 +51,10 @@ func (c *OdigosV1alpha1Client) Destinations(namespace string) DestinationInterfa return newDestinations(c, namespace) } +func (c *OdigosV1alpha1Client) Instrumentations(namespace string) InstrumentationInterface { + return newInstrumentations(c, namespace) +} + func (c *OdigosV1alpha1Client) InstrumentationConfigs(namespace string) InstrumentationConfigInterface { return newInstrumentationConfigs(c, namespace) } diff --git a/api/generated/odigos/informers/externalversions/generic.go b/api/generated/odigos/informers/externalversions/generic.go index 4e3bd2081..f56304bd2 100644 --- a/api/generated/odigos/informers/externalversions/generic.go +++ b/api/generated/odigos/informers/externalversions/generic.go @@ -56,6 +56,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().CollectorsGroups().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("destinations"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Destinations().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("instrumentations"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Instrumentations().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationconfigs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().InstrumentationConfigs().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationinstances"): diff --git a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go new file mode 100644 index 000000000..2b1e116dc --- /dev/null +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.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" +) + +// InstrumentationInformer provides access to a shared informer and lister for +// Instrumentations. +type InstrumentationInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.InstrumentationLister +} + +type instrumentationInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewInstrumentationInformer constructs a new informer for Instrumentation 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 NewInstrumentationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredInstrumentationInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredInstrumentationInformer constructs a new informer for Instrumentation 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 NewFilteredInstrumentationInformer(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().Instrumentations(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OdigosV1alpha1().Instrumentations(namespace).Watch(context.TODO(), options) + }, + }, + &odigosv1alpha1.Instrumentation{}, + resyncPeriod, + indexers, + ) +} + +func (f *instrumentationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredInstrumentationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *instrumentationInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&odigosv1alpha1.Instrumentation{}, f.defaultInformer) +} + +func (f *instrumentationInformer) Lister() v1alpha1.InstrumentationLister { + return v1alpha1.NewInstrumentationLister(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 52778ee8d..f896b99a6 100644 --- a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go @@ -27,6 +27,8 @@ type Interface interface { CollectorsGroups() CollectorsGroupInformer // Destinations returns a DestinationInformer. Destinations() DestinationInformer + // Instrumentations returns a InstrumentationInformer. + Instrumentations() InstrumentationInformer // InstrumentationConfigs returns a InstrumentationConfigInformer. InstrumentationConfigs() InstrumentationConfigInformer // InstrumentationInstances returns a InstrumentationInstanceInformer. @@ -62,6 +64,11 @@ func (v *version) Destinations() DestinationInformer { return &destinationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// Instrumentations returns a InstrumentationInformer. +func (v *version) Instrumentations() InstrumentationInformer { + return &instrumentationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // InstrumentationConfigs returns a InstrumentationConfigInformer. func (v *version) InstrumentationConfigs() InstrumentationConfigInformer { return &instrumentationConfigInformer{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 7adce7617..6bb59254f 100644 --- a/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go +++ b/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go @@ -33,6 +33,14 @@ type DestinationListerExpansion interface{} // DestinationNamespaceLister. type DestinationNamespaceListerExpansion interface{} +// InstrumentationListerExpansion allows custom methods to be added to +// InstrumentationLister. +type InstrumentationListerExpansion interface{} + +// InstrumentationNamespaceListerExpansion allows custom methods to be added to +// InstrumentationNamespaceLister. +type InstrumentationNamespaceListerExpansion interface{} + // InstrumentationConfigListerExpansion allows custom methods to be added to // InstrumentationConfigLister. type InstrumentationConfigListerExpansion interface{} diff --git a/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go new file mode 100644 index 000000000..d537b568c --- /dev/null +++ b/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.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" +) + +// InstrumentationLister helps list Instrumentations. +// All objects returned here must be treated as read-only. +type InstrumentationLister interface { + // List lists all Instrumentations in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Instrumentation, err error) + // Instrumentations returns an object that can list and get Instrumentations. + Instrumentations(namespace string) InstrumentationNamespaceLister + InstrumentationListerExpansion +} + +// instrumentationLister implements the InstrumentationLister interface. +type instrumentationLister struct { + listers.ResourceIndexer[*v1alpha1.Instrumentation] +} + +// NewInstrumentationLister returns a new InstrumentationLister. +func NewInstrumentationLister(indexer cache.Indexer) InstrumentationLister { + return &instrumentationLister{listers.New[*v1alpha1.Instrumentation](indexer, v1alpha1.Resource("instrumentation"))} +} + +// Instrumentations returns an object that can list and get Instrumentations. +func (s *instrumentationLister) Instrumentations(namespace string) InstrumentationNamespaceLister { + return instrumentationNamespaceLister{listers.NewNamespaced[*v1alpha1.Instrumentation](s.ResourceIndexer, namespace)} +} + +// InstrumentationNamespaceLister helps list and get Instrumentations. +// All objects returned here must be treated as read-only. +type InstrumentationNamespaceLister interface { + // List lists all Instrumentations in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Instrumentation, err error) + // Get retrieves the Instrumentation from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Instrumentation, error) + InstrumentationNamespaceListerExpansion +} + +// instrumentationNamespaceLister implements the InstrumentationNamespaceLister +// interface. +type instrumentationNamespaceLister struct { + listers.ResourceIndexer[*v1alpha1.Instrumentation] +} diff --git a/api/odigos/v1alpha1/instrumentation_types.go b/api/odigos/v1alpha1/instrumentation_types.go new file mode 100644 index 000000000..21c70de65 --- /dev/null +++ b/api/odigos/v1alpha1/instrumentation_types.go @@ -0,0 +1,69 @@ +/* +Copyright 2024. + +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 ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +// Instrumentation configures an application for auto-instrumentation. +// +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 +// +kubebuilder:printcolumn:name="Workload",type=string,JSONPath=`.spec.workload.name` +// +kubebuilder:printcolumn:name="Kind",type=string,JSONPath=`.spec.workload.kind` +// +kubebuilder:printcolumn:name="Namespace",type=string,JSONPath=`.spec.workload.namespace` +type Instrumentation struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec InstrumentationSpec `json:"spec,omitempty"` + Status InstrumentationStatus `json:"status,omitempty"` +} + +type InstrumentationSpec struct { + // Workload represents the workload or namespace to be instrumented. + // This field is required upon creation and cannot be modified. + // +kubebuilder:validation:Required + Workload workload.PodWorkload `json:"workload,omitempty"` +} + +type InstrumentationStatus 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"` +} + +//+kubebuilder:object:root=true + +type InstrumentationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Instrumentation `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Instrumentation{}, &InstrumentationList{}) +} diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index bc2dbb747..2a9681af6 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -386,6 +386,33 @@ func (in *HeadSamplingConfig) DeepCopy() *HeadSamplingConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Instrumentation) DeepCopyInto(out *Instrumentation) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Instrumentation. +func (in *Instrumentation) DeepCopy() *Instrumentation { + if in == nil { + return nil + } + out := new(Instrumentation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Instrumentation) 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 *InstrumentationConfig) DeepCopyInto(out *InstrumentationConfig) { *out = *in @@ -750,6 +777,38 @@ 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 *InstrumentationList) DeepCopyInto(out *InstrumentationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Instrumentation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationList. +func (in *InstrumentationList) DeepCopy() *InstrumentationList { + if in == nil { + return nil + } + out := new(InstrumentationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InstrumentationList) 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 *InstrumentationRule) DeepCopyInto(out *InstrumentationRule) { *out = *in @@ -874,6 +933,44 @@ func (in *InstrumentationRuleStatus) DeepCopy() *InstrumentationRuleStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { + *out = *in + out.Workload = in.Workload +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationSpec. +func (in *InstrumentationSpec) DeepCopy() *InstrumentationSpec { + if in == nil { + return nil + } + out := new(InstrumentationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationStatus) DeepCopyInto(out *InstrumentationStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationStatus. +func (in *InstrumentationStatus) DeepCopy() *InstrumentationStatus { + if in == nil { + return nil + } + out := new(InstrumentationStatus) + 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 diff --git a/helm/odigos/templates/crds/odigos.io_instrumentations.yaml b/helm/odigos/templates/crds/odigos.io_instrumentations.yaml new file mode 100644 index 000000000..80c24ac88 --- /dev/null +++ b/helm/odigos/templates/crds/odigos.io_instrumentations.yaml @@ -0,0 +1,145 @@ +--- +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: instrumentations.odigos.io +spec: + group: odigos.io + names: + kind: Instrumentation + listKind: InstrumentationList + plural: instrumentations + singular: instrumentation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.workload.name + name: Workload + type: string + - jsonPath: .spec.workload.kind + name: Kind + type: string + - jsonPath: .spec.workload.namespace + name: Namespace + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Instrumentation configures an application for auto-instrumentation. + 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: + workload: + description: |- + Workload represents the workload or namespace to be instrumented. + This field is required upon creation and cannot be modified. + 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 + required: + - workload + 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: {} From b82625ac58651e1b43a5674d150b510a01638bdf Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 09:59:25 -0500 Subject: [PATCH 012/259] Rename to Source, remove omitempty on workload field --- ...mentations.yaml => odigos.io_sources.yaml} | 16 +- .../{instrumentation.go => source.go} | 54 ++--- .../{instrumentationspec.go => sourcespec.go} | 12 +- ...strumentationstatus.go => sourcestatus.go} | 12 +- .../odigos/applyconfiguration/utils.go | 12 +- .../v1alpha1/fake/fake_instrumentation.go | 196 ------------------ .../v1alpha1/fake/fake_odigos_client.go | 8 +- .../typed/odigos/v1alpha1/fake/fake_source.go | 196 ++++++++++++++++++ .../odigos/v1alpha1/generated_expansion.go | 4 +- .../typed/odigos/v1alpha1/instrumentation.go | 72 ------- .../typed/odigos/v1alpha1/odigos_client.go | 10 +- .../versioned/typed/odigos/v1alpha1/source.go | 72 +++++++ .../informers/externalversions/generic.go | 4 +- .../odigos/v1alpha1/interface.go | 14 +- .../{instrumentation.go => source.go} | 38 ++-- .../odigos/v1alpha1/expansion_generated.go | 16 +- .../odigos/v1alpha1/instrumentation.go | 69 ------ .../odigos/listers/odigos/v1alpha1/source.go | 69 ++++++ ...strumentation_types.go => source_types.go} | 22 +- api/odigos/v1alpha1/zz_generated.deepcopy.go | 194 ++++++++--------- ...mentations.yaml => odigos.io_sources.yaml} | 16 +- 21 files changed, 555 insertions(+), 551 deletions(-) rename api/config/crd/bases/{odigos.io_instrumentations.yaml => odigos.io_sources.yaml} (94%) rename api/generated/odigos/applyconfiguration/odigos/v1alpha1/{instrumentation.go => source.go} (74%) rename api/generated/odigos/applyconfiguration/odigos/v1alpha1/{instrumentationspec.go => sourcespec.go} (66%) rename api/generated/odigos/applyconfiguration/odigos/v1alpha1/{instrumentationstatus.go => sourcestatus.go} (68%) delete mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go create mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_source.go delete mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/source.go rename api/generated/odigos/informers/externalversions/odigos/v1alpha1/{instrumentation.go => source.go} (55%) delete mode 100644 api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go create mode 100644 api/generated/odigos/listers/odigos/v1alpha1/source.go rename api/odigos/v1alpha1/{instrumentation_types.go => source_types.go} (76%) rename helm/odigos/templates/crds/{odigos.io_instrumentations.yaml => odigos.io_sources.yaml} (94%) diff --git a/api/config/crd/bases/odigos.io_instrumentations.yaml b/api/config/crd/bases/odigos.io_sources.yaml similarity index 94% rename from api/config/crd/bases/odigos.io_instrumentations.yaml rename to api/config/crd/bases/odigos.io_sources.yaml index 80c24ac88..cda0e2b88 100644 --- a/api/config/crd/bases/odigos.io_instrumentations.yaml +++ b/api/config/crd/bases/odigos.io_sources.yaml @@ -7,14 +7,14 @@ metadata: labels: metadata.labels.odigos.io/config: "1" metadata.labels.odigos.io/system-object: "true" - name: instrumentations.odigos.io + name: sources.odigos.io spec: group: odigos.io names: - kind: Instrumentation - listKind: InstrumentationList - plural: instrumentations - singular: instrumentation + kind: Source + listKind: SourceList + plural: sources + singular: source scope: Namespaced versions: - additionalPrinterColumns: @@ -30,7 +30,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: Instrumentation configures an application for auto-instrumentation. + description: Source configures an application for auto-instrumentation. properties: apiVersion: description: |- @@ -77,7 +77,7 @@ spec: properties: conditions: description: |- - Represents the observations of a instrumentationrule's current state. + Represents the observations of a source's current state. Known .status.conditions.type are: "Available", "Progressing" items: description: Condition contains details for one aspect of the current @@ -138,6 +138,8 @@ spec: - type x-kubernetes-list-type: map type: object + required: + - spec type: object served: true storage: true diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/source.go similarity index 74% rename from api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go rename to api/generated/odigos/applyconfiguration/odigos/v1alpha1/source.go index 5e3c2990d..4a706aee6 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentation.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/source.go @@ -23,22 +23,22 @@ import ( v1 "k8s.io/client-go/applyconfigurations/meta/v1" ) -// InstrumentationApplyConfiguration represents a declarative configuration of the Instrumentation type for use +// SourceApplyConfiguration represents a declarative configuration of the Source type for use // with apply. -type InstrumentationApplyConfiguration struct { +type SourceApplyConfiguration struct { v1.TypeMetaApplyConfiguration `json:",inline"` *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` - Spec *InstrumentationSpecApplyConfiguration `json:"spec,omitempty"` - Status *InstrumentationStatusApplyConfiguration `json:"status,omitempty"` + Spec *SourceSpecApplyConfiguration `json:"spec,omitempty"` + Status *SourceStatusApplyConfiguration `json:"status,omitempty"` } -// Instrumentation constructs a declarative configuration of the Instrumentation type for use with +// Source constructs a declarative configuration of the Source type for use with // apply. -func Instrumentation(name, namespace string) *InstrumentationApplyConfiguration { - b := &InstrumentationApplyConfiguration{} +func Source(name, namespace string) *SourceApplyConfiguration { + b := &SourceApplyConfiguration{} b.WithName(name) b.WithNamespace(namespace) - b.WithKind("Instrumentation") + b.WithKind("Source") b.WithAPIVersion("odigos.io/v1alpha1") return b } @@ -46,7 +46,7 @@ func Instrumentation(name, namespace string) *InstrumentationApplyConfiguration // 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 *InstrumentationApplyConfiguration) WithKind(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithKind(value string) *SourceApplyConfiguration { b.Kind = &value return b } @@ -54,7 +54,7 @@ func (b *InstrumentationApplyConfiguration) WithKind(value string) *Instrumentat // 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 *InstrumentationApplyConfiguration) WithAPIVersion(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithAPIVersion(value string) *SourceApplyConfiguration { b.APIVersion = &value return b } @@ -62,7 +62,7 @@ func (b *InstrumentationApplyConfiguration) WithAPIVersion(value string) *Instru // 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 *InstrumentationApplyConfiguration) WithName(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithName(value string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.Name = &value return b @@ -71,7 +71,7 @@ func (b *InstrumentationApplyConfiguration) WithName(value string) *Instrumentat // 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 *InstrumentationApplyConfiguration) WithGenerateName(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithGenerateName(value string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.GenerateName = &value return b @@ -80,7 +80,7 @@ func (b *InstrumentationApplyConfiguration) WithGenerateName(value string) *Inst // 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 *InstrumentationApplyConfiguration) WithNamespace(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithNamespace(value string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.Namespace = &value return b @@ -89,7 +89,7 @@ func (b *InstrumentationApplyConfiguration) WithNamespace(value string) *Instrum // 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 *InstrumentationApplyConfiguration) WithUID(value types.UID) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithUID(value types.UID) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.UID = &value return b @@ -98,7 +98,7 @@ func (b *InstrumentationApplyConfiguration) WithUID(value types.UID) *Instrument // 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 *InstrumentationApplyConfiguration) WithResourceVersion(value string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithResourceVersion(value string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ResourceVersion = &value return b @@ -107,7 +107,7 @@ func (b *InstrumentationApplyConfiguration) WithResourceVersion(value string) *I // 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 *InstrumentationApplyConfiguration) WithGeneration(value int64) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithGeneration(value int64) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.Generation = &value return b @@ -116,7 +116,7 @@ func (b *InstrumentationApplyConfiguration) WithGeneration(value int64) *Instrum // 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 *InstrumentationApplyConfiguration) WithCreationTimestamp(value metav1.Time) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithCreationTimestamp(value metav1.Time) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.CreationTimestamp = &value return b @@ -125,7 +125,7 @@ func (b *InstrumentationApplyConfiguration) WithCreationTimestamp(value metav1.T // 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 *InstrumentationApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.DeletionTimestamp = &value return b @@ -134,7 +134,7 @@ func (b *InstrumentationApplyConfiguration) WithDeletionTimestamp(value metav1.T // 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 *InstrumentationApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.DeletionGracePeriodSeconds = &value return b @@ -144,7 +144,7 @@ func (b *InstrumentationApplyConfiguration) WithDeletionGracePeriodSeconds(value // 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 *InstrumentationApplyConfiguration) WithLabels(entries map[string]string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithLabels(entries map[string]string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() if b.Labels == nil && len(entries) > 0 { b.Labels = make(map[string]string, len(entries)) @@ -159,7 +159,7 @@ func (b *InstrumentationApplyConfiguration) WithLabels(entries map[string]string // 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 *InstrumentationApplyConfiguration) WithAnnotations(entries map[string]string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithAnnotations(entries map[string]string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() if b.Annotations == nil && len(entries) > 0 { b.Annotations = make(map[string]string, len(entries)) @@ -173,7 +173,7 @@ func (b *InstrumentationApplyConfiguration) WithAnnotations(entries map[string]s // 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 *InstrumentationApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() for i := range values { if values[i] == nil { @@ -187,7 +187,7 @@ func (b *InstrumentationApplyConfiguration) WithOwnerReferences(values ...*v1.Ow // 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 *InstrumentationApplyConfiguration) WithFinalizers(values ...string) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithFinalizers(values ...string) *SourceApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() for i := range values { b.Finalizers = append(b.Finalizers, values[i]) @@ -195,7 +195,7 @@ func (b *InstrumentationApplyConfiguration) WithFinalizers(values ...string) *In return b } -func (b *InstrumentationApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { +func (b *SourceApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { if b.ObjectMetaApplyConfiguration == nil { b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} } @@ -204,7 +204,7 @@ func (b *InstrumentationApplyConfiguration) ensureObjectMetaApplyConfigurationEx // 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 *InstrumentationApplyConfiguration) WithSpec(value *InstrumentationSpecApplyConfiguration) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithSpec(value *SourceSpecApplyConfiguration) *SourceApplyConfiguration { b.Spec = value return b } @@ -212,13 +212,13 @@ func (b *InstrumentationApplyConfiguration) WithSpec(value *InstrumentationSpecA // 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 *InstrumentationApplyConfiguration) WithStatus(value *InstrumentationStatusApplyConfiguration) *InstrumentationApplyConfiguration { +func (b *SourceApplyConfiguration) WithStatus(value *SourceStatusApplyConfiguration) *SourceApplyConfiguration { b.Status = value return b } // GetName retrieves the value of the Name field in the declarative configuration. -func (b *InstrumentationApplyConfiguration) GetName() *string { +func (b *SourceApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.Name } diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go similarity index 66% rename from api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go rename to api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go index dab281b1d..ae7774ba8 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationspec.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go @@ -21,22 +21,22 @@ import ( workload "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -// InstrumentationSpecApplyConfiguration represents a declarative configuration of the InstrumentationSpec type for use +// SourceSpecApplyConfiguration represents a declarative configuration of the SourceSpec type for use // with apply. -type InstrumentationSpecApplyConfiguration struct { +type SourceSpecApplyConfiguration struct { Workload *workload.PodWorkload `json:"workload,omitempty"` } -// InstrumentationSpecApplyConfiguration constructs a declarative configuration of the InstrumentationSpec type for use with +// SourceSpecApplyConfiguration constructs a declarative configuration of the SourceSpec type for use with // apply. -func InstrumentationSpec() *InstrumentationSpecApplyConfiguration { - return &InstrumentationSpecApplyConfiguration{} +func SourceSpec() *SourceSpecApplyConfiguration { + return &SourceSpecApplyConfiguration{} } // WithWorkload sets the Workload 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 Workload field is set to the value of the last call. -func (b *InstrumentationSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) *InstrumentationSpecApplyConfiguration { +func (b *SourceSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) *SourceSpecApplyConfiguration { b.Workload = &value return b } diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcestatus.go similarity index 68% rename from api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go rename to api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcestatus.go index d3f06e54e..c188eb75c 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationstatus.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcestatus.go @@ -21,22 +21,22 @@ import ( v1 "k8s.io/client-go/applyconfigurations/meta/v1" ) -// InstrumentationStatusApplyConfiguration represents a declarative configuration of the InstrumentationStatus type for use +// SourceStatusApplyConfiguration represents a declarative configuration of the SourceStatus type for use // with apply. -type InstrumentationStatusApplyConfiguration struct { +type SourceStatusApplyConfiguration struct { Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` } -// InstrumentationStatusApplyConfiguration constructs a declarative configuration of the InstrumentationStatus type for use with +// SourceStatusApplyConfiguration constructs a declarative configuration of the SourceStatus type for use with // apply. -func InstrumentationStatus() *InstrumentationStatusApplyConfiguration { - return &InstrumentationStatusApplyConfiguration{} +func SourceStatus() *SourceStatusApplyConfiguration { + return &SourceStatusApplyConfiguration{} } // 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 *InstrumentationStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationStatusApplyConfiguration { +func (b *SourceStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *SourceStatusApplyConfiguration { for i := range values { if values[i] == nil { panic("nil value passed to WithConditions") diff --git a/api/generated/odigos/applyconfiguration/utils.go b/api/generated/odigos/applyconfiguration/utils.go index 6b1921839..4d87e42fc 100644 --- a/api/generated/odigos/applyconfiguration/utils.go +++ b/api/generated/odigos/applyconfiguration/utils.go @@ -59,8 +59,6 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.EnvVarApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("HeadSamplingConfig"): return &odigosv1alpha1.HeadSamplingConfigApplyConfiguration{} - case v1alpha1.SchemeGroupVersion.WithKind("Instrumentation"): - return &odigosv1alpha1.InstrumentationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationConfig"): return &odigosv1alpha1.InstrumentationConfigApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationConfigSpec"): @@ -93,10 +91,6 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.InstrumentationRuleSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationRuleStatus"): return &odigosv1alpha1.InstrumentationRuleStatusApplyConfiguration{} - case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationSpec"): - return &odigosv1alpha1.InstrumentationSpecApplyConfiguration{} - case v1alpha1.SchemeGroupVersion.WithKind("InstrumentationStatus"): - return &odigosv1alpha1.InstrumentationStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentedApplication"): return &odigosv1alpha1.InstrumentedApplicationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentedApplicationSpec"): @@ -119,6 +113,12 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.RuntimeDetailsByContainerApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SdkConfig"): return &odigosv1alpha1.SdkConfigApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Source"): + return &odigosv1alpha1.SourceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SourceSpec"): + return &odigosv1alpha1.SourceSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SourceStatus"): + return &odigosv1alpha1.SourceStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("WorkloadInstrumentationConfig"): return &odigosv1alpha1.WorkloadInstrumentationConfigApplyConfiguration{} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go deleted file mode 100644 index bee026a3e..000000000 --- a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_instrumentation.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -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" -) - -// FakeInstrumentations implements InstrumentationInterface -type FakeInstrumentations struct { - Fake *FakeOdigosV1alpha1 - ns string -} - -var instrumentationsResource = v1alpha1.SchemeGroupVersion.WithResource("instrumentations") - -var instrumentationsKind = v1alpha1.SchemeGroupVersion.WithKind("Instrumentation") - -// Get takes name of the instrumentation, and returns the corresponding instrumentation object, and an error if there is any. -func (c *FakeInstrumentations) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewGetActionWithOptions(instrumentationsResource, c.ns, name, options), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), err -} - -// List takes label and field selectors, and returns the list of Instrumentations that match those selectors. -func (c *FakeInstrumentations) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.InstrumentationList, err error) { - emptyResult := &v1alpha1.InstrumentationList{} - obj, err := c.Fake. - Invokes(testing.NewListActionWithOptions(instrumentationsResource, instrumentationsKind, c.ns, opts), emptyResult) - - if obj == nil { - return emptyResult, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.InstrumentationList{ListMeta: obj.(*v1alpha1.InstrumentationList).ListMeta} - for _, item := range obj.(*v1alpha1.InstrumentationList).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 instrumentations. -func (c *FakeInstrumentations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchActionWithOptions(instrumentationsResource, c.ns, opts)) - -} - -// Create takes the representation of a instrumentation and creates it. Returns the server's representation of the instrumentation, and an error, if there is any. -func (c *FakeInstrumentations) Create(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.CreateOptions) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewCreateActionWithOptions(instrumentationsResource, c.ns, instrumentation, opts), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), err -} - -// Update takes the representation of a instrumentation and updates it. Returns the server's representation of the instrumentation, and an error, if there is any. -func (c *FakeInstrumentations) Update(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewUpdateActionWithOptions(instrumentationsResource, c.ns, instrumentation, opts), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), 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 *FakeInstrumentations) UpdateStatus(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceActionWithOptions(instrumentationsResource, "status", c.ns, instrumentation, opts), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), err -} - -// Delete takes name of the instrumentation and deletes it. Returns an error if one occurs. -func (c *FakeInstrumentations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(instrumentationsResource, c.ns, name, opts), &v1alpha1.Instrumentation{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeInstrumentations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionActionWithOptions(instrumentationsResource, c.ns, opts, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.InstrumentationList{}) - return err -} - -// Patch applies the patch and returns the patched instrumentation. -func (c *FakeInstrumentations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Instrumentation, err error) { - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, name, pt, data, opts, subresources...), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), err -} - -// Apply takes the given apply declarative configuration, applies it and returns the applied instrumentation. -func (c *FakeInstrumentations) Apply(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) { - if instrumentation == nil { - return nil, fmt.Errorf("instrumentation provided to Apply must not be nil") - } - data, err := json.Marshal(instrumentation) - if err != nil { - return nil, err - } - name := instrumentation.Name - if name == nil { - return nil, fmt.Errorf("instrumentation.Name must be provided to Apply") - } - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), 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 *FakeInstrumentations) ApplyStatus(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) { - if instrumentation == nil { - return nil, fmt.Errorf("instrumentation provided to Apply must not be nil") - } - data, err := json.Marshal(instrumentation) - if err != nil { - return nil, err - } - name := instrumentation.Name - if name == nil { - return nil, fmt.Errorf("instrumentation.Name must be provided to Apply") - } - emptyResult := &v1alpha1.Instrumentation{} - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceActionWithOptions(instrumentationsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions(), "status"), emptyResult) - - if obj == nil { - return emptyResult, err - } - return obj.(*v1alpha1.Instrumentation), 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 b9449792b..37a6bb012 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 @@ -35,10 +35,6 @@ func (c *FakeOdigosV1alpha1) Destinations(namespace string) v1alpha1.Destination return &FakeDestinations{c, namespace} } -func (c *FakeOdigosV1alpha1) Instrumentations(namespace string) v1alpha1.InstrumentationInterface { - return &FakeInstrumentations{c, namespace} -} - func (c *FakeOdigosV1alpha1) InstrumentationConfigs(namespace string) v1alpha1.InstrumentationConfigInterface { return &FakeInstrumentationConfigs{c, namespace} } @@ -63,6 +59,10 @@ func (c *FakeOdigosV1alpha1) Processors(namespace string) v1alpha1.ProcessorInte return &FakeProcessors{c, namespace} } +func (c *FakeOdigosV1alpha1) Sources(namespace string) v1alpha1.SourceInterface { + return &FakeSources{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeOdigosV1alpha1) RESTClient() rest.Interface { diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_source.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_source.go new file mode 100644 index 000000000..58451ba26 --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/fake/fake_source.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" +) + +// FakeSources implements SourceInterface +type FakeSources struct { + Fake *FakeOdigosV1alpha1 + ns string +} + +var sourcesResource = v1alpha1.SchemeGroupVersion.WithResource("sources") + +var sourcesKind = v1alpha1.SchemeGroupVersion.WithKind("Source") + +// Get takes name of the source, and returns the corresponding source object, and an error if there is any. +func (c *FakeSources) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(sourcesResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} + +// List takes label and field selectors, and returns the list of Sources that match those selectors. +func (c *FakeSources) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.SourceList, err error) { + emptyResult := &v1alpha1.SourceList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(sourcesResource, sourcesKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.SourceList{ListMeta: obj.(*v1alpha1.SourceList).ListMeta} + for _, item := range obj.(*v1alpha1.SourceList).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 sources. +func (c *FakeSources) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(sourcesResource, c.ns, opts)) + +} + +// Create takes the representation of a source and creates it. Returns the server's representation of the source, and an error, if there is any. +func (c *FakeSources) Create(ctx context.Context, source *v1alpha1.Source, opts v1.CreateOptions) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(sourcesResource, c.ns, source, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} + +// Update takes the representation of a source and updates it. Returns the server's representation of the source, and an error, if there is any. +func (c *FakeSources) Update(ctx context.Context, source *v1alpha1.Source, opts v1.UpdateOptions) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(sourcesResource, c.ns, source, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), 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 *FakeSources) UpdateStatus(ctx context.Context, source *v1alpha1.Source, opts v1.UpdateOptions) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceActionWithOptions(sourcesResource, "status", c.ns, source, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} + +// Delete takes name of the source and deletes it. Returns an error if one occurs. +func (c *FakeSources) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(sourcesResource, c.ns, name, opts), &v1alpha1.Source{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeSources) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(sourcesResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.SourceList{}) + return err +} + +// Patch applies the patch and returns the patched source. +func (c *FakeSources) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Source, err error) { + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(sourcesResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} + +// Apply takes the given apply declarative configuration, applies it and returns the applied source. +func (c *FakeSources) Apply(ctx context.Context, source *odigosv1alpha1.SourceApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Source, err error) { + if source == nil { + return nil, fmt.Errorf("source provided to Apply must not be nil") + } + data, err := json.Marshal(source) + if err != nil { + return nil, err + } + name := source.Name + if name == nil { + return nil, fmt.Errorf("source.Name must be provided to Apply") + } + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(sourcesResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), 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 *FakeSources) ApplyStatus(ctx context.Context, source *odigosv1alpha1.SourceApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Source, err error) { + if source == nil { + return nil, fmt.Errorf("source provided to Apply must not be nil") + } + data, err := json.Marshal(source) + if err != nil { + return nil, err + } + name := source.Name + if name == nil { + return nil, fmt.Errorf("source.Name must be provided to Apply") + } + emptyResult := &v1alpha1.Source{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(sourcesResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions(), "status"), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.Source), err +} 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 ff0918d9d..15777d780 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 @@ -21,8 +21,6 @@ type CollectorsGroupExpansion interface{} type DestinationExpansion interface{} -type InstrumentationExpansion interface{} - type InstrumentationConfigExpansion interface{} type InstrumentationInstanceExpansion interface{} @@ -34,3 +32,5 @@ type InstrumentedApplicationExpansion interface{} type OdigosConfigurationExpansion interface{} type ProcessorExpansion interface{} + +type SourceExpansion interface{} diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go deleted file mode 100644 index 7a77d533d..000000000 --- a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/instrumentation.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -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" -) - -// InstrumentationsGetter has a method to return a InstrumentationInterface. -// A group's client should implement this interface. -type InstrumentationsGetter interface { - Instrumentations(namespace string) InstrumentationInterface -} - -// InstrumentationInterface has methods to work with Instrumentation resources. -type InstrumentationInterface interface { - Create(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.CreateOptions) (*v1alpha1.Instrumentation, error) - Update(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (*v1alpha1.Instrumentation, error) - // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - UpdateStatus(ctx context.Context, instrumentation *v1alpha1.Instrumentation, opts v1.UpdateOptions) (*v1alpha1.Instrumentation, 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.Instrumentation, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.InstrumentationList, 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.Instrumentation, err error) - Apply(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) - // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). - ApplyStatus(ctx context.Context, instrumentation *odigosv1alpha1.InstrumentationApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Instrumentation, err error) - InstrumentationExpansion -} - -// instrumentations implements InstrumentationInterface -type instrumentations struct { - *gentype.ClientWithListAndApply[*v1alpha1.Instrumentation, *v1alpha1.InstrumentationList, *odigosv1alpha1.InstrumentationApplyConfiguration] -} - -// newInstrumentations returns a Instrumentations -func newInstrumentations(c *OdigosV1alpha1Client, namespace string) *instrumentations { - return &instrumentations{ - gentype.NewClientWithListAndApply[*v1alpha1.Instrumentation, *v1alpha1.InstrumentationList, *odigosv1alpha1.InstrumentationApplyConfiguration]( - "instrumentations", - c.RESTClient(), - scheme.ParameterCodec, - namespace, - func() *v1alpha1.Instrumentation { return &v1alpha1.Instrumentation{} }, - func() *v1alpha1.InstrumentationList { return &v1alpha1.InstrumentationList{} }), - } -} 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 dc535257d..6e7b3013c 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 @@ -29,13 +29,13 @@ type OdigosV1alpha1Interface interface { RESTClient() rest.Interface CollectorsGroupsGetter DestinationsGetter - InstrumentationsGetter InstrumentationConfigsGetter InstrumentationInstancesGetter InstrumentationRulesGetter InstrumentedApplicationsGetter OdigosConfigurationsGetter ProcessorsGetter + SourcesGetter } // OdigosV1alpha1Client is used to interact with features provided by the odigos.io group. @@ -51,10 +51,6 @@ func (c *OdigosV1alpha1Client) Destinations(namespace string) DestinationInterfa return newDestinations(c, namespace) } -func (c *OdigosV1alpha1Client) Instrumentations(namespace string) InstrumentationInterface { - return newInstrumentations(c, namespace) -} - func (c *OdigosV1alpha1Client) InstrumentationConfigs(namespace string) InstrumentationConfigInterface { return newInstrumentationConfigs(c, namespace) } @@ -79,6 +75,10 @@ func (c *OdigosV1alpha1Client) Processors(namespace string) ProcessorInterface { return newProcessors(c, namespace) } +func (c *OdigosV1alpha1Client) Sources(namespace string) SourceInterface { + return newSources(c, namespace) +} + // NewForConfig creates a new OdigosV1alpha1Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). diff --git a/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/source.go b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/source.go new file mode 100644 index 000000000..3245a184e --- /dev/null +++ b/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1/source.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" +) + +// SourcesGetter has a method to return a SourceInterface. +// A group's client should implement this interface. +type SourcesGetter interface { + Sources(namespace string) SourceInterface +} + +// SourceInterface has methods to work with Source resources. +type SourceInterface interface { + Create(ctx context.Context, source *v1alpha1.Source, opts v1.CreateOptions) (*v1alpha1.Source, error) + Update(ctx context.Context, source *v1alpha1.Source, opts v1.UpdateOptions) (*v1alpha1.Source, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, source *v1alpha1.Source, opts v1.UpdateOptions) (*v1alpha1.Source, 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.Source, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.SourceList, 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.Source, err error) + Apply(ctx context.Context, source *odigosv1alpha1.SourceApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Source, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, source *odigosv1alpha1.SourceApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.Source, err error) + SourceExpansion +} + +// sources implements SourceInterface +type sources struct { + *gentype.ClientWithListAndApply[*v1alpha1.Source, *v1alpha1.SourceList, *odigosv1alpha1.SourceApplyConfiguration] +} + +// newSources returns a Sources +func newSources(c *OdigosV1alpha1Client, namespace string) *sources { + return &sources{ + gentype.NewClientWithListAndApply[*v1alpha1.Source, *v1alpha1.SourceList, *odigosv1alpha1.SourceApplyConfiguration]( + "sources", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1alpha1.Source { return &v1alpha1.Source{} }, + func() *v1alpha1.SourceList { return &v1alpha1.SourceList{} }), + } +} diff --git a/api/generated/odigos/informers/externalversions/generic.go b/api/generated/odigos/informers/externalversions/generic.go index f56304bd2..0ce00ff04 100644 --- a/api/generated/odigos/informers/externalversions/generic.go +++ b/api/generated/odigos/informers/externalversions/generic.go @@ -56,8 +56,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().CollectorsGroups().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("destinations"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Destinations().Informer()}, nil - case v1alpha1.SchemeGroupVersion.WithResource("instrumentations"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Instrumentations().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationconfigs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().InstrumentationConfigs().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("instrumentationinstances"): @@ -70,6 +68,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().OdigosConfigurations().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("processors"): return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Processors().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("sources"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Odigos().V1alpha1().Sources().Informer()}, nil } diff --git a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go index f896b99a6..160277736 100644 --- a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/interface.go @@ -27,8 +27,6 @@ type Interface interface { CollectorsGroups() CollectorsGroupInformer // Destinations returns a DestinationInformer. Destinations() DestinationInformer - // Instrumentations returns a InstrumentationInformer. - Instrumentations() InstrumentationInformer // InstrumentationConfigs returns a InstrumentationConfigInformer. InstrumentationConfigs() InstrumentationConfigInformer // InstrumentationInstances returns a InstrumentationInstanceInformer. @@ -41,6 +39,8 @@ type Interface interface { OdigosConfigurations() OdigosConfigurationInformer // Processors returns a ProcessorInformer. Processors() ProcessorInformer + // Sources returns a SourceInformer. + Sources() SourceInformer } type version struct { @@ -64,11 +64,6 @@ func (v *version) Destinations() DestinationInformer { return &destinationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } -// Instrumentations returns a InstrumentationInformer. -func (v *version) Instrumentations() InstrumentationInformer { - return &instrumentationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - // InstrumentationConfigs returns a InstrumentationConfigInformer. func (v *version) InstrumentationConfigs() InstrumentationConfigInformer { return &instrumentationConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} @@ -98,3 +93,8 @@ func (v *version) OdigosConfigurations() OdigosConfigurationInformer { func (v *version) Processors() ProcessorInformer { return &processorInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// Sources returns a SourceInformer. +func (v *version) Sources() SourceInformer { + return &sourceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/source.go similarity index 55% rename from api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go rename to api/generated/odigos/informers/externalversions/odigos/v1alpha1/source.go index 2b1e116dc..a74144c7b 100644 --- a/api/generated/odigos/informers/externalversions/odigos/v1alpha1/instrumentation.go +++ b/api/generated/odigos/informers/externalversions/odigos/v1alpha1/source.go @@ -31,59 +31,59 @@ import ( cache "k8s.io/client-go/tools/cache" ) -// InstrumentationInformer provides access to a shared informer and lister for -// Instrumentations. -type InstrumentationInformer interface { +// SourceInformer provides access to a shared informer and lister for +// Sources. +type SourceInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.InstrumentationLister + Lister() v1alpha1.SourceLister } -type instrumentationInformer struct { +type sourceInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc namespace string } -// NewInstrumentationInformer constructs a new informer for Instrumentation type. +// NewSourceInformer constructs a new informer for Source 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 NewInstrumentationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredInstrumentationInformer(client, namespace, resyncPeriod, indexers, nil) +func NewSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredSourceInformer(client, namespace, resyncPeriod, indexers, nil) } -// NewFilteredInstrumentationInformer constructs a new informer for Instrumentation type. +// NewFilteredSourceInformer constructs a new informer for Source 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 NewFilteredInstrumentationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredSourceInformer(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().Instrumentations(namespace).List(context.TODO(), options) + return client.OdigosV1alpha1().Sources(namespace).List(context.TODO(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.OdigosV1alpha1().Instrumentations(namespace).Watch(context.TODO(), options) + return client.OdigosV1alpha1().Sources(namespace).Watch(context.TODO(), options) }, }, - &odigosv1alpha1.Instrumentation{}, + &odigosv1alpha1.Source{}, resyncPeriod, indexers, ) } -func (f *instrumentationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredInstrumentationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *sourceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredSourceInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *instrumentationInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&odigosv1alpha1.Instrumentation{}, f.defaultInformer) +func (f *sourceInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&odigosv1alpha1.Source{}, f.defaultInformer) } -func (f *instrumentationInformer) Lister() v1alpha1.InstrumentationLister { - return v1alpha1.NewInstrumentationLister(f.Informer().GetIndexer()) +func (f *sourceInformer) Lister() v1alpha1.SourceLister { + return v1alpha1.NewSourceLister(f.Informer().GetIndexer()) } diff --git a/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go b/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go index 6bb59254f..fea348a49 100644 --- a/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go +++ b/api/generated/odigos/listers/odigos/v1alpha1/expansion_generated.go @@ -33,14 +33,6 @@ type DestinationListerExpansion interface{} // DestinationNamespaceLister. type DestinationNamespaceListerExpansion interface{} -// InstrumentationListerExpansion allows custom methods to be added to -// InstrumentationLister. -type InstrumentationListerExpansion interface{} - -// InstrumentationNamespaceListerExpansion allows custom methods to be added to -// InstrumentationNamespaceLister. -type InstrumentationNamespaceListerExpansion interface{} - // InstrumentationConfigListerExpansion allows custom methods to be added to // InstrumentationConfigLister. type InstrumentationConfigListerExpansion interface{} @@ -88,3 +80,11 @@ type ProcessorListerExpansion interface{} // ProcessorNamespaceListerExpansion allows custom methods to be added to // ProcessorNamespaceLister. type ProcessorNamespaceListerExpansion interface{} + +// SourceListerExpansion allows custom methods to be added to +// SourceLister. +type SourceListerExpansion interface{} + +// SourceNamespaceListerExpansion allows custom methods to be added to +// SourceNamespaceLister. +type SourceNamespaceListerExpansion interface{} diff --git a/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go b/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go deleted file mode 100644 index d537b568c..000000000 --- a/api/generated/odigos/listers/odigos/v1alpha1/instrumentation.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -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" -) - -// InstrumentationLister helps list Instrumentations. -// All objects returned here must be treated as read-only. -type InstrumentationLister interface { - // List lists all Instrumentations in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.Instrumentation, err error) - // Instrumentations returns an object that can list and get Instrumentations. - Instrumentations(namespace string) InstrumentationNamespaceLister - InstrumentationListerExpansion -} - -// instrumentationLister implements the InstrumentationLister interface. -type instrumentationLister struct { - listers.ResourceIndexer[*v1alpha1.Instrumentation] -} - -// NewInstrumentationLister returns a new InstrumentationLister. -func NewInstrumentationLister(indexer cache.Indexer) InstrumentationLister { - return &instrumentationLister{listers.New[*v1alpha1.Instrumentation](indexer, v1alpha1.Resource("instrumentation"))} -} - -// Instrumentations returns an object that can list and get Instrumentations. -func (s *instrumentationLister) Instrumentations(namespace string) InstrumentationNamespaceLister { - return instrumentationNamespaceLister{listers.NewNamespaced[*v1alpha1.Instrumentation](s.ResourceIndexer, namespace)} -} - -// InstrumentationNamespaceLister helps list and get Instrumentations. -// All objects returned here must be treated as read-only. -type InstrumentationNamespaceLister interface { - // List lists all Instrumentations in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.Instrumentation, err error) - // Get retrieves the Instrumentation from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.Instrumentation, error) - InstrumentationNamespaceListerExpansion -} - -// instrumentationNamespaceLister implements the InstrumentationNamespaceLister -// interface. -type instrumentationNamespaceLister struct { - listers.ResourceIndexer[*v1alpha1.Instrumentation] -} diff --git a/api/generated/odigos/listers/odigos/v1alpha1/source.go b/api/generated/odigos/listers/odigos/v1alpha1/source.go new file mode 100644 index 000000000..36dd5f7b3 --- /dev/null +++ b/api/generated/odigos/listers/odigos/v1alpha1/source.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" +) + +// SourceLister helps list Sources. +// All objects returned here must be treated as read-only. +type SourceLister interface { + // List lists all Sources in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Source, err error) + // Sources returns an object that can list and get Sources. + Sources(namespace string) SourceNamespaceLister + SourceListerExpansion +} + +// sourceLister implements the SourceLister interface. +type sourceLister struct { + listers.ResourceIndexer[*v1alpha1.Source] +} + +// NewSourceLister returns a new SourceLister. +func NewSourceLister(indexer cache.Indexer) SourceLister { + return &sourceLister{listers.New[*v1alpha1.Source](indexer, v1alpha1.Resource("source"))} +} + +// Sources returns an object that can list and get Sources. +func (s *sourceLister) Sources(namespace string) SourceNamespaceLister { + return sourceNamespaceLister{listers.NewNamespaced[*v1alpha1.Source](s.ResourceIndexer, namespace)} +} + +// SourceNamespaceLister helps list and get Sources. +// All objects returned here must be treated as read-only. +type SourceNamespaceLister interface { + // List lists all Sources in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Source, err error) + // Get retrieves the Source from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Source, error) + SourceNamespaceListerExpansion +} + +// sourceNamespaceLister implements the SourceNamespaceLister +// interface. +type sourceNamespaceLister struct { + listers.ResourceIndexer[*v1alpha1.Source] +} diff --git a/api/odigos/v1alpha1/instrumentation_types.go b/api/odigos/v1alpha1/source_types.go similarity index 76% rename from api/odigos/v1alpha1/instrumentation_types.go rename to api/odigos/v1alpha1/source_types.go index 21c70de65..10886b2a0 100644 --- a/api/odigos/v1alpha1/instrumentation_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -22,7 +22,7 @@ import ( "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -// Instrumentation configures an application for auto-instrumentation. +// Source configures an application for auto-instrumentation. // +genclient // +kubebuilder:object:root=true // +kubebuilder:subresource:status @@ -31,23 +31,23 @@ import ( // +kubebuilder:printcolumn:name="Workload",type=string,JSONPath=`.spec.workload.name` // +kubebuilder:printcolumn:name="Kind",type=string,JSONPath=`.spec.workload.kind` // +kubebuilder:printcolumn:name="Namespace",type=string,JSONPath=`.spec.workload.namespace` -type Instrumentation struct { +type Source struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec InstrumentationSpec `json:"spec,omitempty"` - Status InstrumentationStatus `json:"status,omitempty"` + Spec SourceSpec `json:"spec"` + Status SourceStatus `json:"status,omitempty"` } -type InstrumentationSpec struct { +type SourceSpec struct { // Workload represents the workload or namespace to be instrumented. // This field is required upon creation and cannot be modified. // +kubebuilder:validation:Required - Workload workload.PodWorkload `json:"workload,omitempty"` + Workload workload.PodWorkload `json:"workload"` } -type InstrumentationStatus struct { - // Represents the observations of a instrumentationrule's current state. +type SourceStatus struct { + // Represents the observations of a source's current state. // Known .status.conditions.type are: "Available", "Progressing" // +patchMergeKey=type // +patchStrategy=merge @@ -58,12 +58,12 @@ type InstrumentationStatus struct { //+kubebuilder:object:root=true -type InstrumentationList struct { +type SourceList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []Instrumentation `json:"items"` + Items []Source `json:"items"` } func init() { - SchemeBuilder.Register(&Instrumentation{}, &InstrumentationList{}) + SchemeBuilder.Register(&Source{}, &SourceList{}) } diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 2a9681af6..96b4a7d74 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -386,33 +386,6 @@ func (in *HeadSamplingConfig) DeepCopy() *HeadSamplingConfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Instrumentation) DeepCopyInto(out *Instrumentation) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Instrumentation. -func (in *Instrumentation) DeepCopy() *Instrumentation { - if in == nil { - return nil - } - out := new(Instrumentation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Instrumentation) 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 *InstrumentationConfig) DeepCopyInto(out *InstrumentationConfig) { *out = *in @@ -777,38 +750,6 @@ 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 *InstrumentationList) DeepCopyInto(out *InstrumentationList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Instrumentation, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationList. -func (in *InstrumentationList) DeepCopy() *InstrumentationList { - if in == nil { - return nil - } - out := new(InstrumentationList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *InstrumentationList) 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 *InstrumentationRule) DeepCopyInto(out *InstrumentationRule) { *out = *in @@ -933,44 +874,6 @@ func (in *InstrumentationRuleStatus) DeepCopy() *InstrumentationRuleStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { - *out = *in - out.Workload = in.Workload -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationSpec. -func (in *InstrumentationSpec) DeepCopy() *InstrumentationSpec { - if in == nil { - return nil - } - out := new(InstrumentationSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InstrumentationStatus) DeepCopyInto(out *InstrumentationStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationStatus. -func (in *InstrumentationStatus) DeepCopy() *InstrumentationStatus { - if in == nil { - return nil - } - out := new(InstrumentationStatus) - 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 @@ -1368,6 +1271,103 @@ func (in *SdkConfig) DeepCopy() *SdkConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Source) DeepCopyInto(out *Source) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Source. +func (in *Source) DeepCopy() *Source { + if in == nil { + return nil + } + out := new(Source) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Source) 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 *SourceList) DeepCopyInto(out *SourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Source, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceList. +func (in *SourceList) DeepCopy() *SourceList { + if in == nil { + return nil + } + out := new(SourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SourceList) 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 *SourceSpec) DeepCopyInto(out *SourceSpec) { + *out = *in + out.Workload = in.Workload +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. +func (in *SourceSpec) DeepCopy() *SourceSpec { + if in == nil { + return nil + } + out := new(SourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceStatus) DeepCopyInto(out *SourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceStatus. +func (in *SourceStatus) DeepCopy() *SourceStatus { + if in == nil { + return nil + } + out := new(SourceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkloadInstrumentationConfig) DeepCopyInto(out *WorkloadInstrumentationConfig) { *out = *in diff --git a/helm/odigos/templates/crds/odigos.io_instrumentations.yaml b/helm/odigos/templates/crds/odigos.io_sources.yaml similarity index 94% rename from helm/odigos/templates/crds/odigos.io_instrumentations.yaml rename to helm/odigos/templates/crds/odigos.io_sources.yaml index 80c24ac88..cda0e2b88 100644 --- a/helm/odigos/templates/crds/odigos.io_instrumentations.yaml +++ b/helm/odigos/templates/crds/odigos.io_sources.yaml @@ -7,14 +7,14 @@ metadata: labels: metadata.labels.odigos.io/config: "1" metadata.labels.odigos.io/system-object: "true" - name: instrumentations.odigos.io + name: sources.odigos.io spec: group: odigos.io names: - kind: Instrumentation - listKind: InstrumentationList - plural: instrumentations - singular: instrumentation + kind: Source + listKind: SourceList + plural: sources + singular: source scope: Namespaced versions: - additionalPrinterColumns: @@ -30,7 +30,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: Instrumentation configures an application for auto-instrumentation. + description: Source configures an application for auto-instrumentation. properties: apiVersion: description: |- @@ -77,7 +77,7 @@ spec: properties: conditions: description: |- - Represents the observations of a instrumentationrule's current state. + Represents the observations of a source's current state. Known .status.conditions.type are: "Available", "Progressing" items: description: Condition contains details for one aspect of the current @@ -138,6 +138,8 @@ spec: - type x-kubernetes-list-type: map type: object + required: + - spec type: object served: true storage: true From 8d72fea7c2a34ae5dc08eece42b9b8860e0a5486 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 10:56:40 -0500 Subject: [PATCH 013/259] Remove odigos labels --- api/config/crd/bases/odigos.io_sources.yaml | 3 --- api/odigos/v1alpha1/source_types.go | 2 -- helm/odigos/templates/crds/odigos.io_sources.yaml | 3 --- 3 files changed, 8 deletions(-) diff --git a/api/config/crd/bases/odigos.io_sources.yaml b/api/config/crd/bases/odigos.io_sources.yaml index cda0e2b88..c59f98ae2 100644 --- a/api/config/crd/bases/odigos.io_sources.yaml +++ b/api/config/crd/bases/odigos.io_sources.yaml @@ -4,9 +4,6 @@ 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: sources.odigos.io spec: group: odigos.io diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index 10886b2a0..3dd31ef63 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -26,8 +26,6 @@ import ( // +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 // +kubebuilder:printcolumn:name="Workload",type=string,JSONPath=`.spec.workload.name` // +kubebuilder:printcolumn:name="Kind",type=string,JSONPath=`.spec.workload.kind` // +kubebuilder:printcolumn:name="Namespace",type=string,JSONPath=`.spec.workload.namespace` diff --git a/helm/odigos/templates/crds/odigos.io_sources.yaml b/helm/odigos/templates/crds/odigos.io_sources.yaml index cda0e2b88..c59f98ae2 100644 --- a/helm/odigos/templates/crds/odigos.io_sources.yaml +++ b/helm/odigos/templates/crds/odigos.io_sources.yaml @@ -4,9 +4,6 @@ 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: sources.odigos.io spec: group: odigos.io From 3f3b9d09113ae18c78fc3aae222adb0e7bd82616 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:26:27 -0500 Subject: [PATCH 014/259] Add SourceReconciler and create InstrumentationConfig --- .../controllers/startlangdetection/manager.go | 16 +++++++++- .../startlangdetection/source_controller.go | 31 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 instrumentor/controllers/startlangdetection/source_controller.go diff --git a/instrumentor/controllers/startlangdetection/manager.go b/instrumentor/controllers/startlangdetection/manager.go index 2be2646cd..0528db3ae 100644 --- a/instrumentor/controllers/startlangdetection/manager.go +++ b/instrumentor/controllers/startlangdetection/manager.go @@ -1,12 +1,14 @@ package startlangdetection import ( - odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/predicate" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" ) func SetupWithManager(mgr ctrl.Manager) error { @@ -74,5 +76,17 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } + err = builder. + ControllerManagedBy(mgr). + Named("startlangdetection-source"). + For(&v1alpha1.Source{}). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) + if err != nil { + return err + } + return nil } diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go new file mode 100644 index 000000000..b0f907144 --- /dev/null +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -0,0 +1,31 @@ +package startlangdetection + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +type SourceReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (s *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var source v1alpha1.Source + err := s.Get(ctx, req.NamespacedName, &source) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + + err = requestOdigletsToCalculateRuntimeDetails(ctx, s.Client, instConfigName, req.Namespace, obj, s.Scheme) + return ctrl.Result{}, err +} From f4980274cb164e90c2ebdf25ea8e202f8f8ab596 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:37:02 -0500 Subject: [PATCH 015/259] Add finalizer for uninstrumentation --- .../startlangdetection/source_controller.go | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index b0f907144..fc742bdaa 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -6,26 +6,55 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) +var sourceFinalizer = "odigos.io/source-finalizer" + type SourceReconciler struct { client.Client Scheme *runtime.Scheme } -func (s *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - var source v1alpha1.Source - err := s.Get(ctx, req.NamespacedName, &source) +func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + source := &v1alpha1.Source{} + err := r.Get(ctx, req.NamespacedName, source) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } - instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // Deleted objects should be filtered in the event filter + return ctrl.Result{}, err + } + + if source.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { + controllerutil.AddFinalizer(source, sourceFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + + instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) + err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) + return ctrl.Result{}, err + } + } else { + // Source is being deleted + if controllerutil.ContainsFinalizer(source, sourceFinalizer) { + // TODO: delete resources + + controllerutil.RemoveFinalizer(source, sourceFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + } + } - err = requestOdigletsToCalculateRuntimeDetails(ctx, s.Client, instConfigName, req.Namespace, obj, s.Scheme) return ctrl.Result{}, err } From e5dfb908843828143837987a09d309ec681d6ed8 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:48:46 -0500 Subject: [PATCH 016/259] Delete InstrumentationConfig and remove Finalizer when Source is deleted --- .../startlangdetection/source_controller.go | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index fc742bdaa..fa292dc0c 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -4,6 +4,7 @@ import ( "context" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -29,9 +30,10 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) if err != nil { - // Deleted objects should be filtered in the event filter + // TODO: Deleted objects should be filtered in the event filter return ctrl.Result{}, err } + instConfigName := workload.CalculateWorkloadRuntimeObjectName(source.Spec.Workload.Name, source.Spec.Workload.Kind) if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { @@ -40,19 +42,29 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } - instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) return ctrl.Result{}, err } } else { // Source is being deleted if controllerutil.ContainsFinalizer(source, sourceFinalizer) { - // TODO: delete resources - + // Remove the finalizer first, because if the InstrumentationConfig is not found we + // will deadlock on the finalizer never getting removed. + // On the other hand, this could end up deleting a Source with an orphaned InstrumentationConfig. controllerutil.RemoveFinalizer(source, sourceFinalizer) if err := r.Update(ctx, source); err != nil { return ctrl.Result{}, err } + + instConfig := &v1alpha1.InstrumentationConfig{} + err = r.Client.Get(ctx, types.NamespacedName{Name: instConfigName, Namespace: req.Namespace}, instConfig) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + err = r.Client.Delete(ctx, instConfig) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } } } From 8f5738a242427e108e0a519470aaa08a1ab81034 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:52:32 -0500 Subject: [PATCH 017/259] Add Sources to Instrumentor ClusterRole --- cli/cmd/resources/instrumentor.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cli/cmd/resources/instrumentor.go b/cli/cmd/resources/instrumentor.go index 89e3c7d8e..ef6c68fda 100644 --- a/cli/cmd/resources/instrumentor.go +++ b/cli/cmd/resources/instrumentor.go @@ -191,6 +191,16 @@ func NewInstrumentorClusterRole() *rbacv1.ClusterRole { Resources: []string{"instrumentationconfigs"}, Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources"}, + Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, + }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources/finalizers"}, + Verbs: []string{"update"}, + }, }, } } From 3555a0f04454d6fbece7e5191b298664e7839f0b Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 14:11:24 -0500 Subject: [PATCH 018/259] Add workload labels to Source --- .../startlangdetection/source_controller.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index fa292dc0c..855fa4ca6 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -13,7 +13,13 @@ import ( "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -var sourceFinalizer = "odigos.io/source-finalizer" +var ( + sourceFinalizer = "odigos.io/source-finalizer" + + workloadNameLabel = "odigos.io/workload-name" + workloadNamespaceLabel = "odigos.io/workload-namespace" + workloadKindLabel = "odigos.io/workload-kind" +) type SourceReconciler struct { client.Client @@ -38,6 +44,14 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { controllerutil.AddFinalizer(source, sourceFinalizer) + + if source.Labels == nil { + source.Labels = make(map[string]string) + } + source.Labels[workloadNameLabel] = source.Spec.Workload.Name + source.Labels[workloadNamespaceLabel] = source.Spec.Workload.Namespace + source.Labels[workloadKindLabel] = string(source.Spec.Workload.Kind) + if err := r.Update(ctx, source); err != nil { return ctrl.Result{}, err } From dfb39467c3b2502c21dd80224f70acd369e7ce45 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:04:14 -0500 Subject: [PATCH 019/259] Update Uninstall command to delete Source finalizers --- Makefile | 7 ++++++- cli/cmd/uninstall.go | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 06f564c9d..67d0afb4b 100644 --- a/Makefile +++ b/Makefile @@ -200,6 +200,11 @@ cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) +.PHONY: cli-uninstall +cli-uninstall: + @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" + cd ./cli ; go run -tags=embed_manifests . uninstall + .PHONY: cli-upgrade cli-upgrade: @echo "Upgrading odigos from source. version: $(ODIGOS_CLI_VERSION)" @@ -255,4 +260,4 @@ dev-nop-destination: .PHONY: dev-add-backpressue-destination dev-backpressue-destination: - kubectl apply -f ./tests/backpressure-exporter.yaml \ No newline at end of file + kubectl apply -f ./tests/backpressure-exporter.yaml diff --git a/cli/cmd/uninstall.go b/cli/cmd/uninstall.go index c7998609f..608fa981f 100644 --- a/cli/cmd/uninstall.go +++ b/cli/cmd/uninstall.go @@ -387,6 +387,20 @@ func uninstallCRDs(ctx context.Context, cmd *cobra.Command, client *kube.Client, return err } + // Clear finalizers from Source objects so they can be uninstalled + sources, err := client.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) + for _, i := range sources.Items { + source, err := client.OdigosClient.Sources(i.Namespace).Get(ctx, i.Name, metav1.GetOptions{}) + if err != nil { + return err + } + source.SetFinalizers([]string{}) + _, err = client.OdigosClient.Sources(i.Namespace).Update(ctx, source, metav1.UpdateOptions{}) + if err != nil { + return err + } + } + for _, i := range list.Items { err = client.ApiExtensions.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, i.Name, metav1.DeleteOptions{}) if err != nil { From 596f22c6afad391bc3c4d60634614c4ff8e30567 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:25:29 -0500 Subject: [PATCH 020/259] Add logger to SourceReconciler --- .../controllers/startlangdetection/source_controller.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 855fa4ca6..6c8c510dd 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -8,6 +8,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -27,6 +28,8 @@ type SourceReconciler struct { } func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling Source object", "name", req.Name, "namespace", req.Namespace) source := &v1alpha1.Source{} err := r.Get(ctx, req.NamespacedName, source) if err != nil { From 7d3358e85f773cdc0e45c72aa2c0800515ef958d Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:42:26 -0500 Subject: [PATCH 021/259] Add Source checks to Instrumentor --- .../deleteinstrumentedapplication/common.go | 17 ++++++++++++- .../instrumentedapplication_controller.go | 24 +++++++++++++++---- .../workload_controllers.go | 17 ++++++++++++- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/instrumentor/controllers/deleteinstrumentedapplication/common.go b/instrumentor/controllers/deleteinstrumentedapplication/common.go index d70b9c2f8..9bfe973c6 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/common.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/common.go @@ -7,6 +7,7 @@ import ( "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -21,7 +22,21 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work } if instEffectiveEnabled { - return nil + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadObject.GetName(), + "odigos.io/workload-namespace": workloadObject.GetNamespace(), + "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, + }) + err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return err + } + if len(sourceList.Items) == 0 { + return nil + } } if err := deleteWorkloadInstrumentedApplication(ctx, kubeClient, workloadObject); err != nil { diff --git a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go index a3c504cca..71d1b65c1 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go @@ -20,11 +20,12 @@ import ( "context" "fmt" + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -90,9 +91,24 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c } if !instEffectiveEnabled { - logger.Info("Deleting instrumented application for non-enabled workload") - err := r.Client.Delete(ctx, &instrumentedApplication) - return ctrl.Result{}, client.IgnoreNotFound(err) + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadObject.GetName(), + "odigos.io/workload-namespace": workloadObject.GetNamespace(), + "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, + }) + err := r.Client.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return ctrl.Result{}, err + } + if len(sourceList.Items) == 0 { + logger.Info("Deleting instrumented application for non-enabled workload") + err := r.Client.Delete(ctx, &instrumentedApplication) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + } return ctrl.Result{}, nil diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index b8085d4dc..a197780b0 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -4,6 +4,7 @@ import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/yaml" "k8s.io/apimachinery/pkg/runtime" @@ -59,7 +60,21 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor } if !instrumented { - return ctrl.Result{}, nil + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": obj.GetName(), + "odigos.io/workload-namespace": obj.GetNamespace(), + "odigos.io/workload-kind": obj.GetObjectKind().GroupVersionKind().Kind, + }) + err := k8sClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return ctrl.Result{}, err + } + if len(sourceList.Items) == 0 { + return ctrl.Result{}, nil + } } err = requestOdigletsToCalculateRuntimeDetails(ctx, k8sClient, instConfigName, req.Namespace, obj, scheme) From 69059b0d8818c51334cc91b4d480289a1bdda7df Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 11:14:30 -0500 Subject: [PATCH 022/259] Add GetSourceListForWorkload function --- api/odigos/v1alpha1/source_types.go | 21 +++++++++++++++++++ .../deleteinstrumentedapplication/common.go | 11 ++-------- .../instrumentedapplication_controller.go | 12 ++--------- .../workload_controllers.go | 11 ++-------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index 3dd31ef63..f3969810d 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -17,7 +17,11 @@ limitations under the License. package v1alpha1 import ( + "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) @@ -62,6 +66,23 @@ type SourceList struct { Items []Source `json:"items"` } +// GetSourceListForWorkload returns a SourceList of all Sources that have matching +// workload name, namespace, and kind labels for an object. In theory, this should only +// ever return a list with 0 or 1 items, but due diligence should handle unexpected cases. +func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (*SourceList, error) { + sourceList := &SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": obj.GetName(), + "odigos.io/workload-namespace": obj.GetNamespace(), + "odigos.io/workload-kind": obj.GetObjectKind().GroupVersionKind().Kind, + }) + err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return nil, err + } + return sourceList, nil +} + func init() { SchemeBuilder.Register(&Source{}, &SourceList{}) } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/common.go b/instrumentor/controllers/deleteinstrumentedapplication/common.go index 9bfe973c6..ef5ee898f 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/common.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/common.go @@ -3,11 +3,11 @@ package deleteinstrumentedapplication import ( "context" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -23,14 +23,7 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work if instEffectiveEnabled { // Check if a Source object exists for this workload - // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) - sourceList := &odigosv1.SourceList{} - selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": workloadObject.GetName(), - "odigos.io/workload-namespace": workloadObject.GetNamespace(), - "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, - }) - err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, kubeClient, workloadObject) if err != nil { return err } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go index 71d1b65c1..52866a90b 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go @@ -20,12 +20,12 @@ import ( "context" "fmt" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -92,14 +92,7 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c if !instEffectiveEnabled { // Check if a Source object exists for this workload - // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) - sourceList := &odigosv1.SourceList{} - selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": workloadObject.GetName(), - "odigos.io/workload-namespace": workloadObject.GetNamespace(), - "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, - }) - err := r.Client.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, r.Client, workloadObject) if err != nil { return ctrl.Result{}, err } @@ -108,7 +101,6 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c err := r.Client.Delete(ctx, &instrumentedApplication) return ctrl.Result{}, client.IgnoreNotFound(err) } - } return ctrl.Result{}, nil diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index a197780b0..0ef0e7489 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -4,13 +4,13 @@ import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/yaml" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" "k8s.io/apimachinery/pkg/types" @@ -61,14 +61,7 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor if !instrumented { // Check if a Source object exists for this workload - // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) - sourceList := &odigosv1.SourceList{} - selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": obj.GetName(), - "odigos.io/workload-namespace": obj.GetNamespace(), - "odigos.io/workload-kind": obj.GetObjectKind().GroupVersionKind().Kind, - }) - err := k8sClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, k8sClient, obj) if err != nil { return ctrl.Result{}, err } From 5ecfe41dba7ffdd7d9f7a099a43d1c27397d5e9f Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 13:09:24 -0500 Subject: [PATCH 023/259] Add SourceReconciler to deleteinstrumentedapplication controller --- .../deleteinstrumentedapplication/manager.go | 13 +++ .../source_controller.go | 95 +++++++++++++++++++ .../startlangdetection/source_controller.go | 4 + 3 files changed, 112 insertions(+) create mode 100644 instrumentor/controllers/deleteinstrumentedapplication/source_controller.go diff --git a/instrumentor/controllers/deleteinstrumentedapplication/manager.go b/instrumentor/controllers/deleteinstrumentedapplication/manager.go index eb99d5347..e107fd35a 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/manager.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/manager.go @@ -74,6 +74,19 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } + err = builder. + ControllerManagedBy(mgr). + Named("deleteinstrumentedapplication-source"). + WithEventFilter(&SourceDeletedPredicate{}). + For(&odigosv1.Source{}). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) +if err != nil { + return err +} + return nil } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go new file mode 100644 index 000000000..0f611c6ef --- /dev/null +++ b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go @@ -0,0 +1,95 @@ +/* +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 deleteinstrumentedapplication + +import ( + "context" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// Added by startlangdetection controller when Source is created +var instrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" + +type SourceDeletedPredicate struct{} + +func (i *SourceDeletedPredicate) Create(_ event.CreateEvent) bool { + return false +} + +func (i *SourceDeletedPredicate) Update(_ event.UpdateEvent) bool { + // We are actually looking for Update events that add a DeletionTimestamp + // This is so we can still get the workload from the Source object and remove the finalizer + // Then actual deletion of the Source will proceed + return true +} + +func (i *SourceDeletedPredicate) Delete(_ event.DeleteEvent) bool { + return true +} + +func (i *SourceDeletedPredicate) Generic(_ event.GenericEvent) bool { + return false +} + +type SourceReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling Deleted Source object", "name", req.Name, "namespace", req.Namespace) + + source := &v1alpha1.Source{} + err := r.Get(ctx, req.NamespacedName, source) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + if !source.DeletionTimestamp.IsZero() { + logger.Info("Reconciling workload for deleted Source object", "name", req.Name, "namespace", req.Namespace) + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // TODO: Deleted objects should be filtered in the event filter + return ctrl.Result{}, err + } + + if controllerutil.ContainsFinalizer(source, instrumentedApplicationFinalizer) { + controllerutil.RemoveFinalizer(source, instrumentedApplicationFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + } + + err = reconcileWorkloadObject(ctx, r.Client, obj) + if err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil +} diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 6c8c510dd..6894e7a55 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -16,6 +16,8 @@ import ( var ( sourceFinalizer = "odigos.io/source-finalizer" + // TODO: Needed until InstrumentedApplication is removed + instrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" workloadNameLabel = "odigos.io/workload-name" workloadNamespaceLabel = "odigos.io/workload-namespace" @@ -47,6 +49,8 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { controllerutil.AddFinalizer(source, sourceFinalizer) + // Removed by deleteinstrumentedapplication controller + controllerutil.AddFinalizer(source, instrumentedApplicationFinalizer) if source.Labels == nil { source.Labels = make(map[string]string) From 3dd7ed55e330a433e19662717afdb90aa6c21bd6 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:26:27 -0500 Subject: [PATCH 024/259] Add SourceReconciler and create InstrumentationConfig --- .../controllers/startlangdetection/manager.go | 16 +++++++++- .../startlangdetection/source_controller.go | 31 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 instrumentor/controllers/startlangdetection/source_controller.go diff --git a/instrumentor/controllers/startlangdetection/manager.go b/instrumentor/controllers/startlangdetection/manager.go index 2be2646cd..0528db3ae 100644 --- a/instrumentor/controllers/startlangdetection/manager.go +++ b/instrumentor/controllers/startlangdetection/manager.go @@ -1,12 +1,14 @@ package startlangdetection import ( - odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/predicate" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" ) func SetupWithManager(mgr ctrl.Manager) error { @@ -74,5 +76,17 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } + err = builder. + ControllerManagedBy(mgr). + Named("startlangdetection-source"). + For(&v1alpha1.Source{}). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) + if err != nil { + return err + } + return nil } diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go new file mode 100644 index 000000000..b0f907144 --- /dev/null +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -0,0 +1,31 @@ +package startlangdetection + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +type SourceReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (s *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var source v1alpha1.Source + err := s.Get(ctx, req.NamespacedName, &source) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + + err = requestOdigletsToCalculateRuntimeDetails(ctx, s.Client, instConfigName, req.Namespace, obj, s.Scheme) + return ctrl.Result{}, err +} From dae2bd9e0447201ceca2624837930ea09adbccee Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:37:02 -0500 Subject: [PATCH 025/259] Add finalizer for uninstrumentation --- .../startlangdetection/source_controller.go | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index b0f907144..fc742bdaa 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -6,26 +6,55 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) +var sourceFinalizer = "odigos.io/source-finalizer" + type SourceReconciler struct { client.Client Scheme *runtime.Scheme } -func (s *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - var source v1alpha1.Source - err := s.Get(ctx, req.NamespacedName, &source) +func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + source := &v1alpha1.Source{} + err := r.Get(ctx, req.NamespacedName, source) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } - instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // Deleted objects should be filtered in the event filter + return ctrl.Result{}, err + } + + if source.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { + controllerutil.AddFinalizer(source, sourceFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + + instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) + err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) + return ctrl.Result{}, err + } + } else { + // Source is being deleted + if controllerutil.ContainsFinalizer(source, sourceFinalizer) { + // TODO: delete resources + + controllerutil.RemoveFinalizer(source, sourceFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + } + } - err = requestOdigletsToCalculateRuntimeDetails(ctx, s.Client, instConfigName, req.Namespace, obj, s.Scheme) return ctrl.Result{}, err } From 9ba9b57f614d233f81ccc0d7d173c10076714223 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:48:46 -0500 Subject: [PATCH 026/259] Delete InstrumentationConfig and remove Finalizer when Source is deleted --- .../startlangdetection/source_controller.go | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index fc742bdaa..fa292dc0c 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -4,6 +4,7 @@ import ( "context" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -29,9 +30,10 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) if err != nil { - // Deleted objects should be filtered in the event filter + // TODO: Deleted objects should be filtered in the event filter return ctrl.Result{}, err } + instConfigName := workload.CalculateWorkloadRuntimeObjectName(source.Spec.Workload.Name, source.Spec.Workload.Kind) if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { @@ -40,19 +42,29 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } - instConfigName := workload.CalculateWorkloadRuntimeObjectName(req.Name, source.Spec.Workload.Kind) err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) return ctrl.Result{}, err } } else { // Source is being deleted if controllerutil.ContainsFinalizer(source, sourceFinalizer) { - // TODO: delete resources - + // Remove the finalizer first, because if the InstrumentationConfig is not found we + // will deadlock on the finalizer never getting removed. + // On the other hand, this could end up deleting a Source with an orphaned InstrumentationConfig. controllerutil.RemoveFinalizer(source, sourceFinalizer) if err := r.Update(ctx, source); err != nil { return ctrl.Result{}, err } + + instConfig := &v1alpha1.InstrumentationConfig{} + err = r.Client.Get(ctx, types.NamespacedName{Name: instConfigName, Namespace: req.Namespace}, instConfig) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + err = r.Client.Delete(ctx, instConfig) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } } } From df8cc4f672b7ca7a9938fe96439c1ccc693c0aa7 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:52:32 -0500 Subject: [PATCH 027/259] Add Sources to Instrumentor ClusterRole --- cli/cmd/resources/instrumentor.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cli/cmd/resources/instrumentor.go b/cli/cmd/resources/instrumentor.go index 89e3c7d8e..ef6c68fda 100644 --- a/cli/cmd/resources/instrumentor.go +++ b/cli/cmd/resources/instrumentor.go @@ -191,6 +191,16 @@ func NewInstrumentorClusterRole() *rbacv1.ClusterRole { Resources: []string{"instrumentationconfigs"}, Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources"}, + Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, + }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources/finalizers"}, + Verbs: []string{"update"}, + }, }, } } From 6bfeb1d8dc3dcc89220bcade9a30615e18bf12fc Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 14:11:24 -0500 Subject: [PATCH 028/259] Add workload labels to Source --- .../startlangdetection/source_controller.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index fa292dc0c..855fa4ca6 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -13,7 +13,13 @@ import ( "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -var sourceFinalizer = "odigos.io/source-finalizer" +var ( + sourceFinalizer = "odigos.io/source-finalizer" + + workloadNameLabel = "odigos.io/workload-name" + workloadNamespaceLabel = "odigos.io/workload-namespace" + workloadKindLabel = "odigos.io/workload-kind" +) type SourceReconciler struct { client.Client @@ -38,6 +44,14 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { controllerutil.AddFinalizer(source, sourceFinalizer) + + if source.Labels == nil { + source.Labels = make(map[string]string) + } + source.Labels[workloadNameLabel] = source.Spec.Workload.Name + source.Labels[workloadNamespaceLabel] = source.Spec.Workload.Namespace + source.Labels[workloadKindLabel] = string(source.Spec.Workload.Kind) + if err := r.Update(ctx, source); err != nil { return ctrl.Result{}, err } From 312854c1cb719329067156d9d93f1a3841b9019b Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:04:14 -0500 Subject: [PATCH 029/259] Update Uninstall command to delete Source finalizers --- Makefile | 7 ++++++- cli/cmd/uninstall.go | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 06f564c9d..67d0afb4b 100644 --- a/Makefile +++ b/Makefile @@ -200,6 +200,11 @@ cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) +.PHONY: cli-uninstall +cli-uninstall: + @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" + cd ./cli ; go run -tags=embed_manifests . uninstall + .PHONY: cli-upgrade cli-upgrade: @echo "Upgrading odigos from source. version: $(ODIGOS_CLI_VERSION)" @@ -255,4 +260,4 @@ dev-nop-destination: .PHONY: dev-add-backpressue-destination dev-backpressue-destination: - kubectl apply -f ./tests/backpressure-exporter.yaml \ No newline at end of file + kubectl apply -f ./tests/backpressure-exporter.yaml diff --git a/cli/cmd/uninstall.go b/cli/cmd/uninstall.go index c7998609f..608fa981f 100644 --- a/cli/cmd/uninstall.go +++ b/cli/cmd/uninstall.go @@ -387,6 +387,20 @@ func uninstallCRDs(ctx context.Context, cmd *cobra.Command, client *kube.Client, return err } + // Clear finalizers from Source objects so they can be uninstalled + sources, err := client.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) + for _, i := range sources.Items { + source, err := client.OdigosClient.Sources(i.Namespace).Get(ctx, i.Name, metav1.GetOptions{}) + if err != nil { + return err + } + source.SetFinalizers([]string{}) + _, err = client.OdigosClient.Sources(i.Namespace).Update(ctx, source, metav1.UpdateOptions{}) + if err != nil { + return err + } + } + for _, i := range list.Items { err = client.ApiExtensions.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, i.Name, metav1.DeleteOptions{}) if err != nil { From 9528b4b19686c9c9af30f3c7e92b4872ec8da6b5 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:25:29 -0500 Subject: [PATCH 030/259] Add logger to SourceReconciler --- .../controllers/startlangdetection/source_controller.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 855fa4ca6..6c8c510dd 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -8,6 +8,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -27,6 +28,8 @@ type SourceReconciler struct { } func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling Source object", "name", req.Name, "namespace", req.Namespace) source := &v1alpha1.Source{} err := r.Get(ctx, req.NamespacedName, source) if err != nil { From 378d0bf4b741e4482be74b9eececef2fb7620b3f Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 15:42:26 -0500 Subject: [PATCH 031/259] Add Source checks to Instrumentor --- .../deleteinstrumentedapplication/common.go | 17 ++++++++++++- .../instrumentedapplication_controller.go | 24 +++++++++++++++---- .../workload_controllers.go | 17 ++++++++++++- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/instrumentor/controllers/deleteinstrumentedapplication/common.go b/instrumentor/controllers/deleteinstrumentedapplication/common.go index d70b9c2f8..9bfe973c6 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/common.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/common.go @@ -7,6 +7,7 @@ import ( "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -21,7 +22,21 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work } if instEffectiveEnabled { - return nil + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadObject.GetName(), + "odigos.io/workload-namespace": workloadObject.GetNamespace(), + "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, + }) + err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return err + } + if len(sourceList.Items) == 0 { + return nil + } } if err := deleteWorkloadInstrumentedApplication(ctx, kubeClient, workloadObject); err != nil { diff --git a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go index a3c504cca..71d1b65c1 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go @@ -20,11 +20,12 @@ import ( "context" "fmt" + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -90,9 +91,24 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c } if !instEffectiveEnabled { - logger.Info("Deleting instrumented application for non-enabled workload") - err := r.Client.Delete(ctx, &instrumentedApplication) - return ctrl.Result{}, client.IgnoreNotFound(err) + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadObject.GetName(), + "odigos.io/workload-namespace": workloadObject.GetNamespace(), + "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, + }) + err := r.Client.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return ctrl.Result{}, err + } + if len(sourceList.Items) == 0 { + logger.Info("Deleting instrumented application for non-enabled workload") + err := r.Client.Delete(ctx, &instrumentedApplication) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + } return ctrl.Result{}, nil diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index b8085d4dc..a197780b0 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -4,6 +4,7 @@ import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/yaml" "k8s.io/apimachinery/pkg/runtime" @@ -59,7 +60,21 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor } if !instrumented { - return ctrl.Result{}, nil + // Check if a Source object exists for this workload + // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) + sourceList := &odigosv1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": obj.GetName(), + "odigos.io/workload-namespace": obj.GetNamespace(), + "odigos.io/workload-kind": obj.GetObjectKind().GroupVersionKind().Kind, + }) + err := k8sClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return ctrl.Result{}, err + } + if len(sourceList.Items) == 0 { + return ctrl.Result{}, nil + } } err = requestOdigletsToCalculateRuntimeDetails(ctx, k8sClient, instConfigName, req.Namespace, obj, scheme) From adfc696bf29361ab81608f5e854087edbd8bf98b Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 11:14:30 -0500 Subject: [PATCH 032/259] Add GetSourceListForWorkload function --- api/odigos/v1alpha1/source_types.go | 21 +++++++++++++++++++ .../deleteinstrumentedapplication/common.go | 11 ++-------- .../instrumentedapplication_controller.go | 12 ++--------- .../workload_controllers.go | 11 ++-------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index 3dd31ef63..f3969810d 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -17,7 +17,11 @@ limitations under the License. package v1alpha1 import ( + "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) @@ -62,6 +66,23 @@ type SourceList struct { Items []Source `json:"items"` } +// GetSourceListForWorkload returns a SourceList of all Sources that have matching +// workload name, namespace, and kind labels for an object. In theory, this should only +// ever return a list with 0 or 1 items, but due diligence should handle unexpected cases. +func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (*SourceList, error) { + sourceList := &SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": obj.GetName(), + "odigos.io/workload-namespace": obj.GetNamespace(), + "odigos.io/workload-kind": obj.GetObjectKind().GroupVersionKind().Kind, + }) + err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return nil, err + } + return sourceList, nil +} + func init() { SchemeBuilder.Register(&Source{}, &SourceList{}) } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/common.go b/instrumentor/controllers/deleteinstrumentedapplication/common.go index 9bfe973c6..ef5ee898f 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/common.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/common.go @@ -3,11 +3,11 @@ package deleteinstrumentedapplication import ( "context" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -23,14 +23,7 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work if instEffectiveEnabled { // Check if a Source object exists for this workload - // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) - sourceList := &odigosv1.SourceList{} - selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": workloadObject.GetName(), - "odigos.io/workload-namespace": workloadObject.GetNamespace(), - "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, - }) - err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, kubeClient, workloadObject) if err != nil { return err } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go index 71d1b65c1..52866a90b 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go @@ -20,12 +20,12 @@ import ( "context" "fmt" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -92,14 +92,7 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c if !instEffectiveEnabled { // Check if a Source object exists for this workload - // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) - sourceList := &odigosv1.SourceList{} - selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": workloadObject.GetName(), - "odigos.io/workload-namespace": workloadObject.GetNamespace(), - "odigos.io/workload-kind": workloadObject.GetObjectKind().GroupVersionKind().Kind, - }) - err := r.Client.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, r.Client, workloadObject) if err != nil { return ctrl.Result{}, err } @@ -108,7 +101,6 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c err := r.Client.Delete(ctx, &instrumentedApplication) return ctrl.Result{}, client.IgnoreNotFound(err) } - } return ctrl.Result{}, nil diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index a197780b0..0ef0e7489 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -4,13 +4,13 @@ import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/yaml" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" "k8s.io/apimachinery/pkg/types" @@ -61,14 +61,7 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor if !instrumented { // Check if a Source object exists for this workload - // TODO: Move this to IsWorkloadInstrumentationEffectiveEnabled (creates import loop right now) - sourceList := &odigosv1.SourceList{} - selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": obj.GetName(), - "odigos.io/workload-namespace": obj.GetNamespace(), - "odigos.io/workload-kind": obj.GetObjectKind().GroupVersionKind().Kind, - }) - err := k8sClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, k8sClient, obj) if err != nil { return ctrl.Result{}, err } From 32c3afe677a34a61e1d90262ad56cb369c85761f Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 13:09:24 -0500 Subject: [PATCH 033/259] Add SourceReconciler to deleteinstrumentedapplication controller --- .../deleteinstrumentedapplication/manager.go | 13 +++ .../source_controller.go | 95 +++++++++++++++++++ .../startlangdetection/source_controller.go | 4 + 3 files changed, 112 insertions(+) create mode 100644 instrumentor/controllers/deleteinstrumentedapplication/source_controller.go diff --git a/instrumentor/controllers/deleteinstrumentedapplication/manager.go b/instrumentor/controllers/deleteinstrumentedapplication/manager.go index eb99d5347..e107fd35a 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/manager.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/manager.go @@ -74,6 +74,19 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } + err = builder. + ControllerManagedBy(mgr). + Named("deleteinstrumentedapplication-source"). + WithEventFilter(&SourceDeletedPredicate{}). + For(&odigosv1.Source{}). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) +if err != nil { + return err +} + return nil } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go new file mode 100644 index 000000000..0f611c6ef --- /dev/null +++ b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go @@ -0,0 +1,95 @@ +/* +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 deleteinstrumentedapplication + +import ( + "context" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// Added by startlangdetection controller when Source is created +var instrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" + +type SourceDeletedPredicate struct{} + +func (i *SourceDeletedPredicate) Create(_ event.CreateEvent) bool { + return false +} + +func (i *SourceDeletedPredicate) Update(_ event.UpdateEvent) bool { + // We are actually looking for Update events that add a DeletionTimestamp + // This is so we can still get the workload from the Source object and remove the finalizer + // Then actual deletion of the Source will proceed + return true +} + +func (i *SourceDeletedPredicate) Delete(_ event.DeleteEvent) bool { + return true +} + +func (i *SourceDeletedPredicate) Generic(_ event.GenericEvent) bool { + return false +} + +type SourceReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling Deleted Source object", "name", req.Name, "namespace", req.Namespace) + + source := &v1alpha1.Source{} + err := r.Get(ctx, req.NamespacedName, source) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + if !source.DeletionTimestamp.IsZero() { + logger.Info("Reconciling workload for deleted Source object", "name", req.Name, "namespace", req.Namespace) + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // TODO: Deleted objects should be filtered in the event filter + return ctrl.Result{}, err + } + + if controllerutil.ContainsFinalizer(source, instrumentedApplicationFinalizer) { + controllerutil.RemoveFinalizer(source, instrumentedApplicationFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + } + + err = reconcileWorkloadObject(ctx, r.Client, obj) + if err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil +} diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 6c8c510dd..6894e7a55 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -16,6 +16,8 @@ import ( var ( sourceFinalizer = "odigos.io/source-finalizer" + // TODO: Needed until InstrumentedApplication is removed + instrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" workloadNameLabel = "odigos.io/workload-name" workloadNamespaceLabel = "odigos.io/workload-namespace" @@ -47,6 +49,8 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { controllerutil.AddFinalizer(source, sourceFinalizer) + // Removed by deleteinstrumentedapplication controller + controllerutil.AddFinalizer(source, instrumentedApplicationFinalizer) if source.Labels == nil { source.Labels = make(map[string]string) From b44f4421615d247e2a5a2e2c7d09ab891efee8e3 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 13:43:11 -0500 Subject: [PATCH 034/259] Enable build and e2e workflows for feature branch --- .github/workflows/build.yaml | 3 ++- .github/workflows/e2e.yaml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fc8e2f786..7a2055333 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,6 +6,7 @@ on: branches: - main - stable + - feature/source-crd jobs: build-autoscaler: @@ -158,4 +159,4 @@ jobs: - name: Test procdiscovery module working-directory: ./procdiscovery run: | - go test -v ./... \ No newline at end of file + go test -v ./... diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index b1ca6702c..d531d8edf 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -6,6 +6,7 @@ on: branches: - main - stable + - feature/source-crd concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} From ef8f196feb99f05d1804e4971f66d4f4b44ecda7 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 15:58:17 -0500 Subject: [PATCH 035/259] Add Instrumentor Sources RBAC to Helm chart --- .../templates/instrumentor/clusterrole.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/helm/odigos/templates/instrumentor/clusterrole.yaml b/helm/odigos/templates/instrumentor/clusterrole.yaml index d1d479f72..a61c654fb 100644 --- a/helm/odigos/templates/instrumentor/clusterrole.yaml +++ b/helm/odigos/templates/instrumentor/clusterrole.yaml @@ -78,3 +78,21 @@ rules: - patch - update - watch + - apiGroups: + - odigos.io + resources: + - sources + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - odigos.io + resources: + - sources/finalizers + verbs: + - update From 9d7d46c94f3edb6531b723585d2ba4263e8a66d3 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Tue, 24 Dec 2024 09:14:41 +0200 Subject: [PATCH 036/259] chore: init group source to destination --- .../crd/bases/odigos.io_destinations.yaml | 28 +++++++++ api/config/crd/bases/odigos.io_sources.yaml | 6 ++ .../odigos/v1alpha1/destinationspec.go | 19 ++++-- .../odigos/v1alpha1/sourceselector.go | 60 +++++++++++++++++++ .../odigos/v1alpha1/sourcespec.go | 11 ++++ .../odigos/applyconfiguration/utils.go | 2 + api/odigos/v1alpha1/destination_types.go | 21 +++++++ api/odigos/v1alpha1/source_types.go | 4 ++ api/odigos/v1alpha1/zz_generated.deepcopy.go | 37 +++++++++++- .../crds/odigos.io_destinations.yaml | 28 +++++++++ .../templates/crds/odigos.io_sources.yaml | 6 ++ 11 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourceselector.go diff --git a/api/config/crd/bases/odigos.io_destinations.yaml b/api/config/crd/bases/odigos.io_destinations.yaml index 2678954d0..892fe8e9c 100644 --- a/api/config/crd/bases/odigos.io_destinations.yaml +++ b/api/config/crd/bases/odigos.io_destinations.yaml @@ -72,6 +72,34 @@ spec: - METRICS type: string type: array + sourceSelector: + description: |- + SourceSelector defines which sources can send data to this destination. + If not specified, defaults to "all". + properties: + groups: + description: Groups specifies the groups for "groups" mode. + items: + type: string + type: array + mode: + description: |- + Mode can be "all", "namespaces", or "groups". + Determines how sources are selected for this destination. + enum: + - all + - namespaces + - groups + type: string + namespaces: + description: Namespaces specifies the namespaces for "namespaces" + mode. + items: + type: string + type: array + required: + - mode + type: object type: type: string required: diff --git a/api/config/crd/bases/odigos.io_sources.yaml b/api/config/crd/bases/odigos.io_sources.yaml index cda0e2b88..26c179d9f 100644 --- a/api/config/crd/bases/odigos.io_sources.yaml +++ b/api/config/crd/bases/odigos.io_sources.yaml @@ -51,6 +51,12 @@ spec: type: object spec: properties: + groups: + description: Groups represents the logical group(s) this source belongs + to. + items: + type: string + type: array workload: description: |- Workload represents the workload or namespace to be instrumented. diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/destinationspec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/destinationspec.go index 9ad8f26dd..41e75f13f 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/destinationspec.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/destinationspec.go @@ -25,11 +25,12 @@ import ( // DestinationSpecApplyConfiguration represents a declarative configuration of the DestinationSpec type for use // with apply. type DestinationSpecApplyConfiguration struct { - Type *common.DestinationType `json:"type,omitempty"` - DestinationName *string `json:"destinationName,omitempty"` - Data map[string]string `json:"data,omitempty"` - SecretRef *v1.LocalObjectReference `json:"secretRef,omitempty"` - Signals []common.ObservabilitySignal `json:"signals,omitempty"` + Type *common.DestinationType `json:"type,omitempty"` + DestinationName *string `json:"destinationName,omitempty"` + Data map[string]string `json:"data,omitempty"` + SecretRef *v1.LocalObjectReference `json:"secretRef,omitempty"` + Signals []common.ObservabilitySignal `json:"signals,omitempty"` + SourceSelector *SourceSelectorApplyConfiguration `json:"sourceSelector,omitempty"` } // DestinationSpecApplyConfiguration constructs a declarative configuration of the DestinationSpec type for use with @@ -85,3 +86,11 @@ func (b *DestinationSpecApplyConfiguration) WithSignals(values ...common.Observa } return b } + +// WithSourceSelector sets the SourceSelector 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 SourceSelector field is set to the value of the last call. +func (b *DestinationSpecApplyConfiguration) WithSourceSelector(value *SourceSelectorApplyConfiguration) *DestinationSpecApplyConfiguration { + b.SourceSelector = value + return b +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourceselector.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourceselector.go new file mode 100644 index 000000000..ab40e136d --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourceselector.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 + +// SourceSelectorApplyConfiguration represents a declarative configuration of the SourceSelector type for use +// with apply. +type SourceSelectorApplyConfiguration struct { + Mode *string `json:"mode,omitempty"` + Namespaces []string `json:"namespaces,omitempty"` + Groups []string `json:"groups,omitempty"` +} + +// SourceSelectorApplyConfiguration constructs a declarative configuration of the SourceSelector type for use with +// apply. +func SourceSelector() *SourceSelectorApplyConfiguration { + return &SourceSelectorApplyConfiguration{} +} + +// WithMode sets the Mode 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 Mode field is set to the value of the last call. +func (b *SourceSelectorApplyConfiguration) WithMode(value string) *SourceSelectorApplyConfiguration { + b.Mode = &value + return b +} + +// WithNamespaces adds the given value to the Namespaces 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 Namespaces field. +func (b *SourceSelectorApplyConfiguration) WithNamespaces(values ...string) *SourceSelectorApplyConfiguration { + for i := range values { + b.Namespaces = append(b.Namespaces, values[i]) + } + return b +} + +// WithGroups adds the given value to the Groups 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 Groups field. +func (b *SourceSelectorApplyConfiguration) WithGroups(values ...string) *SourceSelectorApplyConfiguration { + for i := range values { + b.Groups = append(b.Groups, values[i]) + } + return b +} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go index ae7774ba8..467f9a483 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go @@ -25,6 +25,7 @@ import ( // with apply. type SourceSpecApplyConfiguration struct { Workload *workload.PodWorkload `json:"workload,omitempty"` + Groups []string `json:"groups,omitempty"` } // SourceSpecApplyConfiguration constructs a declarative configuration of the SourceSpec type for use with @@ -40,3 +41,13 @@ func (b *SourceSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) b.Workload = &value return b } + +// WithGroups adds the given value to the Groups 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 Groups field. +func (b *SourceSpecApplyConfiguration) WithGroups(values ...string) *SourceSpecApplyConfiguration { + for i := range values { + b.Groups = append(b.Groups, values[i]) + } + return b +} diff --git a/api/generated/odigos/applyconfiguration/utils.go b/api/generated/odigos/applyconfiguration/utils.go index 4d87e42fc..880ee0384 100644 --- a/api/generated/odigos/applyconfiguration/utils.go +++ b/api/generated/odigos/applyconfiguration/utils.go @@ -115,6 +115,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.SdkConfigApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Source"): return &odigosv1alpha1.SourceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SourceSelector"): + return &odigosv1alpha1.SourceSelectorApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SourceSpec"): return &odigosv1alpha1.SourceSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SourceStatus"): diff --git a/api/odigos/v1alpha1/destination_types.go b/api/odigos/v1alpha1/destination_types.go index 2094b80ea..2dfbd64b9 100644 --- a/api/odigos/v1alpha1/destination_types.go +++ b/api/odigos/v1alpha1/destination_types.go @@ -30,6 +30,27 @@ type DestinationSpec struct { Data map[string]string `json:"data"` SecretRef *v1.LocalObjectReference `json:"secretRef,omitempty"` Signals []common.ObservabilitySignal `json:"signals"` + + // SourceSelector defines which sources can send data to this destination. + // If not specified, defaults to "all". + // +optional + SourceSelector *SourceSelector `json:"sourceSelector,omitempty"` +} + +// SourceSelector defines the criteria for selecting sources. +type SourceSelector struct { + // Mode can be "all", "namespaces", or "groups". + // Determines how sources are selected for this destination. + // +kubebuilder:validation:Enum=all;namespaces;groups + Mode string `json:"mode"` + + // Namespaces specifies the namespaces for "namespaces" mode. + // +optional + Namespaces []string `json:"namespaces,omitempty"` + + // Groups specifies the groups for "groups" mode. + // +optional + Groups []string `json:"groups,omitempty"` } // DestinationStatus defines the observed state of Destination diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index 10886b2a0..1f00e1d98 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -44,6 +44,10 @@ type SourceSpec struct { // This field is required upon creation and cannot be modified. // +kubebuilder:validation:Required Workload workload.PodWorkload `json:"workload"` + + // Groups represents the logical group(s) this source belongs to. + // +optional + Groups []string `json:"groups,omitempty"` } type SourceStatus struct { diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 96b4a7d74..89c907a3f 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -315,6 +315,11 @@ func (in *DestinationSpec) DeepCopyInto(out *DestinationSpec) { *out = make([]common.ObservabilitySignal, len(*in)) copy(*out, *in) } + if in.SourceSelector != nil { + in, out := &in.SourceSelector, &out.SourceSelector + *out = new(SourceSelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationSpec. @@ -1276,7 +1281,7 @@ func (in *Source) DeepCopyInto(out *Source) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -1330,10 +1335,40 @@ func (in *SourceList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceSelector) DeepCopyInto(out *SourceSelector) { + *out = *in + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Groups != nil { + in, out := &in.Groups, &out.Groups + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSelector. +func (in *SourceSelector) DeepCopy() *SourceSelector { + if in == nil { + return nil + } + out := new(SourceSelector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { *out = *in out.Workload = in.Workload + if in.Groups != nil { + in, out := &in.Groups, &out.Groups + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. diff --git a/helm/odigos/templates/crds/odigos.io_destinations.yaml b/helm/odigos/templates/crds/odigos.io_destinations.yaml index 2678954d0..892fe8e9c 100644 --- a/helm/odigos/templates/crds/odigos.io_destinations.yaml +++ b/helm/odigos/templates/crds/odigos.io_destinations.yaml @@ -72,6 +72,34 @@ spec: - METRICS type: string type: array + sourceSelector: + description: |- + SourceSelector defines which sources can send data to this destination. + If not specified, defaults to "all". + properties: + groups: + description: Groups specifies the groups for "groups" mode. + items: + type: string + type: array + mode: + description: |- + Mode can be "all", "namespaces", or "groups". + Determines how sources are selected for this destination. + enum: + - all + - namespaces + - groups + type: string + namespaces: + description: Namespaces specifies the namespaces for "namespaces" + mode. + items: + type: string + type: array + required: + - mode + type: object type: type: string required: diff --git a/helm/odigos/templates/crds/odigos.io_sources.yaml b/helm/odigos/templates/crds/odigos.io_sources.yaml index cda0e2b88..26c179d9f 100644 --- a/helm/odigos/templates/crds/odigos.io_sources.yaml +++ b/helm/odigos/templates/crds/odigos.io_sources.yaml @@ -51,6 +51,12 @@ spec: type: object spec: properties: + groups: + description: Groups represents the logical group(s) this source belongs + to. + items: + type: string + type: array workload: description: |- Workload represents the workload or namespace to be instrumented. From fc4280d0a22792cdcab8a3f48da860d01632268d Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 10:08:11 +0200 Subject: [PATCH 037/259] feat: read & display "conditions" for actions --- frontend/graph/conversions.go | 26 + frontend/graph/generated.go | 809 +++++++++++------- frontend/graph/model/models_gen.go | 23 +- frontend/graph/schema.graphqls | 13 +- frontend/graph/schema.resolvers.go | 49 +- .../main/actions/action-drawer/index.tsx | 13 +- .../sources/source-drawer-container/index.tsx | 18 +- .../graphql/queries/compute-platform.ts | 13 +- frontend/webapp/types/actions.ts | 6 +- frontend/webapp/types/common.ts | 3 +- 10 files changed, 611 insertions(+), 362 deletions(-) diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index dab54dd16..3af49e844 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -97,3 +97,29 @@ func convertCustomReadDataLabels(labels []*destinations.CustomReadDataLabel) []* } return result } + +func convertConditions(conditions []v1.Condition) []*model.Condition { + var result []*model.Condition + for _, c := range conditions { + result = append(result, &model.Condition{ + Status: model.ConditionStatus(c.Status), + Type: c.Type, + Reason: &c.Reason, + Message: &c.Message, + LastTransitionTime: convertLastTransitionTime(c.LastTransitionTime), + }) + } + return result +} + +// Convert LastTransitionTime to a string pointer if it's not nil +func convertLastTransitionTime(transitionTime v1.Time) *string { + var lastTransitionTime *string + + if !transitionTime.IsZero() { + t := transitionTime.Format(time.RFC3339) + lastTransitionTime = &t + } + + return lastTransitionTime +} diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index e4a309309..a43ea4bc7 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -50,6 +50,10 @@ type DirectiveRoot struct { } type ComplexityRoot struct { + ActionStatus struct { + Conditions func(childComplexity int) int + } + AddClusterInfoAction struct { Details func(childComplexity int) int Disable func(childComplexity int) int @@ -217,12 +221,6 @@ type ComplexityRoot struct { MimeTypes func(childComplexity int) int } - IcaInstanceResponse struct { - ID func(childComplexity int) int - Spec func(childComplexity int) int - Type func(childComplexity int) int - } - InstrumentationConfigAnalyze struct { CreateTime func(childComplexity int) int Created func(childComplexity int) int @@ -382,6 +380,13 @@ type ComplexityRoot struct { Type func(childComplexity int) int } + PipelineAction struct { + ID func(childComplexity int) int + Spec func(childComplexity int) int + Status func(childComplexity int) int + Type func(childComplexity int) int + } + PodAnalyze struct { Containers func(childComplexity int) int NodeName func(childComplexity int) int @@ -493,7 +498,7 @@ type ComputePlatformResolver interface { K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) - Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.IcaInstanceResponse, error) + Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) InstrumentationRules(ctx context.Context, obj *model.ComputePlatform) ([]*model.InstrumentationRule, error) } type DestinationResolver interface { @@ -549,6 +554,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { + case "ActionStatus.conditions": + if e.complexity.ActionStatus.Conditions == nil { + break + } + + return e.complexity.ActionStatus.Conditions(childComplexity), true + case "AddClusterInfoAction.details": if e.complexity.AddClusterInfoAction.Details == nil { break @@ -1245,27 +1257,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.HttpPayloadCollection.MimeTypes(childComplexity), true - case "IcaInstanceResponse.id": - if e.complexity.IcaInstanceResponse.ID == nil { - break - } - - return e.complexity.IcaInstanceResponse.ID(childComplexity), true - - case "IcaInstanceResponse.spec": - if e.complexity.IcaInstanceResponse.Spec == nil { - break - } - - return e.complexity.IcaInstanceResponse.Spec(childComplexity), true - - case "IcaInstanceResponse.type": - if e.complexity.IcaInstanceResponse.Type == nil { - break - } - - return e.complexity.IcaInstanceResponse.Type(childComplexity), true - case "InstrumentationConfigAnalyze.createTime": if e.complexity.InstrumentationConfigAnalyze.CreateTime == nil { break @@ -2008,6 +1999,34 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.PiiMaskingAction.Type(childComplexity), true + case "PipelineAction.id": + if e.complexity.PipelineAction.ID == nil { + break + } + + return e.complexity.PipelineAction.ID(childComplexity), true + + case "PipelineAction.spec": + if e.complexity.PipelineAction.Spec == nil { + break + } + + return e.complexity.PipelineAction.Spec(childComplexity), true + + case "PipelineAction.status": + if e.complexity.PipelineAction.Status == nil { + break + } + + return e.complexity.PipelineAction.Status(childComplexity), true + + case "PipelineAction.type": + if e.complexity.PipelineAction.Type == nil { + break + } + + return e.complexity.PipelineAction.Type(childComplexity), true + case "PodAnalyze.containers": if e.complexity.PodAnalyze.Containers == nil { break @@ -3028,6 +3047,59 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg // region **************************** field.gotpl ***************************** +func (ec *executionContext) _ActionStatus_conditions(ctx context.Context, field graphql.CollectedField, obj *model.ActionStatus) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ActionStatus_conditions(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Conditions, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*model.Condition) + fc.Result = res + return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ActionStatus_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ActionStatus", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "status": + return ec.fieldContext_Condition_status(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) + case "reason": + return ec.fieldContext_Condition_reason(ctx, field) + case "message": + return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _AddClusterInfoAction_id(ctx context.Context, field graphql.CollectedField, obj *model.AddClusterInfoAction) (ret graphql.Marshaler) { fc, err := ec.fieldContext_AddClusterInfoAction_id(ctx, field) if err != nil { @@ -4318,9 +4390,9 @@ func (ec *executionContext) _ComputePlatform_actions(ctx context.Context, field } return graphql.Null } - res := resTmp.([]*model.IcaInstanceResponse) + res := resTmp.([]*model.PipelineAction) fc.Result = res - return ec.marshalNIcaInstanceResponse2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐIcaInstanceResponseᚄ(ctx, field.Selections, res) + return ec.marshalNPipelineAction2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPipelineActionᚄ(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_ComputePlatform_actions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -4332,13 +4404,15 @@ func (ec *executionContext) fieldContext_ComputePlatform_actions(_ context.Conte Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": - return ec.fieldContext_IcaInstanceResponse_id(ctx, field) + return ec.fieldContext_PipelineAction_id(ctx, field) case "type": - return ec.fieldContext_IcaInstanceResponse_type(ctx, field) + return ec.fieldContext_PipelineAction_type(ctx, field) case "spec": - return ec.fieldContext_IcaInstanceResponse_spec(ctx, field) + return ec.fieldContext_PipelineAction_spec(ctx, field) + case "status": + return ec.fieldContext_PipelineAction_status(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type IcaInstanceResponse", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PipelineAction", field.Name) }, } return fc, nil @@ -4404,8 +4478,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_instrumentationRules(_ return fc, nil } -func (ec *executionContext) _Condition_type(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Condition_type(ctx, field) +func (ec *executionContext) _Condition_status(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Condition_status(ctx, field) if err != nil { return graphql.Null } @@ -4418,7 +4492,7 @@ func (ec *executionContext) _Condition_type(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Type, nil + return obj.Status, nil }) if err != nil { ec.Error(ctx, err) @@ -4430,26 +4504,26 @@ func (ec *executionContext) _Condition_type(ctx context.Context, field graphql.C } return graphql.Null } - res := resTmp.(string) + res := resTmp.(model.ConditionStatus) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNConditionStatus2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionStatus(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Condition_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Condition_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Condition", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ConditionStatus does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Condition_status(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Condition_status(ctx, field) +func (ec *executionContext) _Condition_type(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Condition_type(ctx, field) if err != nil { return graphql.Null } @@ -4462,7 +4536,7 @@ func (ec *executionContext) _Condition_status(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Status, nil + return obj.Type, nil }) if err != nil { ec.Error(ctx, err) @@ -4474,26 +4548,26 @@ func (ec *executionContext) _Condition_status(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(model.ConditionStatus) + res := resTmp.(string) fc.Result = res - return ec.marshalNConditionStatus2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionStatus(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Condition_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Condition_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Condition", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ConditionStatus does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Condition_lastTransitionTime(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Condition_lastTransitionTime(ctx, field) +func (ec *executionContext) _Condition_reason(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Condition_reason(ctx, field) if err != nil { return graphql.Null } @@ -4506,7 +4580,7 @@ func (ec *executionContext) _Condition_lastTransitionTime(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.LastTransitionTime, nil + return obj.Reason, nil }) if err != nil { ec.Error(ctx, err) @@ -4520,7 +4594,7 @@ func (ec *executionContext) _Condition_lastTransitionTime(ctx context.Context, f return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Condition_lastTransitionTime(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Condition_reason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Condition", Field: field, @@ -4533,8 +4607,8 @@ func (ec *executionContext) fieldContext_Condition_lastTransitionTime(_ context. return fc, nil } -func (ec *executionContext) _Condition_reason(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Condition_reason(ctx, field) +func (ec *executionContext) _Condition_message(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Condition_message(ctx, field) if err != nil { return graphql.Null } @@ -4547,7 +4621,7 @@ func (ec *executionContext) _Condition_reason(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Reason, nil + return obj.Message, nil }) if err != nil { ec.Error(ctx, err) @@ -4561,7 +4635,7 @@ func (ec *executionContext) _Condition_reason(ctx context.Context, field graphql return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Condition_reason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Condition_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Condition", Field: field, @@ -4574,8 +4648,8 @@ func (ec *executionContext) fieldContext_Condition_reason(_ context.Context, fie return fc, nil } -func (ec *executionContext) _Condition_message(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Condition_message(ctx, field) +func (ec *executionContext) _Condition_lastTransitionTime(ctx context.Context, field graphql.CollectedField, obj *model.Condition) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Condition_lastTransitionTime(ctx, field) if err != nil { return graphql.Null } @@ -4588,7 +4662,7 @@ func (ec *executionContext) _Condition_message(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Message, nil + return obj.LastTransitionTime, nil }) if err != nil { ec.Error(ctx, err) @@ -4602,7 +4676,7 @@ func (ec *executionContext) _Condition_message(ctx context.Context, field graphq return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Condition_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Condition_lastTransitionTime(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Condition", Field: field, @@ -5873,16 +5947,16 @@ func (ec *executionContext) fieldContext_Destination_conditions(_ context.Contex IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "type": - return ec.fieldContext_Condition_type(ctx, field) case "status": return ec.fieldContext_Condition_status(ctx, field) - case "lastTransitionTime": - return ec.fieldContext_Condition_lastTransitionTime(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) case "reason": return ec.fieldContext_Condition_reason(ctx, field) case "message": return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) }, @@ -7639,138 +7713,6 @@ func (ec *executionContext) fieldContext_HttpPayloadCollection_dropPartialPayloa return fc, nil } -func (ec *executionContext) _IcaInstanceResponse_id(ctx context.Context, field graphql.CollectedField, obj *model.IcaInstanceResponse) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_IcaInstanceResponse_id(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ID, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_IcaInstanceResponse_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "IcaInstanceResponse", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _IcaInstanceResponse_type(ctx context.Context, field graphql.CollectedField, obj *model.IcaInstanceResponse) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_IcaInstanceResponse_type(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Type, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_IcaInstanceResponse_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "IcaInstanceResponse", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _IcaInstanceResponse_spec(ctx context.Context, field graphql.CollectedField, obj *model.IcaInstanceResponse) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_IcaInstanceResponse_spec(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Spec, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_IcaInstanceResponse_spec(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "IcaInstanceResponse", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _InstrumentationConfigAnalyze_created(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationConfigAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_InstrumentationConfigAnalyze_created(ctx, field) if err != nil { @@ -9218,16 +9160,16 @@ func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_conditio IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "type": - return ec.fieldContext_Condition_type(ctx, field) case "status": return ec.fieldContext_Condition_status(ctx, field) - case "lastTransitionTime": - return ec.fieldContext_Condition_lastTransitionTime(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) case "reason": return ec.fieldContext_Condition_reason(ctx, field) case "message": return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) }, @@ -12489,14 +12431,187 @@ func (ec *executionContext) fieldContext_PiiMaskingAction_signals(_ context.Cont IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type SignalType does not have child fields") + return nil, errors.New("field of type SignalType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PiiMaskingAction_details(ctx context.Context, field graphql.CollectedField, obj *model.PiiMaskingAction) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PiiMaskingAction_details(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Details, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]string) + fc.Result = res + return ec.marshalOString2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PiiMaskingAction_details(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PiiMaskingAction", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PipelineAction_id(ctx context.Context, field graphql.CollectedField, obj *model.PipelineAction) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PipelineAction_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PipelineAction_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PipelineAction", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PipelineAction_type(ctx context.Context, field graphql.CollectedField, obj *model.PipelineAction) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PipelineAction_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PipelineAction_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PipelineAction", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PipelineAction_spec(ctx context.Context, field graphql.CollectedField, obj *model.PipelineAction) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PipelineAction_spec(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Spec, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PipelineAction_spec(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PipelineAction", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _PiiMaskingAction_details(ctx context.Context, field graphql.CollectedField, obj *model.PiiMaskingAction) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PiiMaskingAction_details(ctx, field) +func (ec *executionContext) _PipelineAction_status(ctx context.Context, field graphql.CollectedField, obj *model.PipelineAction) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PipelineAction_status(ctx, field) if err != nil { return graphql.Null } @@ -12509,28 +12624,35 @@ func (ec *executionContext) _PiiMaskingAction_details(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Details, nil + return obj.Status, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.([]string) + res := resTmp.(*model.ActionStatus) fc.Result = res - return ec.marshalOString2ᚕstringᚄ(ctx, field.Selections, res) + return ec.marshalNActionStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐActionStatus(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PiiMaskingAction_details(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PipelineAction_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PiiMaskingAction", + Object: "PipelineAction", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "conditions": + return ec.fieldContext_ActionStatus_conditions(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ActionStatus", field.Name) }, } return fc, nil @@ -18322,6 +18444,42 @@ func (ec *executionContext) _Action(ctx context.Context, sel ast.SelectionSet, o // region **************************** object.gotpl **************************** +var actionStatusImplementors = []string{"ActionStatus"} + +func (ec *executionContext) _ActionStatus(ctx context.Context, sel ast.SelectionSet, obj *model.ActionStatus) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, actionStatusImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ActionStatus") + case "conditions": + out.Values[i] = ec._ActionStatus_conditions(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var addClusterInfoActionImplementors = []string{"AddClusterInfoAction", "Action"} func (ec *executionContext) _AddClusterInfoAction(ctx context.Context, sel ast.SelectionSet, obj *model.AddClusterInfoAction) graphql.Marshaler { @@ -18785,22 +18943,22 @@ func (ec *executionContext) _Condition(ctx context.Context, sel ast.SelectionSet switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Condition") - case "type": - out.Values[i] = ec._Condition_type(ctx, field, obj) + case "status": + out.Values[i] = ec._Condition_status(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "status": - out.Values[i] = ec._Condition_status(ctx, field, obj) + case "type": + out.Values[i] = ec._Condition_type(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "lastTransitionTime": - out.Values[i] = ec._Condition_lastTransitionTime(ctx, field, obj) case "reason": out.Values[i] = ec._Condition_reason(ctx, field, obj) case "message": out.Values[i] = ec._Condition_message(ctx, field, obj) + case "lastTransitionTime": + out.Values[i] = ec._Condition_lastTransitionTime(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -19792,55 +19950,6 @@ func (ec *executionContext) _HttpPayloadCollection(ctx context.Context, sel ast. return out } -var icaInstanceResponseImplementors = []string{"IcaInstanceResponse"} - -func (ec *executionContext) _IcaInstanceResponse(ctx context.Context, sel ast.SelectionSet, obj *model.IcaInstanceResponse) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, icaInstanceResponseImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("IcaInstanceResponse") - case "id": - out.Values[i] = ec._IcaInstanceResponse_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "type": - out.Values[i] = ec._IcaInstanceResponse_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "spec": - out.Values[i] = ec._IcaInstanceResponse_spec(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var instrumentationConfigAnalyzeImplementors = []string{"InstrumentationConfigAnalyze"} func (ec *executionContext) _InstrumentationConfigAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationConfigAnalyze) graphql.Marshaler { @@ -20983,6 +21092,60 @@ func (ec *executionContext) _PiiMaskingAction(ctx context.Context, sel ast.Selec return out } +var pipelineActionImplementors = []string{"PipelineAction"} + +func (ec *executionContext) _PipelineAction(ctx context.Context, sel ast.SelectionSet, obj *model.PipelineAction) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, pipelineActionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PipelineAction") + case "id": + out.Values[i] = ec._PipelineAction_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "type": + out.Values[i] = ec._PipelineAction_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "spec": + out.Values[i] = ec._PipelineAction_spec(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "status": + out.Values[i] = ec._PipelineAction_status(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var podAnalyzeImplementors = []string{"PodAnalyze"} func (ec *executionContext) _PodAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.PodAnalyze) graphql.Marshaler { @@ -22204,6 +22367,16 @@ func (ec *executionContext) unmarshalNActionInput2githubᚗcomᚋodigosᚑioᚋo return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNActionStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐActionStatus(ctx context.Context, sel ast.SelectionSet, v *model.ActionStatus) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ActionStatus(ctx, sel, v) +} + func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { res, err := graphql.UnmarshalBoolean(v) return res, graphql.ErrorOnPath(ctx, err) @@ -22842,60 +23015,6 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec return res } -func (ec *executionContext) marshalNIcaInstanceResponse2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐIcaInstanceResponseᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.IcaInstanceResponse) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNIcaInstanceResponse2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐIcaInstanceResponse(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNIcaInstanceResponse2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐIcaInstanceResponse(ctx context.Context, sel ast.SelectionSet, v *model.IcaInstanceResponse) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._IcaInstanceResponse(ctx, sel, v) -} - func (ec *executionContext) unmarshalNInstallationStatus2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstallationStatus(ctx context.Context, v interface{}) (model.InstallationStatus, error) { var res model.InstallationStatus err := res.UnmarshalGQL(v) @@ -23366,6 +23485,60 @@ func (ec *executionContext) unmarshalNPersistNamespaceSourceInput2ᚖgithubᚗco return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNPipelineAction2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPipelineActionᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.PipelineAction) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNPipelineAction2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPipelineAction(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNPipelineAction2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPipelineAction(ctx context.Context, sel ast.SelectionSet, v *model.PipelineAction) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PipelineAction(ctx, sel, v) +} + func (ec *executionContext) marshalNPodAnalyze2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPodAnalyzeᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.PodAnalyze) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 8b79a6e3a..fd3f72d4d 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -27,6 +27,10 @@ type ActionInput struct { Details string `json:"details"` } +type ActionStatus struct { + Conditions []*Condition `json:"conditions,omitempty"` +} + type AddClusterInfoAction struct { ID string `json:"id"` Type string `json:"type"` @@ -79,16 +83,16 @@ type ComputePlatform struct { K8sActualSource *K8sActualSource `json:"k8sActualSource,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` Destinations []*Destination `json:"destinations"` - Actions []*IcaInstanceResponse `json:"actions"` + Actions []*PipelineAction `json:"actions"` InstrumentationRules []*InstrumentationRule `json:"instrumentationRules"` } type Condition struct { - Type string `json:"type"` Status ConditionStatus `json:"status"` - LastTransitionTime *string `json:"lastTransitionTime,omitempty"` + Type string `json:"type"` Reason *string `json:"reason,omitempty"` Message *string `json:"message,omitempty"` + LastTransitionTime *string `json:"lastTransitionTime,omitempty"` } type ContainerRuntimeInfoAnalyze struct { @@ -241,12 +245,6 @@ type HTTPPayloadCollectionInput struct { DropPartialPayloads *bool `json:"dropPartialPayloads,omitempty"` } -type IcaInstanceResponse struct { - ID string `json:"id"` - Type string `json:"type"` - Spec string `json:"spec"` -} - type InstrumentationConfigAnalyze struct { Created *EntityProperty `json:"created"` CreateTime *EntityProperty `json:"createTime,omitempty"` @@ -484,6 +482,13 @@ func (this PiiMaskingAction) GetSignals() []SignalType { return interfaceSlice } +type PipelineAction struct { + ID string `json:"id"` + Type string `json:"type"` + Spec string `json:"spec"` + Status *ActionStatus `json:"status"` +} + type PodAnalyze struct { PodName *EntityProperty `json:"podName"` NodeName *EntityProperty `json:"nodeName"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index ba209da7e..efdcb0921 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -68,11 +68,11 @@ type InstrumentedApplicationDetails { } type Condition { - type: String! status: ConditionStatus! - lastTransitionTime: String + type: String! reason: String message: String + lastTransitionTime: String } type K8sActualNamespace { @@ -208,7 +208,7 @@ type ComputePlatform { k8sActualSource(name: String, namespace: String, kind: String): K8sActualSource k8sActualSources: [K8sActualSource]! destinations: [Destination!]! - actions: [IcaInstanceResponse!]! + actions: [PipelineAction!]! instrumentationRules: [InstrumentationRule!]! } @@ -324,10 +324,15 @@ type DestinationDetails { fields: String! } -type IcaInstanceResponse { +type ActionStatus { + conditions: [Condition!] +} + +type PipelineAction { id: String! type: String! spec: String! + status: ActionStatus! } input PatchSourceRequestInput { diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 18674e8f1..1a3a04e71 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -8,7 +8,6 @@ import ( "context" "encoding/json" "fmt" - "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" @@ -135,8 +134,8 @@ func (r *computePlatformResolver) Destinations(ctx context.Context, obj *model.C } // Actions is the resolver for the actions field. -func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.IcaInstanceResponse, error) { - var response []*model.IcaInstanceResponse +func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) { + var response []*model.PipelineAction odigosns := consts.DefaultOdigosNamespace // AddClusterInfos actions @@ -149,10 +148,13 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput if err != nil { return nil, err } - response = append(response, &model.IcaInstanceResponse{ + response = append(response, &model.PipelineAction{ ID: action.Name, Type: action.Kind, Spec: string(specStr), + Status: &model.ActionStatus{ + Conditions: convertConditions(action.Status.Conditions), + }, }) } @@ -166,10 +168,13 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput if err != nil { return nil, err } - response = append(response, &model.IcaInstanceResponse{ + response = append(response, &model.PipelineAction{ ID: action.Name, Type: action.Kind, Spec: string(specStr), + Status: &model.ActionStatus{ + Conditions: convertConditions(action.Status.Conditions), + }, }) } @@ -183,10 +188,13 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput if err != nil { return nil, err } - response = append(response, &model.IcaInstanceResponse{ + response = append(response, &model.PipelineAction{ ID: action.Name, Type: action.Kind, Spec: string(specStr), + Status: &model.ActionStatus{ + Conditions: convertConditions(action.Status.Conditions), + }, }) } @@ -200,10 +208,13 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput if err != nil { return nil, err } - response = append(response, &model.IcaInstanceResponse{ + response = append(response, &model.PipelineAction{ ID: action.Name, Type: action.Kind, Spec: string(specStr), + Status: &model.ActionStatus{ + Conditions: convertConditions(action.Status.Conditions), + }, }) } @@ -217,10 +228,13 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput if err != nil { return nil, err } - response = append(response, &model.IcaInstanceResponse{ + response = append(response, &model.PipelineAction{ ID: action.Name, Type: action.Kind, Spec: string(specStr), + Status: &model.ActionStatus{ + Conditions: convertConditions(action.Status.Conditions), + }, }) } @@ -234,10 +248,13 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput if err != nil { return nil, err } - response = append(response, &model.IcaInstanceResponse{ + response = append(response, &model.PipelineAction{ ID: action.Name, Type: action.Kind, Spec: string(specStr), + Status: &model.ActionStatus{ + Conditions: convertConditions(action.Status.Conditions), + }, }) } @@ -251,10 +268,13 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput if err != nil { return nil, err } - response = append(response, &model.IcaInstanceResponse{ + response = append(response, &model.PipelineAction{ ID: action.Name, Type: action.Kind, Spec: string(specStr), + Status: &model.ActionStatus{ + Conditions: convertConditions(action.Status.Conditions), + }, }) } @@ -275,18 +295,11 @@ func (r *destinationResolver) Type(ctx context.Context, obj *model.Destination) func (r *destinationResolver) Conditions(ctx context.Context, obj *model.Destination) ([]*model.Condition, error) { conditions := make([]*model.Condition, 0, len(obj.Conditions)) for _, c := range obj.Conditions { - // Convert LastTransitionTime to a string pointer if it's not nil - var lastTransitionTime *string - if !c.LastTransitionTime.IsZero() { - t := c.LastTransitionTime.Format(time.RFC3339) - lastTransitionTime = &t - } - // Add the converted Condition to the list conditions = append(conditions, &model.Condition{ Type: c.Type, Status: model.ConditionStatus(c.Status), - LastTransitionTime: lastTransitionTime, + LastTransitionTime: convertLastTransitionTime(c.LastTransitionTime), Reason: &c.Reason, Message: &c.Message, }) diff --git a/frontend/webapp/containers/main/actions/action-drawer/index.tsx b/frontend/webapp/containers/main/actions/action-drawer/index.tsx index b24cb2803..7235fb3c7 100644 --- a/frontend/webapp/containers/main/actions/action-drawer/index.tsx +++ b/frontend/webapp/containers/main/actions/action-drawer/index.tsx @@ -4,7 +4,7 @@ import { ActionFormBody } from '../'; import styled from 'styled-components'; import { useDrawerStore } from '@/store'; import buildDrawerItem from './build-drawer-item'; -import { DataCard } from '@/reuseable-components'; +import { ConditionDetails, DataCard } from '@/reuseable-components'; import { useActionCRUD, useActionFormData } from '@/hooks'; import { ACTION, DATA_CARDS, getActionIcon } from '@/utils'; import OverviewDrawer from '../../overview/overview-drawer'; @@ -21,6 +21,12 @@ const FormContainer = styled.div` overflow-y: auto; `; +const DataContainer = styled.div` + display: flex; + flex-direction: column; + gap: 12px; +`; + export const ActionDrawer: React.FC = () => { const { selectedItem, setSelectedItem } = useDrawerStore(); const { formData, formErrors, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem } = useActionFormData(); @@ -118,7 +124,10 @@ export const ActionDrawer: React.FC = () => { /> ) : ( - + + + + )} ); diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx index 1bd4a6d70..750696632 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -78,19 +78,21 @@ export const SourceDrawer: React.FC = () => { if (!selectedItem) return []; const { item } = selectedItem as { item: K8sActualSource }; - if (!item?.instrumentedApplicationDetails?.conditions) return []; - - const hasPresenceOfOtherAgent = item.instrumentedApplicationDetails.conditions.some( - (condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent'), - ); + const hasPresenceOfOtherAgent = + item?.instrumentedApplicationDetails?.conditions?.some( + (condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent'), + ) || false; return ( - item.instrumentedApplicationDetails.containers.map( + item?.instrumentedApplicationDetails?.containers?.map( (container) => ({ type: DataCardFieldTypes.SOURCE_CONTAINER, width: '100%', - value: JSON.stringify({ ...container, hasPresenceOfOtherAgent }), + value: JSON.stringify({ + ...container, + hasPresenceOfOtherAgent, + }), } as DataCardRow), ) || [] ); @@ -144,7 +146,7 @@ export const SourceDrawer: React.FC = () => { ) : ( - + Date: Wed, 25 Dec 2024 10:13:11 +0200 Subject: [PATCH 038/259] fix: build error --- .../containers/main/actions/action-drawer/build-drawer-item.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/webapp/containers/main/actions/action-drawer/build-drawer-item.ts b/frontend/webapp/containers/main/actions/action-drawer/build-drawer-item.ts index d3c03810b..fdb5fd70e 100644 --- a/frontend/webapp/containers/main/actions/action-drawer/build-drawer-item.ts +++ b/frontend/webapp/containers/main/actions/action-drawer/build-drawer-item.ts @@ -3,7 +3,7 @@ import type { ActionDataParsed, ActionInput } from '@/types'; const buildDrawerItem = (id: string, formData: ActionInput, drawerItem: ActionDataParsed): ActionDataParsed => { const { type, name, notes, signals, disable, details } = formData; - const {} = drawerItem; + const { status } = drawerItem; return { id, @@ -15,6 +15,7 @@ const buildDrawerItem = (id: string, formData: ActionInput, drawerItem: ActionDa disabled: disable, ...safeJsonParse(details, {}), }, + status, }; }; From 7dd99abafeed0f29f91b62c5c4d59df296b3d910 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 25 Dec 2024 11:06:08 +0200 Subject: [PATCH 039/259] chore: wip --- api/config/crd/bases/odigos.io_sources.yaml | 254 +++++++++--------- .../controllers/destination_controller.go | 97 ++++++- cli/cmd/resources/autoscaler.go | 9 + .../templates/crds/odigos.io_sources.yaml | 254 +++++++++--------- 4 files changed, 357 insertions(+), 257 deletions(-) diff --git a/api/config/crd/bases/odigos.io_sources.yaml b/api/config/crd/bases/odigos.io_sources.yaml index 48b67831f..5128911ee 100644 --- a/api/config/crd/bases/odigos.io_sources.yaml +++ b/api/config/crd/bases/odigos.io_sources.yaml @@ -14,139 +14,137 @@ spec: singular: source scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.workload.name - name: Workload - type: string - - jsonPath: .spec.workload.kind - name: Kind - type: string - - jsonPath: .spec.workload.namespace - name: Namespace - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Source configures an application for auto-instrumentation. - 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: - groups: - description: - Groups represents the logical group(s) this source belongs - to. - items: + - additionalPrinterColumns: + - jsonPath: .spec.workload.name + name: Workload + type: string + - jsonPath: .spec.workload.kind + name: Kind + type: string + - jsonPath: .spec.workload.namespace + name: Namespace + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Source configures an application for auto-instrumentation. + 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: + groups: + description: Groups represents the logical group(s) this source belongs + to. + items: + type: string + type: array + workload: + description: |- + Workload represents the workload or namespace to be instrumented. + This field is required upon creation and cannot be modified. + 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 - type: array - workload: - description: |- - Workload represents the workload or namespace to be instrumented. - This field is required upon creation and cannot be modified. + name: + type: string + namespace: + type: string + required: + - kind + - name + - namespace + type: object + required: + - workload + type: object + status: + properties: + conditions: + description: |- + Represents the observations of a source'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: - kind: + 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: |- - 1. the pascal case representation of the workload kind - it is used in k8s api objects as the `Kind` field. + 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 - name: + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown type: string - namespace: + 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: - - kind - - name - - namespace + - lastTransitionTime + - message + - reason + - status + - type type: object - required: - - workload - type: object - status: - properties: - conditions: - description: |- - Represents the observations of a source'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 - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/autoscaler/controllers/destination_controller.go b/autoscaler/controllers/destination_controller.go index 81c7c9e72..d7ca38fd9 100644 --- a/autoscaler/controllers/destination_controller.go +++ b/autoscaler/controllers/destination_controller.go @@ -41,7 +41,31 @@ type DestinationReconciler struct { func (r *DestinationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) logger.V(0).Info("Reconciling Destination") - err := gateway.Sync(ctx, r.Client, r.Scheme, r.ImagePullSecrets, r.OdigosVersion, r.Config) + + var destination v1.Destination + if err := r.Client.Get(ctx, req.NamespacedName, &destination); err != nil { + logger.Error(err, "Failed to get Destination") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + var sources v1.SourceList + if err := r.Client.List(ctx, &sources); err != nil { + logger.Error(err, "Failed to list Sources") + return ctrl.Result{}, err + } + logger.V(1).Info("Sources", "sources", sources.Items) + logger.V(1).Info("Destination", "destination", destination) + logger.V(1).Info("SourceSelector", "sourceSelector", destination.Spec.SourceSelector) + filteredSources := filterSources(sources.Items, destination.Spec.SourceSelector) + logger.V(1).Info("Filtered Sources", "filteredSources", filteredSources) + // Generate route configuration + err := generateRouteConfig(ctx, r.Client, destination, filteredSources) + if err != nil { + logger.Error(err, "Failed to generate route configuration") + return ctrl.Result{}, err + } + + err = gateway.Sync(ctx, r.Client, r.Scheme, r.ImagePullSecrets, r.OdigosVersion, r.Config) if err != nil { return ctrl.Result{}, err } @@ -58,3 +82,74 @@ func (r *DestinationReconciler) SetupWithManager(mgr ctrl.Manager) error { WithEventFilter(&predicate.GenerationChangedPredicate{}). Complete(r) } + +func generateRouteConfig(ctx context.Context, client client.Client, destination v1.Destination, sources []v1.Source) error { + // Build route configuration based on filtered sources and destination signals + routeConfig := buildRouteConfig(destination, sources) + + // Apply the route configuration to the OpenTelemetry collector + // This could involve updating a ConfigMap or another custom resource + // that the collector watches + configMapName := "otelcol-route-config" + configMapNamespace := "odigos-system" // Replace with your namespace + err := updateConfigMap(ctx, client, configMapName, configMapNamespace, routeConfig) + if err != nil { + return err + } + + return nil +} + +func buildRouteConfig(destination v1.Destination, sources []v1.Source) map[string]interface{} { + // Example structure of the route configuration + routeConfig := map[string]interface{}{ + "destination": destination.Spec.DestinationName, + "signals": destination.Spec.Signals, + "sources": []string{}, + } + + for _, source := range sources { + routeConfig["sources"] = append(routeConfig["sources"].([]string), source.Name) + } + + return routeConfig +} + +func updateConfigMap(ctx context.Context, client client.Client, name, namespace string, data map[string]interface{}) error { + + // Update the ConfigMap with the new data + // This could involve creating a new ConfigMap or updating an existing one + // based on the name and namespace provided + return nil +} + +func filterSources(sources []v1.Source, selector *v1.SourceSelector) []v1.Source { + if selector == nil || selector.Mode == "all" { + // Return all sources if selector is nil or mode is "all" + return sources + } + + var filtered []v1.Source + for _, source := range sources { + switch selector.Mode { + case "namespaces": + for _, ns := range selector.Namespaces { + if source.Spec.Workload.Namespace == ns { + filtered = append(filtered, source) + break + } + } + case "groups": + for _, group := range selector.Groups { + for _, srcGroup := range source.Spec.Groups { + if group == srcGroup { + filtered = append(filtered, source) + break + } + } + } + } + } + + return filtered +} diff --git a/cli/cmd/resources/autoscaler.go b/cli/cmd/resources/autoscaler.go index b961c688c..8dd1e6de8 100644 --- a/cli/cmd/resources/autoscaler.go +++ b/cli/cmd/resources/autoscaler.go @@ -259,6 +259,15 @@ func NewAutoscalerClusterRole() *rbacv1.ClusterRole { APIGroups: []string{"odigos.io"}, Resources: []string{"instrumentationconfigs"}, }, + { + Verbs: []string{ + "get", + "list", + "watch", + }, + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources"}, + }, }, } } diff --git a/helm/odigos/templates/crds/odigos.io_sources.yaml b/helm/odigos/templates/crds/odigos.io_sources.yaml index 48b67831f..5128911ee 100644 --- a/helm/odigos/templates/crds/odigos.io_sources.yaml +++ b/helm/odigos/templates/crds/odigos.io_sources.yaml @@ -14,139 +14,137 @@ spec: singular: source scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.workload.name - name: Workload - type: string - - jsonPath: .spec.workload.kind - name: Kind - type: string - - jsonPath: .spec.workload.namespace - name: Namespace - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Source configures an application for auto-instrumentation. - 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: - groups: - description: - Groups represents the logical group(s) this source belongs - to. - items: + - additionalPrinterColumns: + - jsonPath: .spec.workload.name + name: Workload + type: string + - jsonPath: .spec.workload.kind + name: Kind + type: string + - jsonPath: .spec.workload.namespace + name: Namespace + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Source configures an application for auto-instrumentation. + 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: + groups: + description: Groups represents the logical group(s) this source belongs + to. + items: + type: string + type: array + workload: + description: |- + Workload represents the workload or namespace to be instrumented. + This field is required upon creation and cannot be modified. + 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 - type: array - workload: - description: |- - Workload represents the workload or namespace to be instrumented. - This field is required upon creation and cannot be modified. + name: + type: string + namespace: + type: string + required: + - kind + - name + - namespace + type: object + required: + - workload + type: object + status: + properties: + conditions: + description: |- + Represents the observations of a source'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: - kind: + 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: |- - 1. the pascal case representation of the workload kind - it is used in k8s api objects as the `Kind` field. + 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 - name: + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown type: string - namespace: + 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: - - kind - - name - - namespace + - lastTransitionTime + - message + - reason + - status + - type type: object - required: - - workload - type: object - status: - properties: - conditions: - description: |- - Represents the observations of a source'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 - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} From 5da3b1dcbaaf3cd101f90383460c29bf8242af7e Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 11:50:24 +0200 Subject: [PATCH 040/259] feat: add sources field with spec and status to compute platform --- frontend/gqlgen.yml | 6 +- frontend/graph/generated.go | 572 +++++++++++++++++- frontend/graph/model/models_gen.go | 16 +- frontend/graph/schema.graphqls | 16 +- frontend/graph/schema.resolvers.go | 26 + .../graphql/queries/compute-platform.ts | 18 + frontend/webapp/types/compute-platform.ts | 5 +- frontend/webapp/types/sources.ts | 9 + 8 files changed, 642 insertions(+), 26 deletions(-) diff --git a/frontend/gqlgen.yml b/frontend/gqlgen.yml index 562c27730..fda4093fe 100644 --- a/frontend/gqlgen.yml +++ b/frontend/gqlgen.yml @@ -50,12 +50,14 @@ models: resolver: true k8sActualSource: resolver: true + k8sActualSources: + resolver: true + sources: + resolver: true destinations: resolver: true actions: resolver: true - k8sActualSources: - resolver: true instrumentationRules: resolver: true K8sActualNamespace: diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index a43ea4bc7..525a284a0 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -91,6 +91,7 @@ type ComplexityRoot struct { K8sActualNamespaces func(childComplexity int) int K8sActualSource func(childComplexity int, name *string, namespace *string, kind *string) int K8sActualSources func(childComplexity int) int + Sources func(childComplexity int) int } Condition struct { @@ -456,6 +457,11 @@ type ComplexityRoot struct { TotalDataSent func(childComplexity int) int } + Source struct { + Spec func(childComplexity int) int + Status func(childComplexity int) int + } + SourceAnalyze struct { InstrumentationConfig func(childComplexity int) int InstrumentationDevice func(childComplexity int) int @@ -477,6 +483,14 @@ type ComplexityRoot struct { RuntimeVersion func(childComplexity int) int } + SourceSpec struct { + Workload func(childComplexity int) int + } + + SourceStatus struct { + Conditions func(childComplexity int) int + } + SupportedSignals struct { Logs func(childComplexity int) int Metrics func(childComplexity int) int @@ -497,6 +511,7 @@ type ComputePlatformResolver interface { K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) + Sources(ctx context.Context, obj *model.ComputePlatform) ([]*model.Source, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) InstrumentationRules(ctx context.Context, obj *model.ComputePlatform) ([]*model.InstrumentationRule, error) @@ -760,6 +775,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ComputePlatform.K8sActualSources(childComplexity), true + case "ComputePlatform.sources": + if e.complexity.ComputePlatform.Sources == nil { + break + } + + return e.complexity.ComputePlatform.Sources(childComplexity), true + case "Condition.lastTransitionTime": if e.complexity.Condition.LastTransitionTime == nil { break @@ -2331,6 +2353,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SingleSourceMetricsResponse.TotalDataSent(childComplexity), true + case "Source.spec": + if e.complexity.Source.Spec == nil { + break + } + + return e.complexity.Source.Spec(childComplexity), true + + case "Source.status": + if e.complexity.Source.Status == nil { + break + } + + return e.complexity.Source.Status(childComplexity), true + case "SourceAnalyze.instrumentationConfig": if e.complexity.SourceAnalyze.InstrumentationConfig == nil { break @@ -2436,6 +2472,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SourceContainerRuntimeDetails.RuntimeVersion(childComplexity), true + case "SourceSpec.workload": + if e.complexity.SourceSpec.Workload == nil { + break + } + + return e.complexity.SourceSpec.Workload(childComplexity), true + + case "SourceStatus.conditions": + if e.complexity.SourceStatus.Conditions == nil { + break + } + + return e.complexity.SourceStatus.Conditions(childComplexity), true + case "SupportedSignals.logs": if e.complexity.SupportedSignals.Logs == nil { break @@ -4304,6 +4354,56 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont return fc, nil } +func (ec *executionContext) _ComputePlatform_sources(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ComputePlatform_sources(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.ComputePlatform().Sources(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Source) + fc.Result = res + return ec.marshalNSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ComputePlatform_sources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ComputePlatform", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "spec": + return ec.fieldContext_Source_spec(ctx, field) + case "status": + return ec.fieldContext_Source_status(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Source", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _ComputePlatform_destinations(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ComputePlatform_destinations(ctx, field) if err != nil { @@ -8803,10 +8903,10 @@ func (ec *executionContext) fieldContext_InstrumentationRule_workloads(_ context switch field.Name { case "namespace": return ec.fieldContext_PodWorkload_namespace(ctx, field) - case "kind": - return ec.fieldContext_PodWorkload_kind(ctx, field) case "name": return ec.fieldContext_PodWorkload_name(ctx, field) + case "kind": + return ec.fieldContext_PodWorkload_kind(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type PodWorkload", field.Name) }, @@ -13076,8 +13176,8 @@ func (ec *executionContext) fieldContext_PodWorkload_namespace(_ context.Context return fc, nil } -func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PodWorkload_kind(ctx, field) +func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PodWorkload_name(ctx, field) if err != nil { return graphql.Null } @@ -13090,7 +13190,7 @@ func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -13102,26 +13202,26 @@ func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(model.K8sResourceKind) + res := resTmp.(string) fc.Result = res - return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PodWorkload_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PodWorkload_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PodWorkload", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type K8sResourceKind does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PodWorkload_name(ctx, field) +func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PodWorkload_kind(ctx, field) if err != nil { return graphql.Null } @@ -13134,7 +13234,7 @@ func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -13146,19 +13246,19 @@ func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(string) + res := resTmp.(model.K8sResourceKind) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PodWorkload_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PodWorkload_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PodWorkload", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type K8sResourceKind does not have child fields") }, } return fc, nil @@ -13512,6 +13612,8 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context return ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) case "k8sActualSources": return ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) + case "sources": + return ec.fieldContext_ComputePlatform_sources(ctx, field) case "destinations": return ec.fieldContext_ComputePlatform_destinations(ctx, field) case "actions": @@ -14803,6 +14905,102 @@ func (ec *executionContext) fieldContext_SingleSourceMetricsResponse_throughput( return fc, nil } +func (ec *executionContext) _Source_spec(ctx context.Context, field graphql.CollectedField, obj *model.Source) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Source_spec(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Spec, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.SourceSpec) + fc.Result = res + return ec.marshalNSourceSpec2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceSpec(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Source_spec(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Source", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "workload": + return ec.fieldContext_SourceSpec_workload(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceSpec", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Source_status(ctx context.Context, field graphql.CollectedField, obj *model.Source) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Source_status(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Status, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.SourceStatus) + fc.Result = res + return ec.marshalNSourceStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceStatus(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Source_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Source", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "conditions": + return ec.fieldContext_SourceStatus_conditions(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceStatus", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _SourceAnalyze_name(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SourceAnalyze_name(ctx, field) if err != nil { @@ -15533,6 +15731,111 @@ func (ec *executionContext) fieldContext_SourceContainerRuntimeDetails_otherAgen return fc, nil } +func (ec *executionContext) _SourceSpec_workload(ctx context.Context, field graphql.CollectedField, obj *model.SourceSpec) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceSpec_workload(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Workload, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.PodWorkload) + fc.Result = res + return ec.marshalNPodWorkload2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPodWorkload(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SourceSpec_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SourceSpec", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "namespace": + return ec.fieldContext_PodWorkload_namespace(ctx, field) + case "name": + return ec.fieldContext_PodWorkload_name(ctx, field) + case "kind": + return ec.fieldContext_PodWorkload_kind(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PodWorkload", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _SourceStatus_conditions(ctx context.Context, field graphql.CollectedField, obj *model.SourceStatus) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceStatus_conditions(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Conditions, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*model.Condition) + fc.Result = res + return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SourceStatus_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SourceStatus", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "status": + return ec.fieldContext_Condition_status(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) + case "reason": + return ec.fieldContext_Condition_reason(ctx, field) + case "message": + return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _SupportedSignals_traces(ctx context.Context, field graphql.CollectedField, obj *model.SupportedSignals) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SupportedSignals_traces(ctx, field) if err != nil { @@ -18800,6 +19103,42 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "sources": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ComputePlatform_sources(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "destinations": field := field @@ -21265,13 +21604,13 @@ func (ec *executionContext) _PodWorkload(ctx context.Context, sel ast.SelectionS if out.Values[i] == graphql.Null { out.Invalids++ } - case "kind": - out.Values[i] = ec._PodWorkload_kind(ctx, field, obj) + case "name": + out.Values[i] = ec._PodWorkload_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "name": - out.Values[i] = ec._PodWorkload_name(ctx, field, obj) + case "kind": + out.Values[i] = ec._PodWorkload_kind(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -21790,6 +22129,50 @@ func (ec *executionContext) _SingleSourceMetricsResponse(ctx context.Context, se return out } +var sourceImplementors = []string{"Source"} + +func (ec *executionContext) _Source(ctx context.Context, sel ast.SelectionSet, obj *model.Source) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sourceImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Source") + case "spec": + out.Values[i] = ec._Source_spec(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "status": + out.Values[i] = ec._Source_status(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var sourceAnalyzeImplementors = []string{"SourceAnalyze"} func (ec *executionContext) _SourceAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.SourceAnalyze) graphql.Marshaler { @@ -21927,6 +22310,81 @@ func (ec *executionContext) _SourceContainerRuntimeDetails(ctx context.Context, return out } +var sourceSpecImplementors = []string{"SourceSpec"} + +func (ec *executionContext) _SourceSpec(ctx context.Context, sel ast.SelectionSet, obj *model.SourceSpec) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sourceSpecImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SourceSpec") + case "workload": + out.Values[i] = ec._SourceSpec_workload(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var sourceStatusImplementors = []string{"SourceStatus"} + +func (ec *executionContext) _SourceStatus(ctx context.Context, sel ast.SelectionSet, obj *model.SourceStatus) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sourceStatusImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SourceStatus") + case "conditions": + out.Values[i] = ec._SourceStatus_conditions(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var supportedSignalsImplementors = []string{"SupportedSignals"} func (ec *executionContext) _SupportedSignals(ctx context.Context, sel ast.SelectionSet, obj *model.SupportedSignals) graphql.Marshaler { @@ -23841,6 +24299,60 @@ func (ec *executionContext) marshalNSingleSourceMetricsResponse2ᚖgithubᚗcom return ec._SingleSourceMetricsResponse(ctx, sel, v) } +func (ec *executionContext) marshalNSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Source) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSource(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSource(ctx context.Context, sel ast.SelectionSet, v *model.Source) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Source(ctx, sel, v) +} + func (ec *executionContext) marshalNSourceAnalyze2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceAnalyze(ctx context.Context, sel ast.SelectionSet, v model.SourceAnalyze) graphql.Marshaler { return ec._SourceAnalyze(ctx, sel, &v) } @@ -23865,6 +24377,26 @@ func (ec *executionContext) marshalNSourceContainerRuntimeDetails2ᚖgithubᚗco return ec._SourceContainerRuntimeDetails(ctx, sel, v) } +func (ec *executionContext) marshalNSourceSpec2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceSpec(ctx context.Context, sel ast.SelectionSet, v *model.SourceSpec) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._SourceSpec(ctx, sel, v) +} + +func (ec *executionContext) marshalNSourceStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceStatus(ctx context.Context, sel ast.SelectionSet, v *model.SourceStatus) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._SourceStatus(ctx, sel, v) +} + func (ec *executionContext) unmarshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, v interface{}) (model.SpanKind, error) { var res model.SpanKind err := res.UnmarshalGQL(v) diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index fd3f72d4d..1c5b327cd 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -82,6 +82,7 @@ type ComputePlatform struct { K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` K8sActualSource *K8sActualSource `json:"k8sActualSource,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` + Sources []*Source `json:"sources"` Destinations []*Destination `json:"destinations"` Actions []*PipelineAction `json:"actions"` InstrumentationRules []*InstrumentationRule `json:"instrumentationRules"` @@ -504,8 +505,8 @@ type PodContainerAnalyze struct { type PodWorkload struct { Namespace string `json:"namespace"` - Kind K8sResourceKind `json:"kind"` Name string `json:"name"` + Kind K8sResourceKind `json:"kind"` } type PodWorkloadInput struct { @@ -590,6 +591,11 @@ type SingleSourceMetricsResponse struct { Throughput int `json:"throughput"` } +type Source struct { + Spec *SourceSpec `json:"spec"` + Status *SourceStatus `json:"status"` +} + type SourceAnalyze struct { Name *EntityProperty `json:"name"` Kind *EntityProperty `json:"kind"` @@ -611,6 +617,14 @@ type SourceContainerRuntimeDetails struct { OtherAgent *string `json:"otherAgent,omitempty"` } +type SourceSpec struct { + Workload *PodWorkload `json:"workload"` +} + +type SourceStatus struct { + Conditions []*Condition `json:"conditions,omitempty"` +} + type TestConnectionResponse struct { Succeeded bool `json:"succeeded"` StatusCode int `json:"statusCode"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index efdcb0921..30dcacbe3 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -106,10 +106,23 @@ input K8sDesiredSourceInput { autoInstrument: Boolean } +type SourceSpec { + workload: PodWorkload! +} + +type SourceStatus { + conditions: [Condition!] +} + +type Source { + spec: SourceSpec! + status: SourceStatus! +} + type PodWorkload { namespace: String! - kind: K8sResourceKind! name: String! + kind: K8sResourceKind! } input PodWorkloadInput { @@ -207,6 +220,7 @@ type ComputePlatform { k8sActualNamespaces: [K8sActualNamespace]! k8sActualSource(name: String, namespace: String, kind: String): K8sActualSource k8sActualSources: [K8sActualSource]! + sources: [Source!]! destinations: [Destination!]! actions: [PipelineAction!]! instrumentationRules: [InstrumentationRule!]! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 1a3a04e71..48cc9d19c 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -110,6 +110,32 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod return actualSources, nil } +// Sources is the resolver for the sources field. +func (r *computePlatformResolver) Sources(ctx context.Context, obj *model.ComputePlatform) ([]*model.Source, error) { + sources, err := kube.DefaultClient.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + + var result []*model.Source + for _, source := range sources.Items { + result = append(result, &model.Source{ + Spec: &model.SourceSpec{ + Workload: &model.PodWorkload{ + Namespace: source.Spec.Workload.Namespace, + Name: source.Spec.Workload.Name, + Kind: model.K8sResourceKind(source.Spec.Workload.Kind), + }, + }, + Status: &model.SourceStatus{ + Conditions: convertConditions(source.Status.Conditions), + }, + }) + } + + return result, nil +} + // Destinations is the resolver for the destinations field. func (r *computePlatformResolver) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) { odigosns := consts.DefaultOdigosNamespace diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index 364c748a8..3f1c038af 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -25,6 +25,24 @@ export const GET_COMPUTE_PLATFORM = gql` } } } + sources { + spec { + workload { + kind + name + namespace + } + } + status { + conditions { + status + type + reason + message + lastTransitionTime + } + } + } destinations { id name diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index 16fea4d76..94fb47a97 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -1,4 +1,4 @@ -import type { K8sActualSource } from './sources'; +import type { K8sActualSource, Source } from './sources'; import type { ActualDestination } from './destinations'; import type { ActionData, ActionDataParsed } from './actions'; import type { InstrumentationRuleSpec, InstrumentationRuleSpecMapped } from './instrumentation-rules'; @@ -14,9 +14,10 @@ interface ComputePlatformData { computePlatformType: string; k8sActualNamespace?: K8sActualNamespace; k8sActualNamespaces: K8sActualNamespace[]; - actions: ActionData[]; k8sActualSources: K8sActualSource[]; + sources: Source[]; destinations: ActualDestination[]; + actions: ActionData[]; instrumentationRules: InstrumentationRuleSpec[]; } diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index f2ce90015..6a8a25334 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -36,3 +36,12 @@ export type PersistSourcesArray = { name: string; selected: boolean; }; + +export interface Source { + spec: { + workload: WorkloadId; + }; + status: { + conditions: Condition[]; + }; +} From e70dd669067f0524b7e5a08516d3e6c541d0f7a0 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 13:36:26 +0200 Subject: [PATCH 041/259] refactor: simplify action data structure by removing nested status --- frontend/graph/conversions.go | 21 +-- frontend/graph/generated.go | 172 ++++-------------- frontend/graph/model/models_gen.go | 12 +- frontend/graph/schema.graphqls | 6 +- frontend/graph/schema.resolvers.go | 82 +++------ .../main/actions/action-drawer/index.tsx | 2 +- .../graphql/queries/compute-platform.ts | 14 +- frontend/webapp/types/actions.ts | 4 +- 8 files changed, 82 insertions(+), 231 deletions(-) diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index 3af49e844..67746ccc1 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -101,25 +101,20 @@ func convertCustomReadDataLabels(labels []*destinations.CustomReadDataLabel) []* func convertConditions(conditions []v1.Condition) []*model.Condition { var result []*model.Condition for _, c := range conditions { + // Convert LastTransitionTime to a string pointer if it's not nil + var lastTransitionTime *string + if !c.LastTransitionTime.IsZero() { + t := c.LastTransitionTime.Format(time.RFC3339) + lastTransitionTime = &t + } + result = append(result, &model.Condition{ Status: model.ConditionStatus(c.Status), Type: c.Type, Reason: &c.Reason, Message: &c.Message, - LastTransitionTime: convertLastTransitionTime(c.LastTransitionTime), + LastTransitionTime: lastTransitionTime, }) } return result } - -// Convert LastTransitionTime to a string pointer if it's not nil -func convertLastTransitionTime(transitionTime v1.Time) *string { - var lastTransitionTime *string - - if !transitionTime.IsZero() { - t := transitionTime.Format(time.RFC3339) - lastTransitionTime = &t - } - - return lastTransitionTime -} diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index a43ea4bc7..caba2e253 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -50,10 +50,6 @@ type DirectiveRoot struct { } type ComplexityRoot struct { - ActionStatus struct { - Conditions func(childComplexity int) int - } - AddClusterInfoAction struct { Details func(childComplexity int) int Disable func(childComplexity int) int @@ -381,10 +377,10 @@ type ComplexityRoot struct { } PipelineAction struct { - ID func(childComplexity int) int - Spec func(childComplexity int) int - Status func(childComplexity int) int - Type func(childComplexity int) int + Conditions func(childComplexity int) int + ID func(childComplexity int) int + Spec func(childComplexity int) int + Type func(childComplexity int) int } PodAnalyze struct { @@ -554,13 +550,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { - case "ActionStatus.conditions": - if e.complexity.ActionStatus.Conditions == nil { - break - } - - return e.complexity.ActionStatus.Conditions(childComplexity), true - case "AddClusterInfoAction.details": if e.complexity.AddClusterInfoAction.Details == nil { break @@ -1999,6 +1988,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.PiiMaskingAction.Type(childComplexity), true + case "PipelineAction.conditions": + if e.complexity.PipelineAction.Conditions == nil { + break + } + + return e.complexity.PipelineAction.Conditions(childComplexity), true + case "PipelineAction.id": if e.complexity.PipelineAction.ID == nil { break @@ -2013,13 +2009,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.PipelineAction.Spec(childComplexity), true - case "PipelineAction.status": - if e.complexity.PipelineAction.Status == nil { - break - } - - return e.complexity.PipelineAction.Status(childComplexity), true - case "PipelineAction.type": if e.complexity.PipelineAction.Type == nil { break @@ -3047,59 +3036,6 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg // region **************************** field.gotpl ***************************** -func (ec *executionContext) _ActionStatus_conditions(ctx context.Context, field graphql.CollectedField, obj *model.ActionStatus) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ActionStatus_conditions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Conditions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.Condition) - fc.Result = res - return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_ActionStatus_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ActionStatus", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "status": - return ec.fieldContext_Condition_status(ctx, field) - case "type": - return ec.fieldContext_Condition_type(ctx, field) - case "reason": - return ec.fieldContext_Condition_reason(ctx, field) - case "message": - return ec.fieldContext_Condition_message(ctx, field) - case "lastTransitionTime": - return ec.fieldContext_Condition_lastTransitionTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _AddClusterInfoAction_id(ctx context.Context, field graphql.CollectedField, obj *model.AddClusterInfoAction) (ret graphql.Marshaler) { fc, err := ec.fieldContext_AddClusterInfoAction_id(ctx, field) if err != nil { @@ -4409,8 +4345,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_actions(_ context.Conte return ec.fieldContext_PipelineAction_type(ctx, field) case "spec": return ec.fieldContext_PipelineAction_spec(ctx, field) - case "status": - return ec.fieldContext_PipelineAction_status(ctx, field) + case "conditions": + return ec.fieldContext_PipelineAction_conditions(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type PipelineAction", field.Name) }, @@ -12610,8 +12546,8 @@ func (ec *executionContext) fieldContext_PipelineAction_spec(_ context.Context, return fc, nil } -func (ec *executionContext) _PipelineAction_status(ctx context.Context, field graphql.CollectedField, obj *model.PipelineAction) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PipelineAction_status(ctx, field) +func (ec *executionContext) _PipelineAction_conditions(ctx context.Context, field graphql.CollectedField, obj *model.PipelineAction) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PipelineAction_conditions(ctx, field) if err != nil { return graphql.Null } @@ -12624,24 +12560,21 @@ func (ec *executionContext) _PipelineAction_status(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Status, nil + return obj.Conditions, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(*model.ActionStatus) + res := resTmp.([]*model.Condition) fc.Result = res - return ec.marshalNActionStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐActionStatus(ctx, field.Selections, res) + return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PipelineAction_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PipelineAction_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PipelineAction", Field: field, @@ -12649,10 +12582,18 @@ func (ec *executionContext) fieldContext_PipelineAction_status(_ context.Context IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "conditions": - return ec.fieldContext_ActionStatus_conditions(ctx, field) + case "status": + return ec.fieldContext_Condition_status(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) + case "reason": + return ec.fieldContext_Condition_reason(ctx, field) + case "message": + return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type ActionStatus", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) }, } return fc, nil @@ -18444,42 +18385,6 @@ func (ec *executionContext) _Action(ctx context.Context, sel ast.SelectionSet, o // region **************************** object.gotpl **************************** -var actionStatusImplementors = []string{"ActionStatus"} - -func (ec *executionContext) _ActionStatus(ctx context.Context, sel ast.SelectionSet, obj *model.ActionStatus) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, actionStatusImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("ActionStatus") - case "conditions": - out.Values[i] = ec._ActionStatus_conditions(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var addClusterInfoActionImplementors = []string{"AddClusterInfoAction", "Action"} func (ec *executionContext) _AddClusterInfoAction(ctx context.Context, sel ast.SelectionSet, obj *model.AddClusterInfoAction) graphql.Marshaler { @@ -21118,11 +21023,8 @@ func (ec *executionContext) _PipelineAction(ctx context.Context, sel ast.Selecti if out.Values[i] == graphql.Null { out.Invalids++ } - case "status": - out.Values[i] = ec._PipelineAction_status(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } + case "conditions": + out.Values[i] = ec._PipelineAction_conditions(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -22367,16 +22269,6 @@ func (ec *executionContext) unmarshalNActionInput2githubᚗcomᚋodigosᚑioᚋo return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNActionStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐActionStatus(ctx context.Context, sel ast.SelectionSet, v *model.ActionStatus) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._ActionStatus(ctx, sel, v) -} - func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { res, err := graphql.UnmarshalBoolean(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index fd3f72d4d..d5407efa3 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -27,10 +27,6 @@ type ActionInput struct { Details string `json:"details"` } -type ActionStatus struct { - Conditions []*Condition `json:"conditions,omitempty"` -} - type AddClusterInfoAction struct { ID string `json:"id"` Type string `json:"type"` @@ -483,10 +479,10 @@ func (this PiiMaskingAction) GetSignals() []SignalType { } type PipelineAction struct { - ID string `json:"id"` - Type string `json:"type"` - Spec string `json:"spec"` - Status *ActionStatus `json:"status"` + ID string `json:"id"` + Type string `json:"type"` + Spec string `json:"spec"` + Conditions []*Condition `json:"conditions,omitempty"` } type PodAnalyze struct { diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index efdcb0921..3fb7b0f4d 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -324,15 +324,11 @@ type DestinationDetails { fields: String! } -type ActionStatus { - conditions: [Condition!] -} - type PipelineAction { id: String! type: String! spec: String! - status: ActionStatus! + conditions: [Condition!] } input PatchSourceRequestInput { diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 1a3a04e71..ffddced75 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -149,12 +149,10 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput return nil, err } response = append(response, &model.PipelineAction{ - ID: action.Name, - Type: action.Kind, - Spec: string(specStr), - Status: &model.ActionStatus{ - Conditions: convertConditions(action.Status.Conditions), - }, + ID: action.Name, + Type: action.Kind, + Spec: string(specStr), + Conditions: convertConditions(action.Status.Conditions), }) } @@ -169,12 +167,10 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput return nil, err } response = append(response, &model.PipelineAction{ - ID: action.Name, - Type: action.Kind, - Spec: string(specStr), - Status: &model.ActionStatus{ - Conditions: convertConditions(action.Status.Conditions), - }, + ID: action.Name, + Type: action.Kind, + Spec: string(specStr), + Conditions: convertConditions(action.Status.Conditions), }) } @@ -189,12 +185,10 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput return nil, err } response = append(response, &model.PipelineAction{ - ID: action.Name, - Type: action.Kind, - Spec: string(specStr), - Status: &model.ActionStatus{ - Conditions: convertConditions(action.Status.Conditions), - }, + ID: action.Name, + Type: action.Kind, + Spec: string(specStr), + Conditions: convertConditions(action.Status.Conditions), }) } @@ -209,12 +203,10 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput return nil, err } response = append(response, &model.PipelineAction{ - ID: action.Name, - Type: action.Kind, - Spec: string(specStr), - Status: &model.ActionStatus{ - Conditions: convertConditions(action.Status.Conditions), - }, + ID: action.Name, + Type: action.Kind, + Spec: string(specStr), + Conditions: convertConditions(action.Status.Conditions), }) } @@ -229,12 +221,10 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput return nil, err } response = append(response, &model.PipelineAction{ - ID: action.Name, - Type: action.Kind, - Spec: string(specStr), - Status: &model.ActionStatus{ - Conditions: convertConditions(action.Status.Conditions), - }, + ID: action.Name, + Type: action.Kind, + Spec: string(specStr), + Conditions: convertConditions(action.Status.Conditions), }) } @@ -249,12 +239,10 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput return nil, err } response = append(response, &model.PipelineAction{ - ID: action.Name, - Type: action.Kind, - Spec: string(specStr), - Status: &model.ActionStatus{ - Conditions: convertConditions(action.Status.Conditions), - }, + ID: action.Name, + Type: action.Kind, + Spec: string(specStr), + Conditions: convertConditions(action.Status.Conditions), }) } @@ -269,12 +257,10 @@ func (r *computePlatformResolver) Actions(ctx context.Context, obj *model.Comput return nil, err } response = append(response, &model.PipelineAction{ - ID: action.Name, - Type: action.Kind, - Spec: string(specStr), - Status: &model.ActionStatus{ - Conditions: convertConditions(action.Status.Conditions), - }, + ID: action.Name, + Type: action.Kind, + Spec: string(specStr), + Conditions: convertConditions(action.Status.Conditions), }) } @@ -293,17 +279,7 @@ func (r *destinationResolver) Type(ctx context.Context, obj *model.Destination) // Conditions is the resolver for the conditions field. func (r *destinationResolver) Conditions(ctx context.Context, obj *model.Destination) ([]*model.Condition, error) { - conditions := make([]*model.Condition, 0, len(obj.Conditions)) - for _, c := range obj.Conditions { - // Add the converted Condition to the list - conditions = append(conditions, &model.Condition{ - Type: c.Type, - Status: model.ConditionStatus(c.Status), - LastTransitionTime: convertLastTransitionTime(c.LastTransitionTime), - Reason: &c.Reason, - Message: &c.Message, - }) - } + conditions := convertConditions(obj.Conditions) return conditions, nil } diff --git a/frontend/webapp/containers/main/actions/action-drawer/index.tsx b/frontend/webapp/containers/main/actions/action-drawer/index.tsx index 7235fb3c7..f01dd24da 100644 --- a/frontend/webapp/containers/main/actions/action-drawer/index.tsx +++ b/frontend/webapp/containers/main/actions/action-drawer/index.tsx @@ -125,7 +125,7 @@ export const ActionDrawer: React.FC = () => { ) : ( - + )} diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index 364c748a8..674c61246 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -60,14 +60,12 @@ export const GET_COMPUTE_PLATFORM = gql` id type spec - status { - conditions { - status - type - reason - message - lastTransitionTime - } + conditions { + status + type + reason + message + lastTransitionTime } } instrumentationRules { diff --git a/frontend/webapp/types/actions.ts b/frontend/webapp/types/actions.ts index fa7fdccfa..4a914cc8c 100644 --- a/frontend/webapp/types/actions.ts +++ b/frontend/webapp/types/actions.ts @@ -72,9 +72,7 @@ export interface ActionData { id: string; type: ActionsType; spec: ActionItem | string; - status: { - conditions: Condition[]; - }; + conditions: Condition[]; } export interface ActionDataParsed extends ActionData { From 09563f1d6ddc016e39b649cf5d2ec171dfe0f708 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 13:42:42 +0200 Subject: [PATCH 042/259] fix: build error --- .../main/actions/action-drawer/build-drawer-item.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/webapp/containers/main/actions/action-drawer/build-drawer-item.ts b/frontend/webapp/containers/main/actions/action-drawer/build-drawer-item.ts index fdb5fd70e..fb2742509 100644 --- a/frontend/webapp/containers/main/actions/action-drawer/build-drawer-item.ts +++ b/frontend/webapp/containers/main/actions/action-drawer/build-drawer-item.ts @@ -3,7 +3,7 @@ import type { ActionDataParsed, ActionInput } from '@/types'; const buildDrawerItem = (id: string, formData: ActionInput, drawerItem: ActionDataParsed): ActionDataParsed => { const { type, name, notes, signals, disable, details } = formData; - const { status } = drawerItem; + const { conditions } = drawerItem; return { id, @@ -15,7 +15,7 @@ const buildDrawerItem = (id: string, formData: ActionInput, drawerItem: ActionDa disabled: disable, ...safeJsonParse(details, {}), }, - status, + conditions, }; }; From 70b1ed036dc81d541a7c757a26fcfbfd7afa1772 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 14:35:31 +0200 Subject: [PATCH 043/259] feat: add source management for workloads and update GraphQL queries --- cli/cmd/resources/ui.go | 9 ++- frontend/services/namespaces.go | 2 +- frontend/services/utils.go | 79 ++++++++++++++----- .../graphql/queries/compute-platform.ts | 24 +----- frontend/webapp/types/compute-platform.ts | 3 +- frontend/webapp/types/sources.ts | 9 --- 6 files changed, 72 insertions(+), 54 deletions(-) diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index de9a2ca14..6c50853f7 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -235,9 +235,14 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Resources: []string{"instrumentedapplications", "instrumentationinstances", "instrumentationconfigs"}, Verbs: []string{"get", "list"}, }, - { // Needed to watch Odigos entities + { // Needed to instrument / uninstrument applications + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources"}, + Verbs: []string{"get", "list", "create", "delete"}, + }, + { // Needed to watch instrumented applications APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances"}, + Resources: []string{"instrumentedapplications", "instrumentationinstances", "sources"}, Verbs: []string{"watch"}, }, }, diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 534249c90..f06e3355b 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -181,7 +181,7 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo g.Go(func() error { // Only label selected sources, ignore the rest if currWorkload.Selected != nil { - return setWorkloadInstrumentationLabel(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + return ToggleWorkloadSource(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } return nil }) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index 71218b665..57a8a94d6 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -8,11 +8,12 @@ import ( "strings" "time" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" ) const cdnUrl = "https://d15jtxgb40qetw.cloudfront.net" @@ -21,24 +22,6 @@ func GetImageURL(image string) string { return path.Join(cdnUrl, image) } -func setWorkloadInstrumentationLabel(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { - jsonMergePatchData := GetJsonMergePatchForInstrumentationLabel(enabled) - - switch workloadKind { - case WorkloadKindDeployment: - _, err := kube.DefaultClient.AppsV1().Deployments(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindStatefulSet: - _, err := kube.DefaultClient.AppsV1().StatefulSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindDaemonSet: - _, err := kube.DefaultClient.AppsV1().DaemonSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - default: - return errors.New("unsupported workload kind " + string(workloadKind)) - } -} - func ConvertFieldsToString(fields map[string]string) string { if len(fields) == 0 { return "" @@ -86,3 +69,61 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } return latestStatusTime.Time.Format(time.RFC3339) } + +func CreateWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + newSource := &v1alpha1.Source{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "source-", + }, + Spec: v1alpha1.SourceSpec{ + Workload: workload.PodWorkload{ + Namespace: nsName, + Name: workloadName, + Kind: workload.WorkloadKind(workloadKind), + }, + }, + Status: v1alpha1.SourceStatus{ + Conditions: []metav1.Condition{}, + }, + } + + switch workloadKind { + case WorkloadKindDeployment: + case WorkloadKindStatefulSet: + case WorkloadKindDaemonSet: + _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) + return err + default: + return errors.New("unsupported workload kind " + string(workloadKind)) + } + + return nil +} + +func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + switch workloadKind { + case WorkloadKindDeployment: + case WorkloadKindStatefulSet: + case WorkloadKindDaemonSet: + err := kube.DefaultClient.OdigosClient.Sources("").Delete(ctx, workloadName, metav1.DeleteOptions{}) + return err + default: + return errors.New("unsupported workload kind " + string(workloadKind)) + } + + return nil +} + +func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { + if enabled == nil { + return errors.New("enabled must be provided") + } + + if *enabled { + CreateWorkloadSource(ctx, nsName, workloadName, workloadKind) + } else { + DeleteWorkloadSource(ctx, nsName, workloadName, workloadKind) + } + + return nil +} diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index b914d7e09..b75b562c7 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -3,6 +3,9 @@ import { gql } from '@apollo/client'; export const GET_COMPUTE_PLATFORM = gql` query GetComputePlatform { computePlatform { + k8sActualNamespaces { + name + } k8sActualSources { name kind @@ -25,24 +28,6 @@ export const GET_COMPUTE_PLATFORM = gql` } } } - sources { - spec { - workload { - kind - name - namespace - } - } - status { - conditions { - status - type - reason - message - lastTransitionTime - } - } - } destinations { id name @@ -114,9 +99,6 @@ export const GET_COMPUTE_PLATFORM = gql` } } } - k8sActualNamespaces { - name - } } } `; diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index 94fb47a97..e4c65f2d4 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -1,4 +1,4 @@ -import type { K8sActualSource, Source } from './sources'; +import type { K8sActualSource } from './sources'; import type { ActualDestination } from './destinations'; import type { ActionData, ActionDataParsed } from './actions'; import type { InstrumentationRuleSpec, InstrumentationRuleSpecMapped } from './instrumentation-rules'; @@ -15,7 +15,6 @@ interface ComputePlatformData { k8sActualNamespace?: K8sActualNamespace; k8sActualNamespaces: K8sActualNamespace[]; k8sActualSources: K8sActualSource[]; - sources: Source[]; destinations: ActualDestination[]; actions: ActionData[]; instrumentationRules: InstrumentationRuleSpec[]; diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index 6a8a25334..f2ce90015 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -36,12 +36,3 @@ export type PersistSourcesArray = { name: string; selected: boolean; }; - -export interface Source { - spec: { - workload: WorkloadId; - }; - status: { - conditions: Condition[]; - }; -} From 6529165ef6731d9ad23ac620de4720f9efbac47d Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 14:45:02 +0200 Subject: [PATCH 044/259] refactor: remove sources field and related types from GraphQL schema --- frontend/gqlgen.yml | 2 - frontend/graph/generated.go | 532 ----------------------------- frontend/graph/model/models_gen.go | 14 - frontend/graph/schema.graphqls | 14 - frontend/graph/schema.resolvers.go | 26 -- 5 files changed, 588 deletions(-) diff --git a/frontend/gqlgen.yml b/frontend/gqlgen.yml index fda4093fe..f5dedfb41 100644 --- a/frontend/gqlgen.yml +++ b/frontend/gqlgen.yml @@ -52,8 +52,6 @@ models: resolver: true k8sActualSources: resolver: true - sources: - resolver: true destinations: resolver: true actions: diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index 5dc7c723f..a0642bc58 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -87,7 +87,6 @@ type ComplexityRoot struct { K8sActualNamespaces func(childComplexity int) int K8sActualSource func(childComplexity int, name *string, namespace *string, kind *string) int K8sActualSources func(childComplexity int) int - Sources func(childComplexity int) int } Condition struct { @@ -453,11 +452,6 @@ type ComplexityRoot struct { TotalDataSent func(childComplexity int) int } - Source struct { - Spec func(childComplexity int) int - Status func(childComplexity int) int - } - SourceAnalyze struct { InstrumentationConfig func(childComplexity int) int InstrumentationDevice func(childComplexity int) int @@ -479,14 +473,6 @@ type ComplexityRoot struct { RuntimeVersion func(childComplexity int) int } - SourceSpec struct { - Workload func(childComplexity int) int - } - - SourceStatus struct { - Conditions func(childComplexity int) int - } - SupportedSignals struct { Logs func(childComplexity int) int Metrics func(childComplexity int) int @@ -507,7 +493,6 @@ type ComputePlatformResolver interface { K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) - Sources(ctx context.Context, obj *model.ComputePlatform) ([]*model.Source, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) InstrumentationRules(ctx context.Context, obj *model.ComputePlatform) ([]*model.InstrumentationRule, error) @@ -764,13 +749,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ComputePlatform.K8sActualSources(childComplexity), true - case "ComputePlatform.sources": - if e.complexity.ComputePlatform.Sources == nil { - break - } - - return e.complexity.ComputePlatform.Sources(childComplexity), true - case "Condition.lastTransitionTime": if e.complexity.Condition.LastTransitionTime == nil { break @@ -2342,20 +2320,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SingleSourceMetricsResponse.TotalDataSent(childComplexity), true - case "Source.spec": - if e.complexity.Source.Spec == nil { - break - } - - return e.complexity.Source.Spec(childComplexity), true - - case "Source.status": - if e.complexity.Source.Status == nil { - break - } - - return e.complexity.Source.Status(childComplexity), true - case "SourceAnalyze.instrumentationConfig": if e.complexity.SourceAnalyze.InstrumentationConfig == nil { break @@ -2461,20 +2425,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SourceContainerRuntimeDetails.RuntimeVersion(childComplexity), true - case "SourceSpec.workload": - if e.complexity.SourceSpec.Workload == nil { - break - } - - return e.complexity.SourceSpec.Workload(childComplexity), true - - case "SourceStatus.conditions": - if e.complexity.SourceStatus.Conditions == nil { - break - } - - return e.complexity.SourceStatus.Conditions(childComplexity), true - case "SupportedSignals.logs": if e.complexity.SupportedSignals.Logs == nil { break @@ -4290,56 +4240,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont return fc, nil } -func (ec *executionContext) _ComputePlatform_sources(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_sources(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().Sources(rctx, obj) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.Source) - fc.Result = res - return ec.marshalNSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_ComputePlatform_sources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ComputePlatform", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "spec": - return ec.fieldContext_Source_spec(ctx, field) - case "status": - return ec.fieldContext_Source_status(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Source", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _ComputePlatform_destinations(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ComputePlatform_destinations(ctx, field) if err != nil { @@ -13553,8 +13453,6 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context return ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) case "k8sActualSources": return ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) - case "sources": - return ec.fieldContext_ComputePlatform_sources(ctx, field) case "destinations": return ec.fieldContext_ComputePlatform_destinations(ctx, field) case "actions": @@ -14846,102 +14744,6 @@ func (ec *executionContext) fieldContext_SingleSourceMetricsResponse_throughput( return fc, nil } -func (ec *executionContext) _Source_spec(ctx context.Context, field graphql.CollectedField, obj *model.Source) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Source_spec(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Spec, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.SourceSpec) - fc.Result = res - return ec.marshalNSourceSpec2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceSpec(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Source_spec(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Source", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "workload": - return ec.fieldContext_SourceSpec_workload(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type SourceSpec", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _Source_status(ctx context.Context, field graphql.CollectedField, obj *model.Source) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Source_status(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Status, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.SourceStatus) - fc.Result = res - return ec.marshalNSourceStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceStatus(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Source_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Source", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "conditions": - return ec.fieldContext_SourceStatus_conditions(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type SourceStatus", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _SourceAnalyze_name(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SourceAnalyze_name(ctx, field) if err != nil { @@ -15672,111 +15474,6 @@ func (ec *executionContext) fieldContext_SourceContainerRuntimeDetails_otherAgen return fc, nil } -func (ec *executionContext) _SourceSpec_workload(ctx context.Context, field graphql.CollectedField, obj *model.SourceSpec) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceSpec_workload(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Workload, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.PodWorkload) - fc.Result = res - return ec.marshalNPodWorkload2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPodWorkload(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_SourceSpec_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "SourceSpec", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "namespace": - return ec.fieldContext_PodWorkload_namespace(ctx, field) - case "name": - return ec.fieldContext_PodWorkload_name(ctx, field) - case "kind": - return ec.fieldContext_PodWorkload_kind(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type PodWorkload", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _SourceStatus_conditions(ctx context.Context, field graphql.CollectedField, obj *model.SourceStatus) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceStatus_conditions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Conditions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.Condition) - fc.Result = res - return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_SourceStatus_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "SourceStatus", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "status": - return ec.fieldContext_Condition_status(ctx, field) - case "type": - return ec.fieldContext_Condition_type(ctx, field) - case "reason": - return ec.fieldContext_Condition_reason(ctx, field) - case "message": - return ec.fieldContext_Condition_message(ctx, field) - case "lastTransitionTime": - return ec.fieldContext_Condition_lastTransitionTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _SupportedSignals_traces(ctx context.Context, field graphql.CollectedField, obj *model.SupportedSignals) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SupportedSignals_traces(ctx, field) if err != nil { @@ -19008,42 +18705,6 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select continue } - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "sources": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._ComputePlatform_sources(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "destinations": field := field @@ -22031,50 +21692,6 @@ func (ec *executionContext) _SingleSourceMetricsResponse(ctx context.Context, se return out } -var sourceImplementors = []string{"Source"} - -func (ec *executionContext) _Source(ctx context.Context, sel ast.SelectionSet, obj *model.Source) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, sourceImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Source") - case "spec": - out.Values[i] = ec._Source_spec(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "status": - out.Values[i] = ec._Source_status(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var sourceAnalyzeImplementors = []string{"SourceAnalyze"} func (ec *executionContext) _SourceAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.SourceAnalyze) graphql.Marshaler { @@ -22212,81 +21829,6 @@ func (ec *executionContext) _SourceContainerRuntimeDetails(ctx context.Context, return out } -var sourceSpecImplementors = []string{"SourceSpec"} - -func (ec *executionContext) _SourceSpec(ctx context.Context, sel ast.SelectionSet, obj *model.SourceSpec) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, sourceSpecImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("SourceSpec") - case "workload": - out.Values[i] = ec._SourceSpec_workload(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var sourceStatusImplementors = []string{"SourceStatus"} - -func (ec *executionContext) _SourceStatus(ctx context.Context, sel ast.SelectionSet, obj *model.SourceStatus) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, sourceStatusImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("SourceStatus") - case "conditions": - out.Values[i] = ec._SourceStatus_conditions(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var supportedSignalsImplementors = []string{"SupportedSignals"} func (ec *executionContext) _SupportedSignals(ctx context.Context, sel ast.SelectionSet, obj *model.SupportedSignals) graphql.Marshaler { @@ -24191,60 +23733,6 @@ func (ec *executionContext) marshalNSingleSourceMetricsResponse2ᚖgithubᚗcom return ec._SingleSourceMetricsResponse(ctx, sel, v) } -func (ec *executionContext) marshalNSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Source) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSource(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSource(ctx context.Context, sel ast.SelectionSet, v *model.Source) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._Source(ctx, sel, v) -} - func (ec *executionContext) marshalNSourceAnalyze2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceAnalyze(ctx context.Context, sel ast.SelectionSet, v model.SourceAnalyze) graphql.Marshaler { return ec._SourceAnalyze(ctx, sel, &v) } @@ -24269,26 +23757,6 @@ func (ec *executionContext) marshalNSourceContainerRuntimeDetails2ᚖgithubᚗco return ec._SourceContainerRuntimeDetails(ctx, sel, v) } -func (ec *executionContext) marshalNSourceSpec2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceSpec(ctx context.Context, sel ast.SelectionSet, v *model.SourceSpec) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._SourceSpec(ctx, sel, v) -} - -func (ec *executionContext) marshalNSourceStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceStatus(ctx context.Context, sel ast.SelectionSet, v *model.SourceStatus) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._SourceStatus(ctx, sel, v) -} - func (ec *executionContext) unmarshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, v interface{}) (model.SpanKind, error) { var res model.SpanKind err := res.UnmarshalGQL(v) diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index cc9928307..880c47690 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -78,7 +78,6 @@ type ComputePlatform struct { K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` K8sActualSource *K8sActualSource `json:"k8sActualSource,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` - Sources []*Source `json:"sources"` Destinations []*Destination `json:"destinations"` Actions []*PipelineAction `json:"actions"` InstrumentationRules []*InstrumentationRule `json:"instrumentationRules"` @@ -587,11 +586,6 @@ type SingleSourceMetricsResponse struct { Throughput int `json:"throughput"` } -type Source struct { - Spec *SourceSpec `json:"spec"` - Status *SourceStatus `json:"status"` -} - type SourceAnalyze struct { Name *EntityProperty `json:"name"` Kind *EntityProperty `json:"kind"` @@ -613,14 +607,6 @@ type SourceContainerRuntimeDetails struct { OtherAgent *string `json:"otherAgent,omitempty"` } -type SourceSpec struct { - Workload *PodWorkload `json:"workload"` -} - -type SourceStatus struct { - Conditions []*Condition `json:"conditions,omitempty"` -} - type TestConnectionResponse struct { Succeeded bool `json:"succeeded"` StatusCode int `json:"statusCode"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 7f1d95c22..808c9ca2c 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -106,19 +106,6 @@ input K8sDesiredSourceInput { autoInstrument: Boolean } -type SourceSpec { - workload: PodWorkload! -} - -type SourceStatus { - conditions: [Condition!] -} - -type Source { - spec: SourceSpec! - status: SourceStatus! -} - type PodWorkload { namespace: String! name: String! @@ -220,7 +207,6 @@ type ComputePlatform { k8sActualNamespaces: [K8sActualNamespace]! k8sActualSource(name: String, namespace: String, kind: String): K8sActualSource k8sActualSources: [K8sActualSource]! - sources: [Source!]! destinations: [Destination!]! actions: [PipelineAction!]! instrumentationRules: [InstrumentationRule!]! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 585a30baf..ffddced75 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -110,32 +110,6 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod return actualSources, nil } -// Sources is the resolver for the sources field. -func (r *computePlatformResolver) Sources(ctx context.Context, obj *model.ComputePlatform) ([]*model.Source, error) { - sources, err := kube.DefaultClient.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, err - } - - var result []*model.Source - for _, source := range sources.Items { - result = append(result, &model.Source{ - Spec: &model.SourceSpec{ - Workload: &model.PodWorkload{ - Namespace: source.Spec.Workload.Namespace, - Name: source.Spec.Workload.Name, - Kind: model.K8sResourceKind(source.Spec.Workload.Kind), - }, - }, - Status: &model.SourceStatus{ - Conditions: convertConditions(source.Status.Conditions), - }, - }) - } - - return result, nil -} - // Destinations is the resolver for the destinations field. func (r *computePlatformResolver) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) { odigosns := consts.DefaultOdigosNamespace From 3b522ba4af3de1885bd86459e2eec9303e1e75d1 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 14:45:34 +0200 Subject: [PATCH 045/259] refactor: simplify workload source creation and deletion logic --- frontend/services/utils.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index 57a8a94d6..d8ccf9fbc 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -87,31 +87,23 @@ func CreateWorkloadSource(ctx context.Context, nsName string, workloadName strin }, } - switch workloadKind { - case WorkloadKindDeployment: - case WorkloadKindStatefulSet: - case WorkloadKindDaemonSet: - _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) - return err - default: + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) } - return nil + _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) + return err + } func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - switch workloadKind { - case WorkloadKindDeployment: - case WorkloadKindStatefulSet: - case WorkloadKindDaemonSet: - err := kube.DefaultClient.OdigosClient.Sources("").Delete(ctx, workloadName, metav1.DeleteOptions{}) - return err - default: + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) } - return nil + err := kube.DefaultClient.OdigosClient.Sources("").Delete(ctx, workloadName, metav1.DeleteOptions{}) + return err + } func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { From 2c6a150abf4600a3a05b61515e93661c0e79e4aa Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 14:55:04 +0200 Subject: [PATCH 046/259] fix: validate workload kind in CreateWorkloadSource function --- frontend/services/utils.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index d8ccf9fbc..c2a7c85b4 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -71,6 +71,10 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } func CreateWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return errors.New("unsupported workload kind " + string(workloadKind)) + } + newSource := &v1alpha1.Source{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "source-", @@ -87,10 +91,6 @@ func CreateWorkloadSource(ctx context.Context, nsName string, workloadName strin }, } - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) - } - _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) return err From b281203260735c82a91a1731c52a684d4d216236 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 15:44:50 +0200 Subject: [PATCH 047/259] fix: use default namespace for creating and deleting workload sources --- frontend/services/utils.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index c2a7c85b4..abfcdf358 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -10,6 +10,7 @@ import ( "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -91,9 +92,8 @@ func CreateWorkloadSource(ctx context.Context, nsName string, workloadName strin }, } - _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) + _, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) return err - } func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { @@ -101,9 +101,8 @@ func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName strin return errors.New("unsupported workload kind " + string(workloadKind)) } - err := kube.DefaultClient.OdigosClient.Sources("").Delete(ctx, workloadName, metav1.DeleteOptions{}) + err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) return err - } func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { @@ -112,10 +111,8 @@ func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName strin } if *enabled { - CreateWorkloadSource(ctx, nsName, workloadName, workloadKind) + return CreateWorkloadSource(ctx, nsName, workloadName, workloadKind) } else { - DeleteWorkloadSource(ctx, nsName, workloadName, workloadKind) + return DeleteWorkloadSource(ctx, nsName, workloadName, workloadKind) } - - return nil } From d3ccd71073f078394c2cd3d2fba14efd9f5f6951 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 16:34:10 +0200 Subject: [PATCH 048/259] refactor: replace workload source functions with source CRD implementations --- frontend/services/namespaces.go | 2 +- frontend/services/sources.go | 55 +++++++++++++++++++++++---- frontend/services/utils.go | 66 --------------------------------- 3 files changed, 49 insertions(+), 74 deletions(-) diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index f06e3355b..699ed8cde 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -181,7 +181,7 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo g.Go(func() error { // Only label selected sources, ignore the rest if currWorkload.Selected != nil { - return ToggleWorkloadSource(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } return nil }) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 9549fba7a..33e993361 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -2,24 +2,22 @@ package services import ( "context" + "errors" "fmt" "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" + "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" - + "github.com/odigos-io/odigos/k8sutils/pkg/client" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" appsv1 "k8s.io/api/apps/v1" - - "github.com/odigos-io/odigos/frontend/graph/model" - - "github.com/odigos-io/odigos/k8sutils/pkg/client" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "golang.org/x/sync/errgroup" - corev1 "k8s.io/api/core/v1" ) type WorkloadKind string @@ -319,3 +317,46 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s } return annotations } + +func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return errors.New("unsupported workload kind " + string(workloadKind)) + } + + newSource := &v1alpha1.Source{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "source-", + }, + Spec: v1alpha1.SourceSpec{ + Workload: workload.PodWorkload{ + Namespace: nsName, + Name: workloadName, + Kind: workload.WorkloadKind(workloadKind), + }, + }, + } + + _, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) + return err +} + +func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return errors.New("unsupported workload kind " + string(workloadKind)) + } + + err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) + return err +} + +func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { + if enabled == nil { + return errors.New("enabled must be provided") + } + + if *enabled { + return CreateSourceCRD(ctx, nsName, workloadName, workloadKind) + } else { + return DeleteSourceCRD(ctx, nsName, workloadName, workloadKind) + } +} diff --git a/frontend/services/utils.go b/frontend/services/utils.go index abfcdf358..6d8f1603e 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -1,19 +1,12 @@ package services import ( - "context" - "errors" "fmt" "path" - "strings" "time" - "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" - "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/graph/model" - "github.com/odigos-io/odigos/frontend/kube" - "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -23,19 +16,6 @@ func GetImageURL(image string) string { return path.Join(cdnUrl, image) } -func ConvertFieldsToString(fields map[string]string) string { - if len(fields) == 0 { - return "" - } - - var parts []string - for key, value := range fields { - parts = append(parts, fmt.Sprintf("%s: %s", key, value)) - } - - return strings.Join(parts, ", ") -} - func ConvertSignals(signals []model.SignalType) ([]common.ObservabilitySignal, error) { var result []common.ObservabilitySignal for _, s := range signals { @@ -70,49 +50,3 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } return latestStatusTime.Time.Format(time.RFC3339) } - -func CreateWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) - } - - newSource := &v1alpha1.Source{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "source-", - }, - Spec: v1alpha1.SourceSpec{ - Workload: workload.PodWorkload{ - Namespace: nsName, - Name: workloadName, - Kind: workload.WorkloadKind(workloadKind), - }, - }, - Status: v1alpha1.SourceStatus{ - Conditions: []metav1.Condition{}, - }, - } - - _, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) - return err -} - -func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) - } - - err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) - return err -} - -func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { - if enabled == nil { - return errors.New("enabled must be provided") - } - - if *enabled { - return CreateWorkloadSource(ctx, nsName, workloadName, workloadKind) - } else { - return DeleteWorkloadSource(ctx, nsName, workloadName, workloadKind) - } -} From 7af2640878af977f19e15b98b02e317f57faee9b Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 18:36:46 +0200 Subject: [PATCH 049/259] feat: add GetSourceCRD function to retrieve source CRD by workload details --- frontend/services/sources.go | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 33e993361..ca5e50859 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -16,6 +16,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" "golang.org/x/sync/errgroup" ) @@ -318,6 +319,31 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } +func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return nil, errors.New("unsupported workload kind " + string(workloadKind)) + } + + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadName, + "odigos.io/workload-namespace": nsName, + "odigos.io/workload-kind": string(workloadKind), + }) + + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return nil, err + } + + crdName := sourceList.Items[0].Name + source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + return source, nil +} + func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) @@ -345,7 +371,15 @@ func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return errors.New("unsupported workload kind " + string(workloadKind)) } - err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) + if err != nil { + return err + } + if source == nil { + return errors.New("source not found" + workloadName) + } + + err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err } From 8dc557dbfb6e9e0a7f930c8d08a3c6c5f7aeb7c4 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 18:38:49 +0200 Subject: [PATCH 050/259] feat: add GetSourceCRD function to retrieve source CRD by workload details --- frontend/services/sources.go | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 33e993361..ca5e50859 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -16,6 +16,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" "golang.org/x/sync/errgroup" ) @@ -318,6 +319,31 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } +func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return nil, errors.New("unsupported workload kind " + string(workloadKind)) + } + + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadName, + "odigos.io/workload-namespace": nsName, + "odigos.io/workload-kind": string(workloadKind), + }) + + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return nil, err + } + + crdName := sourceList.Items[0].Name + source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + return source, nil +} + func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) @@ -345,7 +371,15 @@ func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return errors.New("unsupported workload kind " + string(workloadKind)) } - err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) + if err != nil { + return err + } + if source == nil { + return errors.New("source not found" + workloadName) + } + + err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err } From 2faf7122c56d5cc87a2c3988bbfd243c6748f7e2 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 18:42:53 +0200 Subject: [PATCH 051/259] fix: handle case when source is not found in GetSourceCRD and remove redundant check in DeleteSourceCRD --- frontend/services/sources.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index ca5e50859..5eef17fdd 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -334,6 +334,9 @@ func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workl if err != nil { return nil, err } + if len(sourceList.Items) == 0 { + return nil, errors.New("source not found" + workloadName) + } crdName := sourceList.Items[0].Name source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) @@ -375,9 +378,6 @@ func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo if err != nil { return err } - if source == nil { - return errors.New("source not found" + workloadName) - } err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err From a94c1506f50010fc27220ed77731cbeac5f11b0d Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Thu, 26 Dec 2024 09:55:51 +0200 Subject: [PATCH 052/259] feat: update GetSourceCRD to GetSourceCRDs for retrieving multiple sources by workload details --- frontend/services/sources.go | 55 +++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 5eef17fdd..7e49196a9 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -319,32 +319,53 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } -func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return nil, errors.New("unsupported workload kind " + string(workloadKind)) +func GetSourceCRDs(ctx context.Context, args ...interface{}) ([]*v1alpha1.Source, error) { + var nsName, workloadName string + var workloadKind WorkloadKind + if len(args) > 0 { + nsName, _ = args[0].(string) + } + if len(args) > 1 { + workloadName, _ = args[1].(string) + } + if len(args) > 2 { + workloadKind, _ = args[2].(WorkloadKind) + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return nil, errors.New("unsupported workload kind " + string(workloadKind)) + } } - selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": workloadName, - "odigos.io/workload-namespace": nsName, - "odigos.io/workload-kind": string(workloadKind), - }) + labelsSet := labels.Set{} + if nsName != "" { + labelsSet["odigos.io/workload-namespace"] = nsName + } + if workloadName != "" { + labelsSet["odigos.io/workload-name"] = workloadName + } + if string(workloadKind) != "" { + labelsSet["odigos.io/workload-kind"] = string(workloadKind) + } - sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: selector.String()}) + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labelsSet).String()}) if err != nil { return nil, err } - if len(sourceList.Items) == 0 { + if workloadName != "" && len(sourceList.Items) == 0 { return nil, errors.New("source not found" + workloadName) } - crdName := sourceList.Items[0].Name - source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) - if err != nil { - return nil, err + var sources []*v1alpha1.Source + + for _, crd := range sourceList.Items { + crdName := crd.Name + source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + sources = append(sources, source) } - return source, nil + return sources, nil } func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { @@ -374,12 +395,12 @@ func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return errors.New("unsupported workload kind " + string(workloadKind)) } - source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) + sources, err := GetSourceCRDs(ctx, nsName, workloadName, workloadKind) if err != nil { return err } - err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) + err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, sources[0].Name, metav1.DeleteOptions{}) return err } From 89fa6fa9f0ac21878102b7025b09507a8ccb86b6 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Thu, 26 Dec 2024 10:34:04 +0200 Subject: [PATCH 053/259] feat: implement SetWorkloadInstrumentationLabel function and update SyncWorkloadsInNamespace to handle source labeling --- frontend/graph/schema.resolvers.go | 15 ++++++++++++--- frontend/services/namespaces.go | 10 +++++++++- frontend/services/sources.go | 20 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index ffddced75..aab9c70dd 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -20,6 +20,7 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" "github.com/odigos-io/odigos/k8sutils/pkg/workload" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) @@ -81,14 +82,21 @@ func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *mode // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { + // sourceList, err := services.GetSourceCRDs(ctx) + // if err != nil { + // return nil, err + // } + + // Initialize an empty list of K8sActualSource + var actualSources []*model.K8sActualSource + + // for _, source := range sourceList { + // TODO: remove "InstrumentedApplications" once we're ready to move over to "InstrumentationConfigs" combined with "Source CRDs" instrumentedApplications, err := kube.DefaultClient.OdigosClient.InstrumentedApplications("").List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } - // Initialize an empty list of K8sActualSource - var actualSources []*model.K8sActualSource - // Convert each instrumented application to the K8sActualSource type for _, app := range instrumentedApplications.Items { actualSource := instrumentedApplicationToActualSource(app) @@ -106,6 +114,7 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod actualSource.ReportedName = &reportedName actualSources = append(actualSources, actualSource) } + // } return actualSources, nil } diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 699ed8cde..602120596 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -181,7 +181,15 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo g.Go(func() error { // Only label selected sources, ignore the rest if currWorkload.Selected != nil { - return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + err := ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + if err != nil { + return err + } + // TODO: remove this after a fix was made in the backend to correctly handle the InstrumentedApplication on-create Source CRD + err = SetWorkloadInstrumentationLabel(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + if err != nil { + return err + } } return nil }) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 7e49196a9..c44a6ceda 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -17,6 +17,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" labels "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" "golang.org/x/sync/errgroup" ) @@ -415,3 +416,22 @@ func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, wo return DeleteSourceCRD(ctx, nsName, workloadName, workloadKind) } } + +// TODO: remove this after a fix was made in the backend to correctly handle the InstrumentedApplication on-create Source CRD +func SetWorkloadInstrumentationLabel(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { + jsonMergePatchData := GetJsonMergePatchForInstrumentationLabel(enabled) + + switch workloadKind { + case WorkloadKindDeployment: + _, err := kube.DefaultClient.AppsV1().Deployments(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) + return err + case WorkloadKindStatefulSet: + _, err := kube.DefaultClient.AppsV1().StatefulSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) + return err + case WorkloadKindDaemonSet: + _, err := kube.DefaultClient.AppsV1().DaemonSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) + return err + default: + return errors.New("unsupported workload kind " + string(workloadKind)) + } +} From fe4895d9171b522ade956c3fc33a741e96bcd00c Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Thu, 26 Dec 2024 17:37:32 +0200 Subject: [PATCH 054/259] refactor: rename GetSourceCRDs to GetAllSourceCRDs and update related functions for clarity --- frontend/graph/schema.resolvers.go | 2 +- frontend/services/sources.go | 68 ++++++++++++++---------------- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index aab9c70dd..feb57ef33 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -82,7 +82,7 @@ func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *mode // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { - // sourceList, err := services.GetSourceCRDs(ctx) + // sourceList, err := services.GetAllSourceCRDs(ctx) // if err != nil { // return nil, err // } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index c44a6ceda..a690a3183 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -320,40 +320,11 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } -func GetSourceCRDs(ctx context.Context, args ...interface{}) ([]*v1alpha1.Source, error) { - var nsName, workloadName string - var workloadKind WorkloadKind - if len(args) > 0 { - nsName, _ = args[0].(string) - } - if len(args) > 1 { - workloadName, _ = args[1].(string) - } - if len(args) > 2 { - workloadKind, _ = args[2].(WorkloadKind) - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return nil, errors.New("unsupported workload kind " + string(workloadKind)) - } - } - - labelsSet := labels.Set{} - if nsName != "" { - labelsSet["odigos.io/workload-namespace"] = nsName - } - if workloadName != "" { - labelsSet["odigos.io/workload-name"] = workloadName - } - if string(workloadKind) != "" { - labelsSet["odigos.io/workload-kind"] = string(workloadKind) - } - - sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labelsSet).String()}) +func GetAllSourceCRDs(ctx context.Context) ([]*v1alpha1.Source, error) { + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } - if workloadName != "" && len(sourceList.Items) == 0 { - return nil, errors.New("source not found" + workloadName) - } var sources []*v1alpha1.Source @@ -369,7 +340,30 @@ func GetSourceCRDs(ctx context.Context, args ...interface{}) ([]*v1alpha1.Source return sources, nil } -func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { +func getSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-namespace": nsName, + "odigos.io/workload-name": workloadName, + "odigos.io/workload-kind": string(workloadKind), + }).String()}) + + if err != nil { + return nil, err + } + if len(sourceList.Items) == 0 { + return nil, errors.New("source not found" + workloadName) + } + if len(sourceList.Items) > 1 { + return nil, errors.New("too many sources" + workloadName) + } + + crdName := sourceList.Items[0].Name + source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + + return source, err +} + +func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) } @@ -391,17 +385,17 @@ func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } -func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { +func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) } - sources, err := GetSourceCRDs(ctx, nsName, workloadName, workloadKind) + source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if err != nil { return err } - err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, sources[0].Name, metav1.DeleteOptions{}) + err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err } @@ -411,9 +405,9 @@ func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, wo } if *enabled { - return CreateSourceCRD(ctx, nsName, workloadName, workloadKind) + return createSourceCRD(ctx, nsName, workloadName, workloadKind) } else { - return DeleteSourceCRD(ctx, nsName, workloadName, workloadKind) + return deleteSourceCRD(ctx, nsName, workloadName, workloadKind) } } From 9136f3b31cf92a80dab7a7cf71ef0969bac77248 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 27 Dec 2024 09:42:32 -0500 Subject: [PATCH 055/259] Feedback --- Makefile | 2 +- api/odigos/v1alpha1/source_types.go | 7 +++-- .../deleteinstrumentedapplication/manager.go | 4 ++- .../source_controller.go | 30 ++----------------- .../controllers/startlangdetection/manager.go | 1 + .../startlangdetection/source_controller.go | 30 +++++++------------ k8sutils/pkg/consts/consts.go | 10 +++++++ k8sutils/pkg/predicate/creation.go | 27 +++++++++++++++++ 8 files changed, 60 insertions(+), 51 deletions(-) create mode 100644 k8sutils/pkg/predicate/creation.go diff --git a/Makefile b/Makefile index 67d0afb4b..e04aad64c 100644 --- a/Makefile +++ b/Makefile @@ -202,7 +202,7 @@ cli-install: .PHONY: cli-uninstall cli-uninstall: - @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" + @echo "Uninstalling odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . uninstall .PHONY: cli-upgrade diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index f3969810d..157071c55 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) @@ -72,9 +73,9 @@ type SourceList struct { func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (*SourceList, error) { sourceList := &SourceList{} selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": obj.GetName(), - "odigos.io/workload-namespace": obj.GetNamespace(), - "odigos.io/workload-kind": obj.GetObjectKind().GroupVersionKind().Kind, + consts.WorkloadNameLabel: obj.GetName(), + consts.WorkloadNamespaceLabel: obj.GetNamespace(), + consts.WorkloadKindLabel: obj.GetObjectKind().GroupVersionKind().Kind, }) err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) if err != nil { diff --git a/instrumentor/controllers/deleteinstrumentedapplication/manager.go b/instrumentor/controllers/deleteinstrumentedapplication/manager.go index e107fd35a..da00e9827 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/manager.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/manager.go @@ -2,6 +2,8 @@ package deleteinstrumentedapplication import ( odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/predicate" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" @@ -77,7 +79,7 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. ControllerManagedBy(mgr). Named("deleteinstrumentedapplication-source"). - WithEventFilter(&SourceDeletedPredicate{}). + WithEventFilter(&k8sutils.OnlyUpdatesPredicate{}). For(&odigosv1.Source{}). Complete(&SourceReconciler{ Client: mgr.GetClient(), diff --git a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go index 0f611c6ef..a60656046 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go @@ -21,40 +21,16 @@ import ( "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/log" ) -// Added by startlangdetection controller when Source is created -var instrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" - -type SourceDeletedPredicate struct{} - -func (i *SourceDeletedPredicate) Create(_ event.CreateEvent) bool { - return false -} - -func (i *SourceDeletedPredicate) Update(_ event.UpdateEvent) bool { - // We are actually looking for Update events that add a DeletionTimestamp - // This is so we can still get the workload from the Source object and remove the finalizer - // Then actual deletion of the Source will proceed - return true -} - -func (i *SourceDeletedPredicate) Delete(_ event.DeleteEvent) bool { - return true -} - -func (i *SourceDeletedPredicate) Generic(_ event.GenericEvent) bool { - return false -} - type SourceReconciler struct { client.Client Scheme *runtime.Scheme @@ -79,8 +55,8 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } - if controllerutil.ContainsFinalizer(source, instrumentedApplicationFinalizer) { - controllerutil.RemoveFinalizer(source, instrumentedApplicationFinalizer) + if controllerutil.ContainsFinalizer(source, consts.InstrumentedApplicationFinalizer) { + controllerutil.RemoveFinalizer(source, consts.InstrumentedApplicationFinalizer) if err := r.Update(ctx, source); err != nil { return ctrl.Result{}, err } diff --git a/instrumentor/controllers/startlangdetection/manager.go b/instrumentor/controllers/startlangdetection/manager.go index 0528db3ae..6504edcbd 100644 --- a/instrumentor/controllers/startlangdetection/manager.go +++ b/instrumentor/controllers/startlangdetection/manager.go @@ -80,6 +80,7 @@ func SetupWithManager(mgr ctrl.Manager) error { ControllerManagedBy(mgr). Named("startlangdetection-source"). For(&v1alpha1.Source{}). + WithEventFilter(predicate.Or(&odigospredicate.CreationPredicate{}, &odigospredicate.OnlyUpdatesPredicate{})). Complete(&SourceReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 6894e7a55..6e4269981 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -11,19 +11,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" + k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/utils" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -var ( - sourceFinalizer = "odigos.io/source-finalizer" - // TODO: Needed until InstrumentedApplication is removed - instrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" - - workloadNameLabel = "odigos.io/workload-name" - workloadNamespaceLabel = "odigos.io/workload-namespace" - workloadKindLabel = "odigos.io/workload-kind" -) - type SourceReconciler struct { client.Client Scheme *runtime.Scheme @@ -47,20 +39,20 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr instConfigName := workload.CalculateWorkloadRuntimeObjectName(source.Spec.Workload.Name, source.Spec.Workload.Kind) if source.DeletionTimestamp.IsZero() { - if !controllerutil.ContainsFinalizer(source, sourceFinalizer) { - controllerutil.AddFinalizer(source, sourceFinalizer) + if !controllerutil.ContainsFinalizer(source, consts.SourceFinalizer) { + controllerutil.AddFinalizer(source, consts.SourceFinalizer) // Removed by deleteinstrumentedapplication controller - controllerutil.AddFinalizer(source, instrumentedApplicationFinalizer) + controllerutil.AddFinalizer(source, consts.InstrumentedApplicationFinalizer) if source.Labels == nil { source.Labels = make(map[string]string) } - source.Labels[workloadNameLabel] = source.Spec.Workload.Name - source.Labels[workloadNamespaceLabel] = source.Spec.Workload.Namespace - source.Labels[workloadKindLabel] = string(source.Spec.Workload.Kind) + source.Labels[consts.WorkloadNameLabel] = source.Spec.Workload.Name + source.Labels[consts.WorkloadNamespaceLabel] = source.Spec.Workload.Namespace + source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) if err := r.Update(ctx, source); err != nil { - return ctrl.Result{}, err + return k8sutils.K8SUpdateErrorHandler(err) } err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) @@ -68,11 +60,11 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } } else { // Source is being deleted - if controllerutil.ContainsFinalizer(source, sourceFinalizer) { + if controllerutil.ContainsFinalizer(source, consts.SourceFinalizer) { // Remove the finalizer first, because if the InstrumentationConfig is not found we // will deadlock on the finalizer never getting removed. // On the other hand, this could end up deleting a Source with an orphaned InstrumentationConfig. - controllerutil.RemoveFinalizer(source, sourceFinalizer) + controllerutil.RemoveFinalizer(source, consts.SourceFinalizer) if err := r.Update(ctx, source); err != nil { return ctrl.Result{}, err } diff --git a/k8sutils/pkg/consts/consts.go b/k8sutils/pkg/consts/consts.go index 686370f2a..7b17a3fcf 100644 --- a/k8sutils/pkg/consts/consts.go +++ b/k8sutils/pkg/consts/consts.go @@ -61,3 +61,13 @@ var ( // this value must be in sync with the one defined in the kubeVersion field in Chart.yaml MinK8SVersionForInstallation = version.MustParse("v1.20.15-0") ) + +const ( + SourceFinalizer = "odigos.io/source-finalizer" + // TODO: Needed until InstrumentedApplication is removed + InstrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" + + WorkloadNameLabel = "odigos.io/workload-name" + WorkloadNamespaceLabel = "odigos.io/workload-namespace" + WorkloadKindLabel = "odigos.io/workload-kind" +) diff --git a/k8sutils/pkg/predicate/creation.go b/k8sutils/pkg/predicate/creation.go new file mode 100644 index 000000000..d34fc88fe --- /dev/null +++ b/k8sutils/pkg/predicate/creation.go @@ -0,0 +1,27 @@ +package predicate + +import ( + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// CreationPredicate only allows delete events. +type CreationPredicate struct{} + +func (i CreationPredicate) Create(e event.CreateEvent) bool { + return true +} + +func (i CreationPredicate) Update(e event.UpdateEvent) bool { + return false +} + +func (i CreationPredicate) Delete(e event.DeleteEvent) bool { + return false +} + +func (i CreationPredicate) Generic(e event.GenericEvent) bool { + return false +} + +var _ predicate.Predicate = &DeletionPredicate{} \ No newline at end of file From bd571474fabcd67e8a7eddabef089fdc31e4308e Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 27 Dec 2024 10:59:48 -0500 Subject: [PATCH 056/259] Add Source chainsaw test --- tests/e2e/source/01-sources.yaml | 54 +++++++ tests/e2e/source/chainsaw-test.yaml | 150 ++++++++++++++++++ .../source/tracesql/context-propagation.yaml | 13 ++ .../source/tracesql/resource-attributes.yaml | 14 ++ .../e2e/source/tracesql/span-attributes.yaml | 18 +++ tests/e2e/source/tracesql/wait-for-trace.yaml | 11 ++ 6 files changed, 260 insertions(+) create mode 100644 tests/e2e/source/01-sources.yaml create mode 100644 tests/e2e/source/chainsaw-test.yaml create mode 100644 tests/e2e/source/tracesql/context-propagation.yaml create mode 100644 tests/e2e/source/tracesql/resource-attributes.yaml create mode 100644 tests/e2e/source/tracesql/span-attributes.yaml create mode 100644 tests/e2e/source/tracesql/wait-for-trace.yaml diff --git a/tests/e2e/source/01-sources.yaml b/tests/e2e/source/01-sources.yaml new file mode 100644 index 000000000..3b82128ba --- /dev/null +++ b/tests/e2e/source/01-sources.yaml @@ -0,0 +1,54 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: frontend + namespace: default +spec: + workload: + name: frontend + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: coupon + namespace: default +spec: + workload: + name: coupon + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: inventory + namespace: default +spec: + workload: + name: inventory + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: pricing + namespace: default +spec: + workload: + name: pricing + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: membership + namespace: default +spec: + workload: + name: membership + namespace: default + kind: Deployment diff --git a/tests/e2e/source/chainsaw-test.yaml b/tests/e2e/source/chainsaw-test.yaml new file mode 100644 index 000000000..7e3e4ff30 --- /dev/null +++ b/tests/e2e/source/chainsaw-test.yaml @@ -0,0 +1,150 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: source +spec: + description: This e2e test runs a multi-apps scenario with Source instrumentation + skipDelete: true + steps: + - name: Prepare destination + try: + - script: + timeout: 60s + content: | + helm repo add grafana https://grafana.github.io/helm-charts + helm repo update + helm install e2e-tests grafana/tempo -n traces --create-namespace --set tempo.storage.trace.block.version=vParquet4 \ + --set tempo.ingester.trace_idle_period=5s \ + --set tempo.ingester.max_block_duration=20s \ + --version 1.10.1 + - assert: + file: ../../common/assert/tempo-running.yaml + - name: Wait for destination to be ready + try: + - script: + timeout: 60s + content: ../../common/wait_for_dest.sh + - name: Install Odigos + try: + - script: + content: | + if [ "$MODE" = "cross-cloud-tests" ]; then + ../../../cli/odigos install --namespace odigos-test --version "$COMMIT_HASH" --image-prefix=public.ecr.aws/y2v0v6s7 + else + ../../../cli/odigos install --namespace odigos-test --version e2e-test + fi + timeout: 60s + - assert: + file: ../../common/assert/odigos-installed.yaml + + - name: Install Demo App + try: + - script: + timeout: 200s + content: | + if [ "$MODE" != "cross-cloud-tests" ]; then + docker pull keyval/odigos-demo-inventory:v0.1 + docker pull keyval/odigos-demo-membership:v0.1 + docker pull keyval/odigos-demo-coupon:v0.1 + docker pull keyval/odigos-demo-inventory:v0.1 + docker pull keyval/odigos-demo-frontend:v0.2 + kind load docker-image keyval/odigos-demo-inventory:v0.1 + kind load docker-image keyval/odigos-demo-membership:v0.1 + kind load docker-image keyval/odigos-demo-coupon:v0.1 + kind load docker-image keyval/odigos-demo-inventory:v0.1 + kind load docker-image keyval/odigos-demo-frontend:v0.2 + else + echo "Skipping docker pull and kind load for cross-cloud-tests mode" + fi + - apply: + file: ../../common/apply/install-simple-demo.yaml + - script: + timeout: 70s + content: | + kubectl wait --for=condition=ready pod -l app=frontend --timeout=60s + kubectl wait --for=condition=ready pod -l app=coupon --timeout=60s + kubectl wait --for=condition=ready pod -l app=inventory --timeout=60s + kubectl wait --for=condition=ready pod -l app=pricing --timeout=60s + kubectl wait --for=condition=ready pod -l app=membership --timeout=60s + - assert: + file: ../../common/assert/simple-demo-installed.yaml + - name: Instrument Deployments + try: + - apply: + file: 01-sources.yaml + - name: Assert Runtime Detected + try: + - assert: + timeout: 2m + file: ../../common/assert/simple-demo-runtime-detected.yaml + - name: Add Destination + try: + - apply: + file: ../../common/apply/add-tempo-traces-destination.yaml + - name: Odigos pipeline ready + try: + - assert: + file: ../../common/assert/pipeline-ready.yaml + - name: Simple-demo instrumented after destination added + try: + - assert: + file: ../../common/assert/simple-demo-instrumented.yaml + - name: Generate Traffic + try: + - script: + timeout: 300s + content: | + # Apply the job + kubectl apply -f ../../common/apply/generate-traffic-job.yaml + + # Wait for the job to complete + job_name=$(kubectl get -f ../../common/apply/generate-traffic-job.yaml -o=jsonpath='{.metadata.name}') + kubectl wait --for=condition=complete job/$job_name + + # Delete the job + kubectl delete -f ../../common/apply/generate-traffic-job.yaml + + while true; do + # wait for traces to be available + sleep 8 + + # Run the wait-for-trace script + echo "Running TraceQL test at $(date)" + ../../common/traceql_runner.sh tracesql/wait-for-trace.yaml + + if [ $? -eq 0 ]; then + break + else + ../../common/flush_traces.sh + sleep 5 + fi + done + - name: Verify Trace - Context Propagation + try: + - script: + timeout: 30s + content: | + ../../common/traceql_runner.sh tracesql/context-propagation.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Verify Trace - Resource Attributes + try: + - script: + content: | + ../../common/traceql_runner.sh tracesql/resource-attributes.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Verify Trace - Span Attributes + try: + - script: + timeout: 60s + content: | + ../../common/traceql_runner.sh tracesql/span-attributes.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system diff --git a/tests/e2e/source/tracesql/context-propagation.yaml b/tests/e2e/source/tracesql/context-propagation.yaml new file mode 100644 index 000000000..9c463f9b3 --- /dev/null +++ b/tests/e2e/source/tracesql/context-propagation.yaml @@ -0,0 +1,13 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: This test checks if the context propagation is working correctly between different languages +query: | + { resource.service.name = "frontend" && resource.telemetry.sdk.language = "java" && + span.http.request.method = "POST" && span.http.route = "/buy" && span:kind = server } + >> ( + { resource.service.name = "pricing" && resource.telemetry.sdk.language = "dotnet" } && + { resource.service.name = "inventory" && resource.telemetry.sdk.language = "python" } && + ({ resource.service.name = "coupon" && resource.telemetry.sdk.language = "nodejs" } + >> { resource.service.name = "membership" && resource.telemetry.sdk.language = "go" })) +expected: + count: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/resource-attributes.yaml b/tests/e2e/source/tracesql/resource-attributes.yaml new file mode 100644 index 000000000..f6b56aef5 --- /dev/null +++ b/tests/e2e/source/tracesql/resource-attributes.yaml @@ -0,0 +1,14 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: | + This test check the following resource attributes: + A. odigos.version attribute exists on all spans and has the correct value + B. Kubernetes attributes are correctly set on all spans + At the time of writing this test, TraceQL api does not support not equal to nil so we use regex instead. +query: | + { resource.odigos.version !~ ".*" || + resource.k8s.deployment.name !~ ".*" || + resource.k8s.node.name !~ "(kind-control-plane|aks-.*)" || + resource.k8s.pod.name !~ ".*" } +expected: + count: 0 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/span-attributes.yaml b/tests/e2e/source/tracesql/span-attributes.yaml new file mode 100644 index 000000000..d508d4a39 --- /dev/null +++ b/tests/e2e/source/tracesql/span-attributes.yaml @@ -0,0 +1,18 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: | + This test checks the span attributes for a specific trace. + TODO - JS, Python and DotNet SDK are not generating data in latest semconv. add additional checks when they are updated. +query: | + { resource.service.name = "frontend" && resource.telemetry.sdk.language = "java" && + span.http.request.method = "POST" && span.http.route = "/buy" && span:kind = server && + span.http.response.status_code = 200 && span.url.query = "id=123" } + >> ( + { resource.service.name = "pricing" && resource.telemetry.sdk.language = "dotnet" && span:kind = server } && + { resource.service.name = "inventory" && resource.telemetry.sdk.language = "python" && span:kind = server } && + ({ resource.service.name = "coupon" && resource.telemetry.sdk.language = "nodejs" && span:kind = server } + >> { resource.service.name = "membership" && resource.telemetry.sdk.language = "go" && + span.http.request.method = "GET" && span:kind = server && + span.http.response.status_code = 200 && span.url.path = "/isMember" })) +expected: + count: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/wait-for-trace.yaml b/tests/e2e/source/tracesql/wait-for-trace.yaml new file mode 100644 index 000000000..a88f58987 --- /dev/null +++ b/tests/e2e/source/tracesql/wait-for-trace.yaml @@ -0,0 +1,11 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: This test waits for a trace that goes from frontend to pricing, inventory, coupon, and membership services +query: | + { resource.service.name = "frontend" } && + { resource.service.name = "pricing" } && + { resource.service.name = "inventory" } && + { resource.service.name = "coupon" } && + { resource.service.name = "membership" } +expected: + count: 1 \ No newline at end of file From 70533c21d23accf6f10f261bbedfb3b176e2f15f Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 27 Dec 2024 18:03:10 +0200 Subject: [PATCH 057/259] Create common interface for instrumenteationConfig and instrumentedApplication for migration --- .../odigos.io_instrumentationconfigs.yaml | 56 +++++++++++++++++++ .../v1alpha1/instrumentationconfigstatus.go | 18 ++++++ .../v1alpha1/instrumentationconfig_types.go | 37 ++++++++++++ .../v1alpha1/instrumentedapplication_types.go | 27 ++++----- api/odigos/v1alpha1/zz_generated.deepcopy.go | 7 +++ .../odigos.io_instrumentationconfigs.yaml | 56 +++++++++++++++++++ .../instrumentationdevice/common.go | 46 +++++++-------- .../instrumentation/instrumentation.go | 20 +++---- 8 files changed, 217 insertions(+), 50 deletions(-) diff --git a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml index 8fb450866..5a49486ff 100644 --- a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml +++ b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml @@ -423,6 +423,62 @@ spec: type: object status: properties: + conditions: + 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 runtimeDetailsByContainer: description: Capture Runtime Details for the workloads that this CR applies to. diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go index efab8f15b..f6796dea5 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go @@ -17,10 +17,15 @@ limitations under the License. package v1alpha1 +import ( + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + // InstrumentationConfigStatusApplyConfiguration represents a declarative configuration of the InstrumentationConfigStatus type for use // with apply. type InstrumentationConfigStatusApplyConfiguration struct { RuntimeDetailsByContainer []RuntimeDetailsByContainerApplyConfiguration `json:"runtimeDetailsByContainer,omitempty"` + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` } // InstrumentationConfigStatusApplyConfiguration constructs a declarative configuration of the InstrumentationConfigStatus type for use with @@ -41,3 +46,16 @@ func (b *InstrumentationConfigStatusApplyConfiguration) WithRuntimeDetailsByCont } return b } + +// 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 *InstrumentationConfigStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationConfigStatusApplyConfiguration { + 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/odigos/v1alpha1/instrumentationconfig_types.go b/api/odigos/v1alpha1/instrumentationconfig_types.go index 0c157f3a3..6d4b93882 100644 --- a/api/odigos/v1alpha1/instrumentationconfig_types.go +++ b/api/odigos/v1alpha1/instrumentationconfig_types.go @@ -5,8 +5,16 @@ import ( "github.com/odigos-io/odigos/common" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) +// +kubebuilder:object:generate=false +type WorkloadDetails interface { + client.Object + RuntimeDetailsByContainer() []RuntimeDetailsByContainer + Conditions() *[]metav1.Condition +} + // +genclient // +kubebuilder:object:root=true // +kubebuilder:subresource:status @@ -20,9 +28,38 @@ type InstrumentationConfig struct { Status InstrumentationConfigStatus `json:"status,omitempty"` } +var _ WorkloadDetails = &InstrumentationConfig{} + +func (ic *InstrumentationConfig) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { + return ic.Status.RuntimeDetailsByContainer +} + +func (ic *InstrumentationConfig) Conditions() *[]metav1.Condition { + return &ic.Status.Conditions +} + +// +kubebuilder:object:generate=true +type RuntimeDetailsByContainer struct { + ContainerName string `json:"containerName"` + Language common.ProgrammingLanguage `json:"language"` + RuntimeVersion string `json:"runtimeVersion,omitempty"` + EnvVars []EnvVar `json:"envVars,omitempty"` + OtherAgent *OtherAgent `json:"otherAgent,omitempty"` + LibCType *common.LibCType `json:"libCType,omitempty"` + + // Stores the error message from the CRI runtime if returned to prevent instrumenting the container if an error exists. + CriErrorMessage *string `json:"criErrorMessage,omitempty"` + // Holds the environment variables retrieved from the container runtime. + EnvFromContainerRuntime []EnvVar `json:"envFromContainerRuntime,omitempty"` + // A temporary variable used during migration to track whether the new runtime detection process has been executed. If empty, it indicates the process has not yet been run. This field may be removed later. + RuntimeUpdateState *ProcessingState `json:"runtimeUpdateState,omitempty"` +} + type InstrumentationConfigStatus struct { // Capture Runtime Details for the workloads that this CR applies to. RuntimeDetailsByContainer []RuntimeDetailsByContainer `json:"runtimeDetailsByContainer,omitempty"` + + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" protobuf:"bytes,1,rep,name=conditions"` } // Config for the OpenTelemeetry SDKs that should be applied to a workload. diff --git a/api/odigos/v1alpha1/instrumentedapplication_types.go b/api/odigos/v1alpha1/instrumentedapplication_types.go index cbedca502..85be3223c 100644 --- a/api/odigos/v1alpha1/instrumentedapplication_types.go +++ b/api/odigos/v1alpha1/instrumentedapplication_types.go @@ -51,23 +51,6 @@ const ( ProcessingStateSkipped ProcessingState = "Skipped" ) -// +kubebuilder:object:generate=true -type RuntimeDetailsByContainer struct { - ContainerName string `json:"containerName"` - Language common.ProgrammingLanguage `json:"language"` - RuntimeVersion string `json:"runtimeVersion,omitempty"` - EnvVars []EnvVar `json:"envVars,omitempty"` - OtherAgent *OtherAgent `json:"otherAgent,omitempty"` - LibCType *common.LibCType `json:"libCType,omitempty"` - - // Stores the error message from the CRI runtime if returned to prevent instrumenting the container if an error exists. - CriErrorMessage *string `json:"criErrorMessage,omitempty"` - // Holds the environment variables retrieved from the container runtime. - EnvFromContainerRuntime []EnvVar `json:"envFromContainerRuntime,omitempty"` - // A temporary variable used during migration to track whether the new runtime detection process has been executed. If empty, it indicates the process has not yet been run. This field may be removed later. - RuntimeUpdateState *ProcessingState `json:"runtimeUpdateState,omitempty"` -} - // +kubebuilder:object:generate=true type OptionByContainer struct { ContainerName string `json:"containerName"` @@ -110,6 +93,16 @@ type InstrumentedApplicationList struct { Items []InstrumentedApplication `json:"items"` } +var _ WorkloadDetails = &InstrumentedApplication{} + +func (ia *InstrumentedApplication) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { + return ia.Spec.RuntimeDetails +} + +func (ia *InstrumentedApplication) Conditions() *[]metav1.Condition { + return &ia.Status.Conditions +} + func init() { SchemeBuilder.Register(&InstrumentedApplication{}, &InstrumentedApplicationList{}) } diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 5c7edb7ad..4d703eb33 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -484,6 +484,13 @@ func (in *InstrumentationConfigStatus) DeepCopyInto(out *InstrumentationConfigSt (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationConfigStatus. diff --git a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml index 8fb450866..5a49486ff 100644 --- a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml +++ b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml @@ -423,6 +423,62 @@ spec: type: object status: properties: + conditions: + 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 runtimeDetailsByContainer: description: Capture Runtime Details for the workloads that this CR applies to. diff --git a/instrumentor/controllers/instrumentationdevice/common.go b/instrumentor/controllers/instrumentationdevice/common.go index a0de5727a..e93919dae 100644 --- a/instrumentor/controllers/instrumentationdevice/common.go +++ b/instrumentor/controllers/instrumentationdevice/common.go @@ -66,13 +66,13 @@ func isDataCollectionReady(ctx context.Context, c client.Client) bool { return nodeCollectorsGroup.Status.Ready } -func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, runtimeDetails *odigosv1.InstrumentedApplication) (error, bool) { +func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetails) (error, bool) { // devicePartiallyApplied is used to indicate that the instrumentation device was partially applied for some of the containers. devicePartiallyApplied := false deviceNotAppliedDueToPresenceOfAnotherAgent := false logger := log.FromContext(ctx) - obj, err := getWorkloadObject(ctx, kubeClient, runtimeDetails) + obj, err := getWorkloadObject(ctx, kubeClient, workloadDetails) if err != nil { return err, false } @@ -133,7 +133,7 @@ func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.C agentsCanRunConcurrently = *odigosConfiguration.AllowConcurrentAgents } - err, deviceApplied, deviceSkippedDueToOtherAgent := instrumentation.ApplyInstrumentationDevicesToPodTemplate(podSpec, runtimeDetails, otelSdkToUse, obj, logger, agentsCanRunConcurrently) + err, deviceApplied, deviceSkippedDueToOtherAgent := instrumentation.ApplyInstrumentationDevicesToPodTemplate(podSpec, workloadDetails.RuntimeDetailsByContainer(), otelSdkToUse, obj, logger, agentsCanRunConcurrently) if err != nil { return err } @@ -213,8 +213,8 @@ func removeInstrumentationDeviceFromWorkload(ctx context.Context, kubeClient cli return nil } -func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDetails *odigosv1.InstrumentedApplication) (client.Object, error) { - name, kind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(runtimeDetails.Name) +func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDetailsObject client.Object) (client.Object, error) { + name, kind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(runtimeDetailsObject.GetName()) if err != nil { return nil, err } @@ -225,7 +225,7 @@ func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDet } err = kubeClient.Get(ctx, client.ObjectKey{ - Namespace: runtimeDetails.Namespace, + Namespace: runtimeDetailsObject.GetNamespace(), Name: name, }, workloadObject) if err != nil { @@ -251,45 +251,45 @@ func getPodSpecFromObject(obj client.Object) (*corev1.PodTemplateSpec, error) { // reconciles a single workload, which might be triggered by a change in multiple resources. // each time a relevant resource changes, this function is called to reconcile the workload // and always writes the status into the InstrumentedApplication CR -func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, instrumentedApplication *odigosv1.InstrumentedApplication, isNodeCollectorReady bool) error { +func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetails, isNodeCollectorReady bool) error { - workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedApplication.Name) + workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(workloadDetails.GetName()) if err != nil { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) return err } if !isNodeCollectorReady { - err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonDataCollectionNotReady) + err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonDataCollectionNotReady) if err == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonDataCollectionNotReady), "OpenTelemetry pipeline not yet ready to receive data") + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonDataCollectionNotReady), "OpenTelemetry pipeline not yet ready to receive data") } else { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) } return err } - if len(instrumentedApplication.Spec.RuntimeDetails) == 0 { - err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonNoRuntimeDetails) + if len(workloadDetails.RuntimeDetailsByContainer()) == 0 { + err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonNoRuntimeDetails) if err == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonNoRuntimeDetails), "No runtime details found") + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonNoRuntimeDetails), "No runtime details found") } else { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) } return err } - runtimeVersionSupport, err := versionsupport.IsRuntimeVersionSupported(ctx, instrumentedApplication.Spec.RuntimeDetails) + runtimeVersionSupport, err := versionsupport.IsRuntimeVersionSupported(ctx, workloadDetails.RuntimeDetailsByContainer()) if !runtimeVersionSupport { - errRemove := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported) + errRemove := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported) if errRemove == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported), err.Error()) } else { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), errRemove.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), errRemove.Error()) } return nil } - err, devicePartiallyApplied := addInstrumentationDeviceToWorkload(ctx, kubeClient, instrumentedApplication) + err, devicePartiallyApplied := addInstrumentationDeviceToWorkload(ctx, kubeClient, workloadDetails) if err == nil { var successMessage string if devicePartiallyApplied { @@ -297,9 +297,9 @@ func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, inst } else { successMessage = "Instrumentation device applied successfully" } - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionTrue, appliedInstrumentationDeviceType, "InstrumentationDeviceApplied", successMessage) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionTrue, appliedInstrumentationDeviceType, "InstrumentationDeviceApplied", successMessage) } else { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrApplying), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrApplying), err.Error()) } return err } diff --git a/instrumentor/instrumentation/instrumentation.go b/instrumentor/instrumentation/instrumentation.go index f5afd09b4..5451356e3 100644 --- a/instrumentor/instrumentation/instrumentation.go +++ b/instrumentor/instrumentation/instrumentation.go @@ -22,7 +22,7 @@ var ( ErrPatchEnvVars = errors.New("failed to patch env vars") ) -func ApplyInstrumentationDevicesToPodTemplate(original *corev1.PodTemplateSpec, runtimeDetails *odigosv1.InstrumentedApplication, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object, +func ApplyInstrumentationDevicesToPodTemplate(original *corev1.PodTemplateSpec, runtimeDetails []odigosv1.RuntimeDetailsByContainer, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object, logger logr.Logger, agentsCanRunConcurrently bool) (error, bool, bool) { // delete any existing instrumentation devices. // this is necessary for example when migrating from community to enterprise, @@ -154,8 +154,8 @@ func RevertInstrumentationDevices(original *corev1.PodTemplateSpec) bool { return changed } -func getLanguageOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) common.ProgrammingLanguage { - for _, l := range instrumentation.Spec.RuntimeDetails { +func getLanguageOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) common.ProgrammingLanguage { + for _, l := range runtimeDetails { if l.ContainerName == containerName { return l.Language } @@ -164,8 +164,8 @@ func getLanguageOfContainer(instrumentation *odigosv1.InstrumentedApplication, c return common.UnknownProgrammingLanguage } -func getContainerOtherAgents(instrumentation *odigosv1.InstrumentedApplication, containerName string) *odigosv1.OtherAgent { - for _, l := range instrumentation.Spec.RuntimeDetails { +func getContainerOtherAgents(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) *odigosv1.OtherAgent { + for _, l := range runtimeDetails { if l.ContainerName == containerName { if l.OtherAgent != nil && *l.OtherAgent != (odigosv1.OtherAgent{}) { return l.OtherAgent @@ -175,8 +175,8 @@ func getContainerOtherAgents(instrumentation *odigosv1.InstrumentedApplication, return nil } -func getLibCTypeOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) *common.LibCType { - for _, l := range instrumentation.Spec.RuntimeDetails { +func getLibCTypeOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) *common.LibCType { + for _, l := range runtimeDetails { if l.ContainerName == containerName { return l.LibCType } @@ -187,10 +187,10 @@ func getLibCTypeOfContainer(instrumentation *odigosv1.InstrumentedApplication, c // getEnvVarsOfContainer returns the env vars which are defined for the given container and are used for instrumentation purposes. // This function also returns env vars which are declared in the container build. -func getEnvVarsOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) map[string]string { +func getEnvVarsOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) map[string]string { envVars := make(map[string]string) - for _, l := range instrumentation.Spec.RuntimeDetails { + for _, l := range runtimeDetails { if l.ContainerName == containerName { for _, env := range l.EnvVars { envVars[env.Name] = env.Value @@ -204,7 +204,7 @@ func getEnvVarsOfContainer(instrumentation *odigosv1.InstrumentedApplication, co // when otelsdk is nil, it means that the container is not instrumented. // this will trigger reverting of any existing env vars which were set by odigos before. -func patchEnvVarsForContainer(runtimeDetails *odigosv1.InstrumentedApplication, container *corev1.Container, sdk *common.OtelSdk, programmingLanguage common.ProgrammingLanguage, manifestEnvOriginal *envoverwrite.OrigWorkloadEnvValues) error { +func patchEnvVarsForContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, container *corev1.Container, sdk *common.OtelSdk, programmingLanguage common.ProgrammingLanguage, manifestEnvOriginal *envoverwrite.OrigWorkloadEnvValues) error { observedEnvs := getEnvVarsOfContainer(runtimeDetails, container.Name) From 0bdfaa1de8a66401cb26ecf789914afdf06b96a5 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 27 Dec 2024 18:32:31 +0200 Subject: [PATCH 058/259] rename interface to WorkloadDetailsObject --- api/odigos/v1alpha1/instrumentationconfig_types.go | 4 ++-- api/odigos/v1alpha1/instrumentedapplication_types.go | 2 +- instrumentor/controllers/instrumentationdevice/common.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/odigos/v1alpha1/instrumentationconfig_types.go b/api/odigos/v1alpha1/instrumentationconfig_types.go index 6d4b93882..a8158d713 100644 --- a/api/odigos/v1alpha1/instrumentationconfig_types.go +++ b/api/odigos/v1alpha1/instrumentationconfig_types.go @@ -9,7 +9,7 @@ import ( ) // +kubebuilder:object:generate=false -type WorkloadDetails interface { +type WorkloadDetailsObject interface { client.Object RuntimeDetailsByContainer() []RuntimeDetailsByContainer Conditions() *[]metav1.Condition @@ -28,7 +28,7 @@ type InstrumentationConfig struct { Status InstrumentationConfigStatus `json:"status,omitempty"` } -var _ WorkloadDetails = &InstrumentationConfig{} +var _ WorkloadDetailsObject = &InstrumentationConfig{} func (ic *InstrumentationConfig) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { return ic.Status.RuntimeDetailsByContainer diff --git a/api/odigos/v1alpha1/instrumentedapplication_types.go b/api/odigos/v1alpha1/instrumentedapplication_types.go index 85be3223c..b66a6c6a2 100644 --- a/api/odigos/v1alpha1/instrumentedapplication_types.go +++ b/api/odigos/v1alpha1/instrumentedapplication_types.go @@ -93,7 +93,7 @@ type InstrumentedApplicationList struct { Items []InstrumentedApplication `json:"items"` } -var _ WorkloadDetails = &InstrumentedApplication{} +var _ WorkloadDetailsObject = &InstrumentedApplication{} func (ia *InstrumentedApplication) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { return ia.Spec.RuntimeDetails diff --git a/instrumentor/controllers/instrumentationdevice/common.go b/instrumentor/controllers/instrumentationdevice/common.go index e93919dae..7adaecab1 100644 --- a/instrumentor/controllers/instrumentationdevice/common.go +++ b/instrumentor/controllers/instrumentationdevice/common.go @@ -66,7 +66,7 @@ func isDataCollectionReady(ctx context.Context, c client.Client) bool { return nodeCollectorsGroup.Status.Ready } -func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetails) (error, bool) { +func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetailsObject) (error, bool) { // devicePartiallyApplied is used to indicate that the instrumentation device was partially applied for some of the containers. devicePartiallyApplied := false deviceNotAppliedDueToPresenceOfAnotherAgent := false @@ -251,7 +251,7 @@ func getPodSpecFromObject(obj client.Object) (*corev1.PodTemplateSpec, error) { // reconciles a single workload, which might be triggered by a change in multiple resources. // each time a relevant resource changes, this function is called to reconcile the workload // and always writes the status into the InstrumentedApplication CR -func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetails, isNodeCollectorReady bool) error { +func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetailsObject, isNodeCollectorReady bool) error { workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(workloadDetails.GetName()) if err != nil { From 9ee971dea72a0e6322cf9d6effe02e53d409cb99 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 27 Dec 2024 13:03:30 -0500 Subject: [PATCH 059/259] Update comment --- k8sutils/pkg/predicate/creation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8sutils/pkg/predicate/creation.go b/k8sutils/pkg/predicate/creation.go index d34fc88fe..921a62795 100644 --- a/k8sutils/pkg/predicate/creation.go +++ b/k8sutils/pkg/predicate/creation.go @@ -5,7 +5,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" ) -// CreationPredicate only allows delete events. +// CreationPredicate only allows create events. type CreationPredicate struct{} func (i CreationPredicate) Create(e event.CreateEvent) bool { From c0c526ef60cb118fbcf513f54d51698c5fd24c31 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 27 Dec 2024 13:22:47 -0500 Subject: [PATCH 060/259] Return SourceList by value in GetSourceListForWorkload --- api/odigos/v1alpha1/source_types.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index 157071c55..ae4ab1799 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -70,18 +70,15 @@ type SourceList struct { // GetSourceListForWorkload returns a SourceList of all Sources that have matching // workload name, namespace, and kind labels for an object. In theory, this should only // ever return a list with 0 or 1 items, but due diligence should handle unexpected cases. -func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (*SourceList, error) { - sourceList := &SourceList{} +func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (SourceList, error) { + sourceList := SourceList{} selector := labels.SelectorFromSet(labels.Set{ consts.WorkloadNameLabel: obj.GetName(), consts.WorkloadNamespaceLabel: obj.GetNamespace(), consts.WorkloadKindLabel: obj.GetObjectKind().GroupVersionKind().Kind, }) - err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) - if err != nil { - return nil, err - } - return sourceList, nil + err := kubeClient.List(ctx, &sourceList, &client.ListOptions{LabelSelector: selector}) + return sourceList, err } func init() { From 503679661871bcb9bd4302803d34ef963743e783 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 11:49:23 +0200 Subject: [PATCH 061/259] Add check for existing Source CRD before creation --- frontend/services/sources.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index a690a3183..a965cf485 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -368,6 +368,11 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo return errors.New("unsupported workload kind " + string(workloadKind)) } + source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) + if source != nil && err == nil { + return errors.New("source already exists") + } + newSource := &v1alpha1.Source{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "source-", @@ -381,7 +386,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo }, } - _, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) + _, err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) return err } From f0ad0bcdefeb90db40073481a57360ce49a697ec Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 11:51:44 +0200 Subject: [PATCH 062/259] Enhance error message for existing Source CRD to include workload name --- frontend/services/sources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index a965cf485..30c6a2616 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -370,7 +370,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return errors.New("source already exists") + return errors.New("source already exists" + workloadName) } newSource := &v1alpha1.Source{ From 1cfc83d265b5563edf3e5d2ba6634194ce3327ee Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 27 Dec 2024 10:59:48 -0500 Subject: [PATCH 063/259] Add Source chainsaw test --- tests/e2e/source/01-sources.yaml | 54 +++++++ tests/e2e/source/chainsaw-test.yaml | 150 ++++++++++++++++++ .../source/tracesql/context-propagation.yaml | 13 ++ .../source/tracesql/resource-attributes.yaml | 14 ++ .../e2e/source/tracesql/span-attributes.yaml | 18 +++ tests/e2e/source/tracesql/wait-for-trace.yaml | 11 ++ 6 files changed, 260 insertions(+) create mode 100644 tests/e2e/source/01-sources.yaml create mode 100644 tests/e2e/source/chainsaw-test.yaml create mode 100644 tests/e2e/source/tracesql/context-propagation.yaml create mode 100644 tests/e2e/source/tracesql/resource-attributes.yaml create mode 100644 tests/e2e/source/tracesql/span-attributes.yaml create mode 100644 tests/e2e/source/tracesql/wait-for-trace.yaml diff --git a/tests/e2e/source/01-sources.yaml b/tests/e2e/source/01-sources.yaml new file mode 100644 index 000000000..3b82128ba --- /dev/null +++ b/tests/e2e/source/01-sources.yaml @@ -0,0 +1,54 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: frontend + namespace: default +spec: + workload: + name: frontend + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: coupon + namespace: default +spec: + workload: + name: coupon + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: inventory + namespace: default +spec: + workload: + name: inventory + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: pricing + namespace: default +spec: + workload: + name: pricing + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: membership + namespace: default +spec: + workload: + name: membership + namespace: default + kind: Deployment diff --git a/tests/e2e/source/chainsaw-test.yaml b/tests/e2e/source/chainsaw-test.yaml new file mode 100644 index 000000000..7e3e4ff30 --- /dev/null +++ b/tests/e2e/source/chainsaw-test.yaml @@ -0,0 +1,150 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: source +spec: + description: This e2e test runs a multi-apps scenario with Source instrumentation + skipDelete: true + steps: + - name: Prepare destination + try: + - script: + timeout: 60s + content: | + helm repo add grafana https://grafana.github.io/helm-charts + helm repo update + helm install e2e-tests grafana/tempo -n traces --create-namespace --set tempo.storage.trace.block.version=vParquet4 \ + --set tempo.ingester.trace_idle_period=5s \ + --set tempo.ingester.max_block_duration=20s \ + --version 1.10.1 + - assert: + file: ../../common/assert/tempo-running.yaml + - name: Wait for destination to be ready + try: + - script: + timeout: 60s + content: ../../common/wait_for_dest.sh + - name: Install Odigos + try: + - script: + content: | + if [ "$MODE" = "cross-cloud-tests" ]; then + ../../../cli/odigos install --namespace odigos-test --version "$COMMIT_HASH" --image-prefix=public.ecr.aws/y2v0v6s7 + else + ../../../cli/odigos install --namespace odigos-test --version e2e-test + fi + timeout: 60s + - assert: + file: ../../common/assert/odigos-installed.yaml + + - name: Install Demo App + try: + - script: + timeout: 200s + content: | + if [ "$MODE" != "cross-cloud-tests" ]; then + docker pull keyval/odigos-demo-inventory:v0.1 + docker pull keyval/odigos-demo-membership:v0.1 + docker pull keyval/odigos-demo-coupon:v0.1 + docker pull keyval/odigos-demo-inventory:v0.1 + docker pull keyval/odigos-demo-frontend:v0.2 + kind load docker-image keyval/odigos-demo-inventory:v0.1 + kind load docker-image keyval/odigos-demo-membership:v0.1 + kind load docker-image keyval/odigos-demo-coupon:v0.1 + kind load docker-image keyval/odigos-demo-inventory:v0.1 + kind load docker-image keyval/odigos-demo-frontend:v0.2 + else + echo "Skipping docker pull and kind load for cross-cloud-tests mode" + fi + - apply: + file: ../../common/apply/install-simple-demo.yaml + - script: + timeout: 70s + content: | + kubectl wait --for=condition=ready pod -l app=frontend --timeout=60s + kubectl wait --for=condition=ready pod -l app=coupon --timeout=60s + kubectl wait --for=condition=ready pod -l app=inventory --timeout=60s + kubectl wait --for=condition=ready pod -l app=pricing --timeout=60s + kubectl wait --for=condition=ready pod -l app=membership --timeout=60s + - assert: + file: ../../common/assert/simple-demo-installed.yaml + - name: Instrument Deployments + try: + - apply: + file: 01-sources.yaml + - name: Assert Runtime Detected + try: + - assert: + timeout: 2m + file: ../../common/assert/simple-demo-runtime-detected.yaml + - name: Add Destination + try: + - apply: + file: ../../common/apply/add-tempo-traces-destination.yaml + - name: Odigos pipeline ready + try: + - assert: + file: ../../common/assert/pipeline-ready.yaml + - name: Simple-demo instrumented after destination added + try: + - assert: + file: ../../common/assert/simple-demo-instrumented.yaml + - name: Generate Traffic + try: + - script: + timeout: 300s + content: | + # Apply the job + kubectl apply -f ../../common/apply/generate-traffic-job.yaml + + # Wait for the job to complete + job_name=$(kubectl get -f ../../common/apply/generate-traffic-job.yaml -o=jsonpath='{.metadata.name}') + kubectl wait --for=condition=complete job/$job_name + + # Delete the job + kubectl delete -f ../../common/apply/generate-traffic-job.yaml + + while true; do + # wait for traces to be available + sleep 8 + + # Run the wait-for-trace script + echo "Running TraceQL test at $(date)" + ../../common/traceql_runner.sh tracesql/wait-for-trace.yaml + + if [ $? -eq 0 ]; then + break + else + ../../common/flush_traces.sh + sleep 5 + fi + done + - name: Verify Trace - Context Propagation + try: + - script: + timeout: 30s + content: | + ../../common/traceql_runner.sh tracesql/context-propagation.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Verify Trace - Resource Attributes + try: + - script: + content: | + ../../common/traceql_runner.sh tracesql/resource-attributes.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Verify Trace - Span Attributes + try: + - script: + timeout: 60s + content: | + ../../common/traceql_runner.sh tracesql/span-attributes.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system diff --git a/tests/e2e/source/tracesql/context-propagation.yaml b/tests/e2e/source/tracesql/context-propagation.yaml new file mode 100644 index 000000000..9c463f9b3 --- /dev/null +++ b/tests/e2e/source/tracesql/context-propagation.yaml @@ -0,0 +1,13 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: This test checks if the context propagation is working correctly between different languages +query: | + { resource.service.name = "frontend" && resource.telemetry.sdk.language = "java" && + span.http.request.method = "POST" && span.http.route = "/buy" && span:kind = server } + >> ( + { resource.service.name = "pricing" && resource.telemetry.sdk.language = "dotnet" } && + { resource.service.name = "inventory" && resource.telemetry.sdk.language = "python" } && + ({ resource.service.name = "coupon" && resource.telemetry.sdk.language = "nodejs" } + >> { resource.service.name = "membership" && resource.telemetry.sdk.language = "go" })) +expected: + count: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/resource-attributes.yaml b/tests/e2e/source/tracesql/resource-attributes.yaml new file mode 100644 index 000000000..f6b56aef5 --- /dev/null +++ b/tests/e2e/source/tracesql/resource-attributes.yaml @@ -0,0 +1,14 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: | + This test check the following resource attributes: + A. odigos.version attribute exists on all spans and has the correct value + B. Kubernetes attributes are correctly set on all spans + At the time of writing this test, TraceQL api does not support not equal to nil so we use regex instead. +query: | + { resource.odigos.version !~ ".*" || + resource.k8s.deployment.name !~ ".*" || + resource.k8s.node.name !~ "(kind-control-plane|aks-.*)" || + resource.k8s.pod.name !~ ".*" } +expected: + count: 0 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/span-attributes.yaml b/tests/e2e/source/tracesql/span-attributes.yaml new file mode 100644 index 000000000..d508d4a39 --- /dev/null +++ b/tests/e2e/source/tracesql/span-attributes.yaml @@ -0,0 +1,18 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: | + This test checks the span attributes for a specific trace. + TODO - JS, Python and DotNet SDK are not generating data in latest semconv. add additional checks when they are updated. +query: | + { resource.service.name = "frontend" && resource.telemetry.sdk.language = "java" && + span.http.request.method = "POST" && span.http.route = "/buy" && span:kind = server && + span.http.response.status_code = 200 && span.url.query = "id=123" } + >> ( + { resource.service.name = "pricing" && resource.telemetry.sdk.language = "dotnet" && span:kind = server } && + { resource.service.name = "inventory" && resource.telemetry.sdk.language = "python" && span:kind = server } && + ({ resource.service.name = "coupon" && resource.telemetry.sdk.language = "nodejs" && span:kind = server } + >> { resource.service.name = "membership" && resource.telemetry.sdk.language = "go" && + span.http.request.method = "GET" && span:kind = server && + span.http.response.status_code = 200 && span.url.path = "/isMember" })) +expected: + count: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/wait-for-trace.yaml b/tests/e2e/source/tracesql/wait-for-trace.yaml new file mode 100644 index 000000000..a88f58987 --- /dev/null +++ b/tests/e2e/source/tracesql/wait-for-trace.yaml @@ -0,0 +1,11 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: This test waits for a trace that goes from frontend to pricing, inventory, coupon, and membership services +query: | + { resource.service.name = "frontend" } && + { resource.service.name = "pricing" } && + { resource.service.name = "inventory" } && + { resource.service.name = "coupon" } && + { resource.service.name = "membership" } +expected: + count: 1 \ No newline at end of file From 7bf007c17e6b5f6ed0add0126e43c06c5e12c70e Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 27 Dec 2024 13:03:30 -0500 Subject: [PATCH 064/259] Update comment --- k8sutils/pkg/predicate/creation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8sutils/pkg/predicate/creation.go b/k8sutils/pkg/predicate/creation.go index d34fc88fe..921a62795 100644 --- a/k8sutils/pkg/predicate/creation.go +++ b/k8sutils/pkg/predicate/creation.go @@ -5,7 +5,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" ) -// CreationPredicate only allows delete events. +// CreationPredicate only allows create events. type CreationPredicate struct{} func (i CreationPredicate) Create(e event.CreateEvent) bool { From 64cd9f1b0ee6100924cf2b146f74e621a2beb4a4 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 27 Dec 2024 13:22:47 -0500 Subject: [PATCH 065/259] Return SourceList by value in GetSourceListForWorkload --- api/odigos/v1alpha1/source_types.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index 157071c55..ae4ab1799 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -70,18 +70,15 @@ type SourceList struct { // GetSourceListForWorkload returns a SourceList of all Sources that have matching // workload name, namespace, and kind labels for an object. In theory, this should only // ever return a list with 0 or 1 items, but due diligence should handle unexpected cases. -func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (*SourceList, error) { - sourceList := &SourceList{} +func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (SourceList, error) { + sourceList := SourceList{} selector := labels.SelectorFromSet(labels.Set{ consts.WorkloadNameLabel: obj.GetName(), consts.WorkloadNamespaceLabel: obj.GetNamespace(), consts.WorkloadKindLabel: obj.GetObjectKind().GroupVersionKind().Kind, }) - err := kubeClient.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) - if err != nil { - return nil, err - } - return sourceList, nil + err := kubeClient.List(ctx, &sourceList, &client.ListOptions{LabelSelector: selector}) + return sourceList, err } func init() { From 14a0758a97d7919d82af78caa375626f67d9976a Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 16:09:25 +0200 Subject: [PATCH 066/259] Refactor K8sActualSources resolver and clean up unused code; improve error messages for source retrieval --- frontend/graph/schema.resolvers.go | 8 ----- frontend/services/namespaces.go | 11 ++---- frontend/services/sources.go | 54 ++++-------------------------- 3 files changed, 9 insertions(+), 64 deletions(-) diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index feb57ef33..72ecf8166 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -82,15 +82,9 @@ func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *mode // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { - // sourceList, err := services.GetAllSourceCRDs(ctx) - // if err != nil { - // return nil, err - // } - // Initialize an empty list of K8sActualSource var actualSources []*model.K8sActualSource - // for _, source := range sourceList { // TODO: remove "InstrumentedApplications" once we're ready to move over to "InstrumentationConfigs" combined with "Source CRDs" instrumentedApplications, err := kube.DefaultClient.OdigosClient.InstrumentedApplications("").List(ctx, metav1.ListOptions{}) if err != nil { @@ -103,7 +97,6 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod services.AddHealthyInstrumentationInstancesCondition(ctx, &app, actualSource) owner, _ := services.GetWorkload(ctx, actualSource.Namespace, string(actualSource.Kind), actualSource.Name) if owner == nil { - continue } ownerAnnotations := owner.GetAnnotations() @@ -114,7 +107,6 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod actualSource.ReportedName = &reportedName actualSources = append(actualSources, actualSource) } - // } return actualSources, nil } diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 602120596..a4fb57780 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -181,18 +181,11 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo g.Go(func() error { // Only label selected sources, ignore the rest if currWorkload.Selected != nil { - err := ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) - if err != nil { - return err - } - // TODO: remove this after a fix was made in the backend to correctly handle the InstrumentedApplication on-create Source CRD - err = SetWorkloadInstrumentationLabel(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) - if err != nil { - return err - } + return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } return nil }) } + return g.Wait() } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 30c6a2616..7c1ca5647 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -17,7 +17,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" labels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" "golang.org/x/sync/errgroup" ) @@ -320,28 +319,8 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } -func GetAllSourceCRDs(ctx context.Context) ([]*v1alpha1.Source, error) { - sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, err - } - - var sources []*v1alpha1.Source - - for _, crd := range sourceList.Items { - crdName := crd.Name - source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) - if err != nil { - return nil, err - } - sources = append(sources, source) - } - - return sources, nil -} - func getSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { - sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ + sourceList, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ "odigos.io/workload-namespace": nsName, "odigos.io/workload-name": workloadName, "odigos.io/workload-kind": string(workloadKind), @@ -351,14 +330,14 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, err } if len(sourceList.Items) == 0 { - return nil, errors.New("source not found" + workloadName) + return nil, errors.New("source not found " + "\"" + workloadName + "\"") } if len(sourceList.Items) > 1 { - return nil, errors.New("too many sources" + workloadName) + return nil, errors.New("too many sources " + "\"" + workloadName + "\"") } crdName := sourceList.Items[0].Name - source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + source, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) return source, err } @@ -370,7 +349,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return errors.New("source already exists" + workloadName) + return errors.New("source already exists " + "\"" + workloadName + "\"") } newSource := &v1alpha1.Source{ @@ -386,7 +365,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo }, } - _, err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) + _, err = kube.DefaultClient.OdigosClient.Sources(nsName).Create(ctx, newSource, metav1.CreateOptions{}) return err } @@ -400,7 +379,7 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } - err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) + err = kube.DefaultClient.OdigosClient.Sources(nsName).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err } @@ -415,22 +394,3 @@ func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, wo return deleteSourceCRD(ctx, nsName, workloadName, workloadKind) } } - -// TODO: remove this after a fix was made in the backend to correctly handle the InstrumentedApplication on-create Source CRD -func SetWorkloadInstrumentationLabel(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { - jsonMergePatchData := GetJsonMergePatchForInstrumentationLabel(enabled) - - switch workloadKind { - case WorkloadKindDeployment: - _, err := kube.DefaultClient.AppsV1().Deployments(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindStatefulSet: - _, err := kube.DefaultClient.AppsV1().StatefulSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindDaemonSet: - _, err := kube.DefaultClient.AppsV1().DaemonSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - default: - return errors.New("unsupported workload kind " + string(workloadKind)) - } -} From c9a5e5a950fac1463c96173188a9e03c335a5e34 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 17:03:41 +0200 Subject: [PATCH 067/259] Improve error messages in source retrieval and add workload kind validation --- frontend/services/sources.go | 16 +++++++++------- frontend/services/utils.go | 10 ++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 7c1ca5647..9c548b473 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -330,10 +330,10 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, err } if len(sourceList.Items) == 0 { - return nil, errors.New("source not found " + "\"" + workloadName + "\"") + return nil, errors.New("\"" + workloadName + "\"" + " source not found") } if len(sourceList.Items) > 1 { - return nil, errors.New("too many sources " + "\"" + workloadName + "\"") + return nil, errors.New("\"" + workloadName + "\"" + " expected to get 1 source got " + string(len(sourceList.Items))) } crdName := sourceList.Items[0].Name @@ -343,13 +343,14 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl } func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) + err := CheckWorkloadKind(workloadKind) + if err != nil { + return err } source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return errors.New("source already exists " + "\"" + workloadName + "\"") + return errors.New("\"" + workloadName + "\"" + " source already exists") } newSource := &v1alpha1.Source{ @@ -370,8 +371,9 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo } func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) + err := CheckWorkloadKind(workloadKind) + if err != nil { + return err } source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index 6d8f1603e..c092e532e 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -1,6 +1,7 @@ package services import ( + "errors" "fmt" "path" "time" @@ -50,3 +51,12 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } return latestStatusTime.Time.Format(time.RFC3339) } + +func CheckWorkloadKind(kind WorkloadKind) error { + switch kind { + case WorkloadKindDeployment, WorkloadKindStatefulSet, WorkloadKindDaemonSet: + return nil + default: + return errors.New("unsupported workload kind: " + string(kind)) + } +} From 6bdd6832ac752b92aa1c2119b8032e4f7e407eba Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 18:03:52 +0200 Subject: [PATCH 068/259] Add --nowait option to cli-install command in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 61f6679c7..96005d933 100644 --- a/Makefile +++ b/Makefile @@ -199,7 +199,7 @@ check-clean-work-tree: .PHONY: cli-install cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" - cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) + cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) --nowait .PHONY: cli-uninstall cli-uninstall: From b5d6d55ba82e6688277eff6060b8ae5c9fd45e74 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 18:55:07 +0200 Subject: [PATCH 069/259] Refactor K8sActualSource and related components to streamline data handling; update conditions and containers access patterns --- frontend/graph/conversions.go | 30 +- frontend/graph/generated.go | 1214 +++-------------- frontend/graph/model/models_gen.go | 36 +- frontend/graph/schema.graphqls | 28 +- frontend/graph/schema.resolvers.go | 71 +- frontend/services/sources.go | 74 +- .../common/dropdowns/error-dropdown/index.tsx | 4 +- .../dropdowns/language-dropdown/index.tsx | 2 +- .../build-drawer-item.ts | 8 +- .../sources/source-drawer-container/index.tsx | 9 +- .../graphql/queries/compute-platform.ts | 29 +- .../compute-platform/useComputePlatform.ts | 8 +- .../condition-details/index.tsx | 2 +- frontend/webapp/types/compute-platform.ts | 2 +- frontend/webapp/types/sources.ts | 13 +- .../utils/constants/programming-languages.ts | 27 +- .../strings/get-entity-label/index.ts | 2 +- .../strings/get-health-status/index.ts | 2 +- 18 files changed, 316 insertions(+), 1245 deletions(-) diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index 67746ccc1..d4f093047 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -42,10 +42,10 @@ func k8sLastTransitionTimeToGql(t v1.Time) *string { return &str } -func instrumentedApplicationToActualSource(instrumentedApp v1alpha1.InstrumentedApplication) *model.K8sActualSource { - // Map the container runtime details +func instrumentationConfigToActualSource(instruConfig v1alpha1.InstrumentationConfig) *model.K8sActualSource { + // Map the containers runtime details var containers []*model.SourceContainerRuntimeDetails - for _, container := range instrumentedApp.Spec.RuntimeDetails { + for _, container := range instruConfig.Status.RuntimeDetailsByContainer { var otherAgentName *string if container.OtherAgent != nil { otherAgentName = &container.OtherAgent.Name @@ -59,30 +59,28 @@ func instrumentedApplicationToActualSource(instrumentedApp v1alpha1.Instrumented }) } - // Map the conditions of the application + // Map the conditions var conditions []*model.Condition - for _, condition := range instrumentedApp.Status.Conditions { + for _, condition := range instruConfig.Status.Conditions { conditions = append(conditions, &model.Condition{ - Type: condition.Type, Status: k8sConditionStatusToGql(condition.Status), + Type: condition.Type, Reason: &condition.Reason, - LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), Message: &condition.Message, + LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), }) } // Return the converted K8sActualSource object return &model.K8sActualSource{ - Namespace: instrumentedApp.Namespace, - Kind: k8sKindToGql(instrumentedApp.OwnerReferences[0].Kind), - Name: instrumentedApp.OwnerReferences[0].Name, - ServiceName: &instrumentedApp.Name, + Namespace: instruConfig.Namespace, + Kind: k8sKindToGql(instruConfig.OwnerReferences[0].Kind), + Name: instruConfig.OwnerReferences[0].Name, NumberOfInstances: nil, - AutoInstrumented: instrumentedApp.Spec.Options != nil, - InstrumentedApplicationDetails: &model.InstrumentedApplicationDetails{ - Containers: containers, - Conditions: conditions, - }, + ServiceName: &instruConfig.Name, + ReportedName: &instruConfig.Spec.ServiceName, + Containers: containers, + Conditions: conditions, } } diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index a0642bc58..5da74e178 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -85,7 +85,6 @@ type ComplexityRoot struct { InstrumentationRules func(childComplexity int) int K8sActualNamespace func(childComplexity int, name string) int K8sActualNamespaces func(childComplexity int) int - K8sActualSource func(childComplexity int, name *string, namespace *string, kind *string) int K8sActualSources func(childComplexity int) int } @@ -240,22 +239,12 @@ type ComplexityRoot struct { Workload func(childComplexity int) int } - InstrumentationLibrary struct { - LibraryName func(childComplexity int) int - Options func(childComplexity int) int - } - InstrumentationLibraryGlobalId struct { Language func(childComplexity int) int Name func(childComplexity int) int SpanKind func(childComplexity int) int } - InstrumentationOption struct { - OptionKey func(childComplexity int) int - SpanKind func(childComplexity int) int - } - InstrumentationRule struct { Disabled func(childComplexity int) int InstrumentationLibraries func(childComplexity int) int @@ -272,12 +261,6 @@ type ComplexityRoot struct { Created func(childComplexity int) int } - InstrumentedApplicationDetails struct { - Conditions func(childComplexity int) int - Containers func(childComplexity int) int - InstrumentationOptions func(childComplexity int) int - } - K8sActualNamespace struct { InstrumentationLabelEnabled func(childComplexity int) int K8sActualSources func(childComplexity int, instrumentationLabeled *bool) int @@ -285,15 +268,14 @@ type ComplexityRoot struct { } K8sActualSource struct { - AutoInstrumented func(childComplexity int) int - AutoInstrumentedDecision func(childComplexity int) int - InstrumentedApplicationDetails func(childComplexity int) int - Kind func(childComplexity int) int - Name func(childComplexity int) int - Namespace func(childComplexity int) int - NumberOfInstances func(childComplexity int) int - ReportedName func(childComplexity int) int - ServiceName func(childComplexity int) int + Conditions func(childComplexity int) int + Containers func(childComplexity int) int + Kind func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + NumberOfInstances func(childComplexity int) int + ReportedName func(childComplexity int) int + ServiceName func(childComplexity int) int } LatencySamplerAction struct { @@ -489,9 +471,8 @@ type ComplexityRoot struct { } type ComputePlatformResolver interface { - K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) - K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) + K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) @@ -730,18 +711,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ComputePlatform.K8sActualNamespaces(childComplexity), true - case "ComputePlatform.k8sActualSource": - if e.complexity.ComputePlatform.K8sActualSource == nil { - break - } - - args, err := ec.field_ComputePlatform_k8sActualSource_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.ComputePlatform.K8sActualSource(childComplexity, args["name"].(*string), args["namespace"].(*string), args["kind"].(*string)), true - case "ComputePlatform.k8sActualSources": if e.complexity.ComputePlatform.K8sActualSources == nil { break @@ -1323,20 +1292,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationLabelsAnalyze.Workload(childComplexity), true - case "InstrumentationLibrary.libraryName": - if e.complexity.InstrumentationLibrary.LibraryName == nil { - break - } - - return e.complexity.InstrumentationLibrary.LibraryName(childComplexity), true - - case "InstrumentationLibrary.options": - if e.complexity.InstrumentationLibrary.Options == nil { - break - } - - return e.complexity.InstrumentationLibrary.Options(childComplexity), true - case "InstrumentationLibraryGlobalId.language": if e.complexity.InstrumentationLibraryGlobalId.Language == nil { break @@ -1358,20 +1313,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationLibraryGlobalId.SpanKind(childComplexity), true - case "InstrumentationOption.optionKey": - if e.complexity.InstrumentationOption.OptionKey == nil { - break - } - - return e.complexity.InstrumentationOption.OptionKey(childComplexity), true - - case "InstrumentationOption.spanKind": - if e.complexity.InstrumentationOption.SpanKind == nil { - break - } - - return e.complexity.InstrumentationOption.SpanKind(childComplexity), true - case "InstrumentationRule.disabled": if e.complexity.InstrumentationRule.Disabled == nil { break @@ -1442,27 +1383,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentedApplicationAnalyze.Created(childComplexity), true - case "InstrumentedApplicationDetails.conditions": - if e.complexity.InstrumentedApplicationDetails.Conditions == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.Conditions(childComplexity), true - - case "InstrumentedApplicationDetails.containers": - if e.complexity.InstrumentedApplicationDetails.Containers == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.Containers(childComplexity), true - - case "InstrumentedApplicationDetails.instrumentationOptions": - if e.complexity.InstrumentedApplicationDetails.InstrumentationOptions == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.InstrumentationOptions(childComplexity), true - case "K8sActualNamespace.instrumentationLabelEnabled": if e.complexity.K8sActualNamespace.InstrumentationLabelEnabled == nil { break @@ -1489,26 +1409,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualNamespace.Name(childComplexity), true - case "K8sActualSource.autoInstrumented": - if e.complexity.K8sActualSource.AutoInstrumented == nil { - break - } - - return e.complexity.K8sActualSource.AutoInstrumented(childComplexity), true - - case "K8sActualSource.autoInstrumentedDecision": - if e.complexity.K8sActualSource.AutoInstrumentedDecision == nil { + case "K8sActualSource.conditions": + if e.complexity.K8sActualSource.Conditions == nil { break } - return e.complexity.K8sActualSource.AutoInstrumentedDecision(childComplexity), true + return e.complexity.K8sActualSource.Conditions(childComplexity), true - case "K8sActualSource.instrumentedApplicationDetails": - if e.complexity.K8sActualSource.InstrumentedApplicationDetails == nil { + case "K8sActualSource.containers": + if e.complexity.K8sActualSource.Containers == nil { break } - return e.complexity.K8sActualSource.InstrumentedApplicationDetails(childComplexity), true + return e.complexity.K8sActualSource.Containers(childComplexity), true case "K8sActualSource.kind": if e.complexity.K8sActualSource.Kind == nil { @@ -2638,39 +2551,6 @@ func (ec *executionContext) field_ComputePlatform_k8sActualNamespace_args(ctx co return args, nil } -func (ec *executionContext) field_ComputePlatform_k8sActualSource_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 *string - if tmp, ok := rawArgs["name"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["name"] = arg0 - var arg1 *string - if tmp, ok := rawArgs["namespace"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) - arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["namespace"] = arg1 - var arg2 *string - if tmp, ok := rawArgs["kind"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("kind")) - arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["kind"] = arg2 - return args, nil -} - func (ec *executionContext) field_K8sActualNamespace_k8sActualSources_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -3992,66 +3872,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_computePlatformType(_ c return fc, nil } -func (ec *executionContext) _ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().K8sActualNamespace(rctx, obj, fc.Args["name"].(string)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*model.K8sActualNamespace) - fc.Result = res - return ec.marshalOK8sActualNamespace2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualNamespace(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ComputePlatform", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_K8sActualNamespace_name(ctx, field) - case "instrumentationLabelEnabled": - return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) - case "k8sActualSources": - return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type K8sActualNamespace", field.Name) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_ComputePlatform_k8sActualNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - func (ec *executionContext) _ComputePlatform_k8sActualNamespaces(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) if err != nil { @@ -4104,8 +3924,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespaces(_ c return fc, nil } -func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) +func (ec *executionContext) _ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) if err != nil { return graphql.Null } @@ -4118,7 +3938,7 @@ func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().K8sActualSource(rctx, obj, fc.Args["name"].(*string), fc.Args["namespace"].(*string), fc.Args["kind"].(*string)) + return ec.resolvers.ComputePlatform().K8sActualNamespace(rctx, obj, fc.Args["name"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -4127,12 +3947,12 @@ func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context if resTmp == nil { return graphql.Null } - res := resTmp.(*model.K8sActualSource) + res := resTmp.(*model.K8sActualNamespace) fc.Result = res - return ec.marshalOK8sActualSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualSource(ctx, field.Selections, res) + return ec.marshalOK8sActualNamespace2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualNamespace(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ComputePlatform", Field: field, @@ -4140,26 +3960,14 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx con IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "namespace": - return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": - return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) - case "numberOfInstances": - return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) - case "reportedName": - return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + return ec.fieldContext_K8sActualNamespace_name(ctx, field) + case "instrumentationLabelEnabled": + return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) + case "k8sActualSources": + return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) + return nil, fmt.Errorf("no field named %q was found under type K8sActualNamespace", field.Name) }, } defer func() { @@ -4169,7 +3977,7 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx con } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_ComputePlatform_k8sActualSource_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_ComputePlatform_k8sActualNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -4217,22 +4025,20 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont switch field.Name { case "namespace": return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) + case "kind": + return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "serviceName": + return ec.fieldContext_K8sActualSource_serviceName(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + case "containers": + return ec.fieldContext_K8sActualSource_containers(ctx, field) + case "conditions": + return ec.fieldContext_K8sActualSource_conditions(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) }, @@ -8226,8 +8032,8 @@ func (ec *executionContext) fieldContext_InstrumentationLabelsAnalyze_instrument return fc, nil } -func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibrary) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibrary_libraryName(ctx, field) +func (ec *executionContext) _InstrumentationLibraryGlobalId_name(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_name(ctx, field) if err != nil { return graphql.Null } @@ -8240,7 +8046,7 @@ func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Cont }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.LibraryName, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -8257,9 +8063,9 @@ func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Cont return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibrary_libraryName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibrary", + Object: "InstrumentationLibraryGlobalId", Field: field, IsMethod: false, IsResolver: false, @@ -8270,8 +8076,8 @@ func (ec *executionContext) fieldContext_InstrumentationLibrary_libraryName(_ co return fc, nil } -func (ec *executionContext) _InstrumentationLibrary_options(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibrary) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibrary_options(ctx, field) +func (ec *executionContext) _InstrumentationLibraryGlobalId_spanKind(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_spanKind(ctx, field) if err != nil { return graphql.Null } @@ -8284,44 +8090,35 @@ func (ec *executionContext) _InstrumentationLibrary_options(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Options, nil + return obj.SpanKind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.InstrumentationOption) + res := resTmp.(*model.SpanKind) fc.Result = res - return ec.marshalNInstrumentationOption2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOptionᚄ(ctx, field.Selections, res) + return ec.marshalOSpanKind2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibrary_options(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_spanKind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibrary", + Object: "InstrumentationLibraryGlobalId", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "optionKey": - return ec.fieldContext_InstrumentationOption_optionKey(ctx, field) - case "spanKind": - return ec.fieldContext_InstrumentationOption_spanKind(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationOption", field.Name) + return nil, errors.New("field of type SpanKind does not have child fields") }, } return fc, nil } -func (ec *executionContext) _InstrumentationLibraryGlobalId_name(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_name(ctx, field) +func (ec *executionContext) _InstrumentationLibraryGlobalId_language(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_language(ctx, field) if err != nil { return graphql.Null } @@ -8334,38 +8131,35 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId_name(ctx context.Con }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Language, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*model.ProgrammingLanguage) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOProgrammingLanguage2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐProgrammingLanguage(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_language(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "InstrumentationLibraryGlobalId", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ProgrammingLanguage does not have child fields") }, } return fc, nil } -func (ec *executionContext) _InstrumentationLibraryGlobalId_spanKind(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_spanKind(ctx, field) +func (ec *executionContext) _InstrumentationRule_ruleId(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationRule_ruleId(ctx, field) if err != nil { return graphql.Null } @@ -8378,35 +8172,38 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId_spanKind(ctx context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SpanKind, nil + return obj.RuleID, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*model.SpanKind) + res := resTmp.(string) fc.Result = res - return ec.marshalOSpanKind2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_spanKind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationRule_ruleId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibraryGlobalId", + Object: "InstrumentationRule", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type SpanKind does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _InstrumentationLibraryGlobalId_language(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_language(ctx, field) +func (ec *executionContext) _InstrumentationRule_ruleName(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationRule_ruleName(ctx, field) if err != nil { return graphql.Null } @@ -8419,7 +8216,7 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId_language(ctx context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Language, nil + return obj.RuleName, nil }) if err != nil { ec.Error(ctx, err) @@ -8428,26 +8225,26 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId_language(ctx context if resTmp == nil { return graphql.Null } - res := resTmp.(*model.ProgrammingLanguage) + res := resTmp.(*string) fc.Result = res - return ec.marshalOProgrammingLanguage2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐProgrammingLanguage(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_language(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationRule_ruleName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibraryGlobalId", + Object: "InstrumentationRule", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ProgrammingLanguage does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _InstrumentationOption_optionKey(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationOption) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationOption_optionKey(ctx, field) +func (ec *executionContext) _InstrumentationRule_notes(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationRule_notes(ctx, field) if err != nil { return graphql.Null } @@ -8460,26 +8257,23 @@ func (ec *executionContext) _InstrumentationOption_optionKey(ctx context.Context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.OptionKey, nil + return obj.Notes, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationOption_optionKey(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationRule_notes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationOption", + Object: "InstrumentationRule", Field: field, IsMethod: false, IsResolver: false, @@ -8490,8 +8284,8 @@ func (ec *executionContext) fieldContext_InstrumentationOption_optionKey(_ conte return fc, nil } -func (ec *executionContext) _InstrumentationOption_spanKind(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationOption) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationOption_spanKind(ctx, field) +func (ec *executionContext) _InstrumentationRule_disabled(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationRule_disabled(ctx, field) if err != nil { return graphql.Null } @@ -8504,177 +8298,7 @@ func (ec *executionContext) _InstrumentationOption_spanKind(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SpanKind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(model.SpanKind) - fc.Result = res - return ec.marshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationOption_spanKind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationOption", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type SpanKind does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationRule_ruleId(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationRule_ruleId(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.RuleID, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationRule_ruleId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationRule", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationRule_ruleName(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationRule_ruleName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.RuleName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*string) - fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationRule_ruleName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationRule", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationRule_notes(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationRule_notes(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Notes, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*string) - fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationRule_notes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationRule", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationRule_disabled(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationRule_disabled(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Disabled, nil + return obj.Disabled, nil }) if err != nil { ec.Error(ctx, err) @@ -9009,160 +8633,6 @@ func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_containe return fc, nil } -func (ec *executionContext) _InstrumentedApplicationDetails_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_containers(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Containers, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.SourceContainerRuntimeDetails) - fc.Result = res - return ec.marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "containerName": - return ec.fieldContext_SourceContainerRuntimeDetails_containerName(ctx, field) - case "language": - return ec.fieldContext_SourceContainerRuntimeDetails_language(ctx, field) - case "runtimeVersion": - return ec.fieldContext_SourceContainerRuntimeDetails_runtimeVersion(ctx, field) - case "otherAgent": - return ec.fieldContext_SourceContainerRuntimeDetails_otherAgent(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type SourceContainerRuntimeDetails", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationDetails_conditions(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_conditions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Conditions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.Condition) - fc.Result = res - return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "status": - return ec.fieldContext_Condition_status(ctx, field) - case "type": - return ec.fieldContext_Condition_type(ctx, field) - case "reason": - return ec.fieldContext_Condition_reason(ctx, field) - case "message": - return ec.fieldContext_Condition_message(ctx, field) - case "lastTransitionTime": - return ec.fieldContext_Condition_lastTransitionTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationDetails_instrumentationOptions(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_instrumentationOptions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.InstrumentationOptions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.InstrumentationLibrary) - fc.Result = res - return ec.marshalNInstrumentationLibrary2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_instrumentationOptions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "libraryName": - return ec.fieldContext_InstrumentationLibrary_libraryName(ctx, field) - case "options": - return ec.fieldContext_InstrumentationLibrary_options(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationLibrary", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualNamespace_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualNamespace_name(ctx, field) if err != nil { @@ -9289,22 +8759,20 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx switch field.Name { case "namespace": return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) + case "kind": + return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "serviceName": + return ec.fieldContext_K8sActualSource_serviceName(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + case "containers": + return ec.fieldContext_K8sActualSource_containers(ctx, field) + case "conditions": + return ec.fieldContext_K8sActualSource_conditions(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) }, @@ -9367,50 +8835,6 @@ func (ec *executionContext) fieldContext_K8sActualSource_namespace(_ context.Con return fc, nil } -func (ec *executionContext) _K8sActualSource_kind(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_kind(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Kind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(model.K8sResourceKind) - fc.Result = res - return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_K8sActualSource_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "K8sActualSource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type K8sResourceKind does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualSource_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualSource_name(ctx, field) if err != nil { @@ -9455,8 +8879,8 @@ func (ec *executionContext) fieldContext_K8sActualSource_name(_ context.Context, return fc, nil } -func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_serviceName(ctx, field) +func (ec *executionContext) _K8sActualSource_kind(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_kind(ctx, field) if err != nil { return graphql.Null } @@ -9469,28 +8893,31 @@ func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ServiceName, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(model.K8sResourceKind) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_serviceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type K8sResourceKind does not have child fields") }, } return fc, nil @@ -9537,8 +8964,8 @@ func (ec *executionContext) fieldContext_K8sActualSource_numberOfInstances(_ con return fc, nil } -func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) +func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_serviceName(ctx, field) if err != nil { return graphql.Null } @@ -9551,7 +8978,7 @@ func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ReportedName, nil + return obj.ServiceName, nil }) if err != nil { ec.Error(ctx, err) @@ -9565,7 +8992,7 @@ func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, f return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_reportedName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_serviceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, @@ -9578,8 +9005,8 @@ func (ec *executionContext) fieldContext_K8sActualSource_reportedName(_ context. return fc, nil } -func (ec *executionContext) _K8sActualSource_autoInstrumented(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) +func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) if err != nil { return graphql.Null } @@ -9592,38 +9019,35 @@ func (ec *executionContext) _K8sActualSource_autoInstrumented(ctx context.Contex }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.AutoInstrumented, nil + return obj.ReportedName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(bool) + res := resTmp.(*string) fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_autoInstrumented(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_reportedName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _K8sActualSource_autoInstrumentedDecision(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) +func (ec *executionContext) _K8sActualSource_containers(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_containers(ctx, field) if err != nil { return graphql.Null } @@ -9636,38 +9060,45 @@ func (ec *executionContext) _K8sActualSource_autoInstrumentedDecision(ctx contex }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.AutoInstrumentedDecision, nil + return obj.Containers, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.([]*model.SourceContainerRuntimeDetails) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_autoInstrumentedDecision(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "containerName": + return ec.fieldContext_SourceContainerRuntimeDetails_containerName(ctx, field) + case "language": + return ec.fieldContext_SourceContainerRuntimeDetails_language(ctx, field) + case "runtimeVersion": + return ec.fieldContext_SourceContainerRuntimeDetails_runtimeVersion(ctx, field) + case "otherAgent": + return ec.fieldContext_SourceContainerRuntimeDetails_otherAgent(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceContainerRuntimeDetails", field.Name) }, } return fc, nil } -func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) +func (ec *executionContext) _K8sActualSource_conditions(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_conditions(ctx, field) if err != nil { return graphql.Null } @@ -9680,7 +9111,7 @@ func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.InstrumentedApplicationDetails, nil + return obj.Conditions, nil }) if err != nil { ec.Error(ctx, err) @@ -9689,12 +9120,12 @@ func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx if resTmp == nil { return graphql.Null } - res := resTmp.(*model.InstrumentedApplicationDetails) + res := resTmp.([]*model.Condition) fc.Result = res - return ec.marshalOInstrumentedApplicationDetails2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationDetails(ctx, field.Selections, res) + return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_instrumentedApplicationDetails(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, @@ -9702,14 +9133,18 @@ func (ec *executionContext) fieldContext_K8sActualSource_instrumentedApplication IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "containers": - return ec.fieldContext_InstrumentedApplicationDetails_containers(ctx, field) - case "conditions": - return ec.fieldContext_InstrumentedApplicationDetails_conditions(ctx, field) - case "instrumentationOptions": - return ec.fieldContext_InstrumentedApplicationDetails_instrumentationOptions(ctx, field) + case "status": + return ec.fieldContext_Condition_status(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) + case "reason": + return ec.fieldContext_Condition_reason(ctx, field) + case "message": + return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type InstrumentedApplicationDetails", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) }, } return fc, nil @@ -13445,12 +12880,10 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context switch field.Name { case "computePlatformType": return ec.fieldContext_ComputePlatform_computePlatformType(ctx, field) - case "k8sActualNamespace": - return ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) case "k8sActualNamespaces": return ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) - case "k8sActualSource": - return ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) + case "k8sActualNamespace": + return ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) case "k8sActualSources": return ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) case "destinations": @@ -18554,53 +17987,20 @@ func (ec *executionContext) _ClusterInfo(ctx context.Context, sel ast.SelectionS var computePlatformImplementors = []string{"ComputePlatform"} -func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.SelectionSet, obj *model.ComputePlatform) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, computePlatformImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("ComputePlatform") - case "computePlatformType": - out.Values[i] = ec._ComputePlatform_computePlatformType(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "k8sActualNamespace": - field := field - - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._ComputePlatform_k8sActualNamespace(ctx, field, obj) - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } +func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.SelectionSet, obj *model.ComputePlatform) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, computePlatformImplementors) - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ComputePlatform") + case "computePlatformType": + out.Values[i] = ec._ComputePlatform_computePlatformType(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } case "k8sActualNamespaces": field := field @@ -18637,7 +18037,7 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "k8sActualSource": + case "k8sActualNamespace": field := field innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { @@ -18646,7 +18046,7 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ComputePlatform_k8sActualSource(ctx, field, obj) + res = ec._ComputePlatform_k8sActualNamespace(ctx, field, obj) return res } @@ -20031,50 +19431,6 @@ func (ec *executionContext) _InstrumentationLabelsAnalyze(ctx context.Context, s return out } -var instrumentationLibraryImplementors = []string{"InstrumentationLibrary"} - -func (ec *executionContext) _InstrumentationLibrary(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLibrary) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationLibraryImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationLibrary") - case "libraryName": - out.Values[i] = ec._InstrumentationLibrary_libraryName(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "options": - out.Values[i] = ec._InstrumentationLibrary_options(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var instrumentationLibraryGlobalIdImplementors = []string{"InstrumentationLibraryGlobalId"} func (ec *executionContext) _InstrumentationLibraryGlobalId(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLibraryGlobalID) graphql.Marshaler { @@ -20118,50 +19474,6 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId(ctx context.Context, return out } -var instrumentationOptionImplementors = []string{"InstrumentationOption"} - -func (ec *executionContext) _InstrumentationOption(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationOption) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationOptionImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationOption") - case "optionKey": - out.Values[i] = ec._InstrumentationOption_optionKey(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "spanKind": - out.Values[i] = ec._InstrumentationOption_spanKind(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var instrumentationRuleImplementors = []string{"InstrumentationRule"} func (ec *executionContext) _InstrumentationRule(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationRule) graphql.Marshaler { @@ -20259,49 +19571,6 @@ func (ec *executionContext) _InstrumentedApplicationAnalyze(ctx context.Context, return out } -var instrumentedApplicationDetailsImplementors = []string{"InstrumentedApplicationDetails"} - -func (ec *executionContext) _InstrumentedApplicationDetails(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentedApplicationDetails) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentedApplicationDetailsImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentedApplicationDetails") - case "containers": - out.Values[i] = ec._InstrumentedApplicationDetails_containers(ctx, field, obj) - case "conditions": - out.Values[i] = ec._InstrumentedApplicationDetails_conditions(ctx, field, obj) - case "instrumentationOptions": - out.Values[i] = ec._InstrumentedApplicationDetails_instrumentationOptions(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var k8sActualNamespaceImplementors = []string{"K8sActualNamespace"} func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.SelectionSet, obj *model.K8sActualNamespace) graphql.Marshaler { @@ -20395,34 +19664,26 @@ func (ec *executionContext) _K8sActualSource(ctx context.Context, sel ast.Select if out.Values[i] == graphql.Null { out.Invalids++ } - case "kind": - out.Values[i] = ec._K8sActualSource_kind(ctx, field, obj) + case "name": + out.Values[i] = ec._K8sActualSource_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "name": - out.Values[i] = ec._K8sActualSource_name(ctx, field, obj) + case "kind": + out.Values[i] = ec._K8sActualSource_kind(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "serviceName": - out.Values[i] = ec._K8sActualSource_serviceName(ctx, field, obj) case "numberOfInstances": out.Values[i] = ec._K8sActualSource_numberOfInstances(ctx, field, obj) + case "serviceName": + out.Values[i] = ec._K8sActualSource_serviceName(ctx, field, obj) case "reportedName": out.Values[i] = ec._K8sActualSource_reportedName(ctx, field, obj) - case "autoInstrumented": - out.Values[i] = ec._K8sActualSource_autoInstrumented(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "autoInstrumentedDecision": - out.Values[i] = ec._K8sActualSource_autoInstrumentedDecision(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "instrumentedApplicationDetails": - out.Values[i] = ec._K8sActualSource_instrumentedApplicationDetails(ctx, field, obj) + case "containers": + out.Values[i] = ec._K8sActualSource_containers(ctx, field, obj) + case "conditions": + out.Values[i] = ec._K8sActualSource_conditions(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -23001,60 +22262,6 @@ func (ec *executionContext) marshalNInstrumentationLabelsAnalyze2ᚖgithubᚗcom return ec._InstrumentationLabelsAnalyze(ctx, sel, v) } -func (ec *executionContext) marshalNInstrumentationLibrary2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.InstrumentationLibrary) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNInstrumentationLibrary2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibrary(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNInstrumentationLibrary2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibrary(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationLibrary) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentationLibrary(ctx, sel, v) -} - func (ec *executionContext) marshalNInstrumentationLibraryGlobalId2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryGlobalID(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationLibraryGlobalID) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23070,60 +22277,6 @@ func (ec *executionContext) unmarshalNInstrumentationLibraryGlobalIdInput2ᚖgit return &res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNInstrumentationOption2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOptionᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.InstrumentationOption) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNInstrumentationOption2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOption(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNInstrumentationOption2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOption(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationOption) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentationOption(ctx, sel, v) -} - func (ec *executionContext) marshalNInstrumentationRule2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationRule(ctx context.Context, sel ast.SelectionSet, v model.InstrumentationRule) graphql.Marshaler { return ec._InstrumentationRule(ctx, sel, &v) } @@ -23757,16 +22910,6 @@ func (ec *executionContext) marshalNSourceContainerRuntimeDetails2ᚖgithubᚗco return ec._SourceContainerRuntimeDetails(ctx, sel, v) } -func (ec *executionContext) unmarshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, v interface{}) (model.SpanKind, error) { - var res model.SpanKind - err := res.UnmarshalGQL(v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, sel ast.SelectionSet, v model.SpanKind) graphql.Marshaler { - return v -} - func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -24316,13 +23459,6 @@ func (ec *executionContext) unmarshalOInstrumentationLibraryGlobalIdInput2ᚕᚖ return res, nil } -func (ec *executionContext) marshalOInstrumentedApplicationDetails2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationDetails(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentedApplicationDetails) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._InstrumentedApplicationDetails(ctx, sel, v) -} - func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v interface{}) (*int, error) { if v == nil { return nil, nil diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 880c47690..46078a80b 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -74,9 +74,8 @@ type ClusterInfo struct { type ComputePlatform struct { ComputePlatformType ComputePlatformType `json:"computePlatformType"` - K8sActualNamespace *K8sActualNamespace `json:"k8sActualNamespace,omitempty"` K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` - K8sActualSource *K8sActualSource `json:"k8sActualSource,omitempty"` + K8sActualNamespace *K8sActualNamespace `json:"k8sActualNamespace,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` Destinations []*Destination `json:"destinations"` Actions []*PipelineAction `json:"actions"` @@ -264,11 +263,6 @@ type InstrumentationLabelsAnalyze struct { InstrumentedText *EntityProperty `json:"instrumentedText,omitempty"` } -type InstrumentationLibrary struct { - LibraryName string `json:"libraryName"` - Options []*InstrumentationOption `json:"options"` -} - type InstrumentationLibraryGlobalID struct { Name string `json:"name"` SpanKind *SpanKind `json:"spanKind,omitempty"` @@ -281,11 +275,6 @@ type InstrumentationLibraryGlobalIDInput struct { Language *ProgrammingLanguage `json:"language,omitempty"` } -type InstrumentationOption struct { - OptionKey string `json:"optionKey"` - SpanKind SpanKind `json:"spanKind"` -} - type InstrumentationRule struct { RuleID string `json:"ruleId"` RuleName *string `json:"ruleName,omitempty"` @@ -311,12 +300,6 @@ type InstrumentedApplicationAnalyze struct { Containers []*ContainerRuntimeInfoAnalyze `json:"containers"` } -type InstrumentedApplicationDetails struct { - Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` - Conditions []*Condition `json:"conditions,omitempty"` - InstrumentationOptions []*InstrumentationLibrary `json:"instrumentationOptions"` -} - type K8sActualNamespace struct { Name string `json:"name"` InstrumentationLabelEnabled *bool `json:"instrumentationLabelEnabled,omitempty"` @@ -324,15 +307,14 @@ type K8sActualNamespace struct { } type K8sActualSource struct { - Namespace string `json:"namespace"` - Kind K8sResourceKind `json:"kind"` - Name string `json:"name"` - ServiceName *string `json:"serviceName,omitempty"` - NumberOfInstances *int `json:"numberOfInstances,omitempty"` - ReportedName *string `json:"reportedName,omitempty"` - AutoInstrumented bool `json:"autoInstrumented"` - AutoInstrumentedDecision string `json:"autoInstrumentedDecision"` - InstrumentedApplicationDetails *InstrumentedApplicationDetails `json:"instrumentedApplicationDetails,omitempty"` + Namespace string `json:"namespace"` + Name string `json:"name"` + Kind K8sResourceKind `json:"kind"` + NumberOfInstances *int `json:"numberOfInstances,omitempty"` + ServiceName *string `json:"serviceName,omitempty"` + ReportedName *string `json:"reportedName,omitempty"` + Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` + Conditions []*Condition `json:"conditions,omitempty"` } type K8sDesiredNamespaceInput struct { diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 808c9ca2c..49f4ba8d8 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -51,22 +51,6 @@ type SourceContainerRuntimeDetails { otherAgent: String } -type InstrumentationOption { - optionKey: String! - spanKind: SpanKind! -} - -type InstrumentationLibrary { - libraryName: String! - options: [InstrumentationOption!]! -} - -type InstrumentedApplicationDetails { - containers: [SourceContainerRuntimeDetails!] - conditions: [Condition!] - instrumentationOptions: [InstrumentationLibrary!]! -} - type Condition { status: ConditionStatus! type: String! @@ -91,14 +75,13 @@ input K8sNamespaceId { type K8sActualSource { namespace: String! - kind: K8sResourceKind! name: String! - serviceName: String + kind: K8sResourceKind! numberOfInstances: Int + serviceName: String reportedName: String - autoInstrumented: Boolean! - autoInstrumentedDecision: String! - instrumentedApplicationDetails: InstrumentedApplicationDetails + containers: [SourceContainerRuntimeDetails!] + conditions: [Condition!] } input K8sDesiredSourceInput { @@ -203,9 +186,8 @@ input MessagingPayloadCollectionInput { type ComputePlatform { computePlatformType: ComputePlatformType! - k8sActualNamespace(name: String!): K8sActualNamespace k8sActualNamespaces: [K8sActualNamespace]! - k8sActualSource(name: String, namespace: String, kind: String): K8sActualSource + k8sActualNamespace(name: String!): K8sActualNamespace k8sActualSources: [K8sActualSource]! destinations: [Destination!]! actions: [PipelineAction!]! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 72ecf8166..be2134cee 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -20,11 +20,33 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) +// K8sActualNamespaces is the resolver for the k8sActualNamespaces field. +func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) { + namespacesResponse := services.GetK8SNamespaces(ctx) + + K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) + for i, namespace := range namespacesResponse.Namespaces { + + namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) + + K8sActualNamespaces[i] = &model.K8sActualNamespace{ + Name: namespace.Name, + InstrumentationLabelEnabled: nsInstrumented, + } + } + + return K8sActualNamespaces, nil +} + // K8sActualNamespace is the resolver for the k8sActualNamespace field. func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) { namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, name, nil) @@ -52,59 +74,20 @@ func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *m }, nil } -// K8sActualNamespaces is the resolver for the k8sActualNamespaces field. -func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) { - namespacesResponse := services.GetK8SNamespaces(ctx) - - K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) - for i, namespace := range namespacesResponse.Namespaces { - - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) - - K8sActualNamespaces[i] = &model.K8sActualNamespace{ - Name: namespace.Name, - InstrumentationLabelEnabled: nsInstrumented, - } - } - - return K8sActualNamespaces, nil -} - -// K8sActualSource is the resolver for the k8sActualSource field. -func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) { - return nil, nil -} - // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { // Initialize an empty list of K8sActualSource var actualSources []*model.K8sActualSource - // TODO: remove "InstrumentedApplications" once we're ready to move over to "InstrumentationConfigs" combined with "Source CRDs" - instrumentedApplications, err := kube.DefaultClient.OdigosClient.InstrumentedApplications("").List(ctx, metav1.ListOptions{}) + instrumentationConfigs, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs("").List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } // Convert each instrumented application to the K8sActualSource type - for _, app := range instrumentedApplications.Items { - actualSource := instrumentedApplicationToActualSource(app) - services.AddHealthyInstrumentationInstancesCondition(ctx, &app, actualSource) - owner, _ := services.GetWorkload(ctx, actualSource.Namespace, string(actualSource.Kind), actualSource.Name) - if owner == nil { - continue - } - ownerAnnotations := owner.GetAnnotations() - var reportedName string - if ownerAnnotations != nil { - reportedName = ownerAnnotations[consts.OdigosReportedNameAnnotation] - } - actualSource.ReportedName = &reportedName + for _, instruConfig := range instrumentationConfigs.Items { + actualSource := instrumentationConfigToActualSource(instruConfig) + // services.AddHealthyInstrumentationInstancesCondition(ctx, &instruConfig, actualSource) actualSources = append(actualSources, actualSource) } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 9c548b473..51e7b483d 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -86,9 +86,9 @@ func GetWorkload(c context.Context, ns string, kind string, name string) (metav1 } } -func AddHealthyInstrumentationInstancesCondition(ctx context.Context, app *v1alpha1.InstrumentedApplication, source *model.K8sActualSource) error { - labelSelector := fmt.Sprintf("%s=%s", consts.InstrumentedAppNameLabel, app.Name) - instancesList, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(app.Namespace).List(ctx, metav1.ListOptions{ +func AddHealthyInstrumentationInstancesCondition(ctx context.Context, instruConfig *v1alpha1.InstrumentationConfig, source *model.K8sActualSource) error { + labelSelector := fmt.Sprintf("%s=%s", consts.InstrumentedAppNameLabel, instruConfig.Name) + instancesList, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(instruConfig.Namespace).List(ctx, metav1.ListOptions{ LabelSelector: labelSelector, }) @@ -120,7 +120,7 @@ func AddHealthyInstrumentationInstancesCondition(ctx context.Context, app *v1alp message := fmt.Sprintf("%d/%d instances are healthy", healthyInstances, totalInstances) lastTransitionTime := Metav1TimeToString(latestStatusTime) - source.InstrumentedApplicationDetails.Conditions = append(source.InstrumentedApplicationDetails.Conditions, &model.Condition{ + source.Conditions = append(source.Conditions, &model.Condition{ Type: "HealthyInstrumentationInstances", Status: status, LastTransitionTime: &lastTransitionTime, @@ -178,19 +178,19 @@ func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumenta var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().Deployments(namespace.Name).List, ctx, metav1.ListOptions{}, func(deps *appsv1.DeploymentList) error { for _, dep := range deps.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(dep.GetLabels(), string(WorkloadKindDeployment), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } + // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(dep.GetLabels(), string(WorkloadKindDeployment), namespace.GetLabels()) + // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { + // continue + // } numberOfInstances := int(dep.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ - Namespace: dep.Namespace, - Name: dep.Name, - Kind: k8sKindToGql(string(WorkloadKindDeployment)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: dep.Namespace, + Name: dep.Name, + Kind: k8sKindToGql(string(WorkloadKindDeployment)), + NumberOfInstances: &numberOfInstances, + // AutoInstrumented: autoInstrumented, + // AutoInstrumentedDecision: decisionText, + // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -207,19 +207,19 @@ func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentat var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().DaemonSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(dss *appsv1.DaemonSetList) error { for _, ds := range dss.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ds.GetLabels(), string(WorkloadKindDaemonSet), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } + // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ds.GetLabels(), string(WorkloadKindDaemonSet), namespace.GetLabels()) + // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { + // continue + // } numberOfInstances := int(ds.Status.NumberReady) response = append(response, model.K8sActualSource{ - Namespace: ds.Namespace, - Name: ds.Name, - Kind: k8sKindToGql(string(WorkloadKindDaemonSet)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: ds.Namespace, + Name: ds.Name, + Kind: k8sKindToGql(string(WorkloadKindDaemonSet)), + NumberOfInstances: &numberOfInstances, + // AutoInstrumented: autoInstrumented, + // AutoInstrumentedDecision: decisionText, + // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -236,19 +236,19 @@ func getStatefulSets(ctx context.Context, namespace corev1.Namespace, instrument var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().StatefulSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(sss *appsv1.StatefulSetList) error { for _, ss := range sss.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ss.GetLabels(), string(WorkloadKindStatefulSet), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } + // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ss.GetLabels(), string(WorkloadKindStatefulSet), namespace.GetLabels()) + // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { + // continue + // } numberOfInstances := int(ss.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ - Namespace: ss.Namespace, - Name: ss.Name, - Kind: k8sKindToGql(string(WorkloadKindStatefulSet)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: ss.Namespace, + Name: ss.Name, + Kind: k8sKindToGql(string(WorkloadKindStatefulSet)), + NumberOfInstances: &numberOfInstances, + // AutoInstrumented: autoInstrumented, + // AutoInstrumentedDecision: decisionText, + // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil diff --git a/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx b/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx index 571fc3bcd..5c687db28 100644 --- a/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx +++ b/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx @@ -20,8 +20,8 @@ export const ErrorDropdown: React.FC = ({ title = 'Error Message', value, const options = useMemo(() => { const payload: DropdownOption[] = []; - sources.forEach(({ instrumentedApplicationDetails: { conditions } }) => { - conditions.forEach(({ type, status, message }) => { + sources.forEach(({ conditions }) => { + conditions.forEach(({ status, message }) => { if (status === BACKEND_BOOLEAN.FALSE && !payload.find((opt) => opt.id === message)) { payload.push({ id: message, value: message }); } diff --git a/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx b/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx index 31c53ac9a..92fbfc011 100644 --- a/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx +++ b/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx @@ -19,7 +19,7 @@ export const LanguageDropdown: React.FC = ({ title = 'Programming Languag const options = useMemo(() => { const payload: DropdownOption[] = []; - sources.forEach(({ instrumentedApplicationDetails: { containers } }) => { + sources.forEach(({ containers }) => { containers.forEach(({ language }) => { if (!payload.find((opt) => opt.id === language)) { payload.push({ id: language, value: language }); diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts index e72c56c6b..8d190d9c9 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts +++ b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts @@ -3,16 +3,18 @@ import type { K8sActualSource, WorkloadId } from '@/types'; const buildDrawerItem = (id: WorkloadId, formData: { reportedName: string }, drawerItem: K8sActualSource): K8sActualSource => { const { namespace, name, kind } = id; const { reportedName } = formData; - const { selected, numberOfInstances, instrumentedApplicationDetails } = drawerItem; + const { numberOfInstances, serviceName, conditions, containers, selected } = drawerItem; return { namespace, name, kind, + numberOfInstances, + serviceName, reportedName, + conditions, + containers, selected, - numberOfInstances, - instrumentedApplicationDetails, }; }; diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx index 750696632..4ea0eb2ac 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -79,12 +79,11 @@ export const SourceDrawer: React.FC = () => { const { item } = selectedItem as { item: K8sActualSource }; const hasPresenceOfOtherAgent = - item?.instrumentedApplicationDetails?.conditions?.some( - (condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent'), - ) || false; + item?.conditions?.some((condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent')) || + false; return ( - item?.instrumentedApplicationDetails?.containers?.map( + item?.containers?.map( (container) => ({ type: DataCardFieldTypes.SOURCE_CONTAINER, @@ -146,7 +145,7 @@ export const SourceDrawer: React.FC = () => { ) : ( - + { k8sActualSources = k8sActualSources.filter((source) => !!filters.types.find((type) => type.id === source.kind)); } if (!!filters.onlyErrors) { - k8sActualSources = k8sActualSources.filter((source) => !!source.instrumentedApplicationDetails?.conditions?.find((cond) => cond.status === BACKEND_BOOLEAN.FALSE)); + k8sActualSources = k8sActualSources.filter((source) => !!source.conditions?.find((cond) => cond.status === BACKEND_BOOLEAN.FALSE)); } if (!!filters.errors.length) { - k8sActualSources = k8sActualSources.filter((source) => !!filters.errors.find((error) => !!source.instrumentedApplicationDetails?.conditions?.find((cond) => cond.message === error.id))); + k8sActualSources = k8sActualSources.filter((source) => !!filters.errors.find((error) => !!source.conditions?.find((cond) => cond.message === error.id))); } if (!!filters.languages.length) { - k8sActualSources = k8sActualSources.filter( - (source) => !!filters.languages.find((language) => !!source.instrumentedApplicationDetails?.containers?.find((cont) => cont.language === language.id)), - ); + k8sActualSources = k8sActualSources.filter((source) => !!filters.languages.find((language) => !!source.containers?.find((cont) => cont.language === language.id))); } if (!!filters.monitors.length) { destinations = destinations.filter((destination) => !!filters.monitors.find((metric) => destination.exportedSignals[metric.id])); diff --git a/frontend/webapp/reuseable-components/condition-details/index.tsx b/frontend/webapp/reuseable-components/condition-details/index.tsx index 7f7a06de2..42ac4a9bf 100644 --- a/frontend/webapp/reuseable-components/condition-details/index.tsx +++ b/frontend/webapp/reuseable-components/condition-details/index.tsx @@ -39,7 +39,7 @@ const Row = styled.div` gap: 12px; `; -export const ConditionDetails: React.FC = ({ conditions }) => { +export const ConditionDetails: React.FC = ({ conditions = [] }) => { const [extend, setExtend] = useState(false); const loading = useMemo(() => !conditions.length, [conditions]); diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index e4c65f2d4..9808d3545 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -12,8 +12,8 @@ interface ComputePlatformData { id: string; name: string; computePlatformType: string; - k8sActualNamespace?: K8sActualNamespace; k8sActualNamespaces: K8sActualNamespace[]; + k8sActualNamespace: K8sActualNamespace; k8sActualSources: K8sActualSource[]; destinations: ActualDestination[]; actions: ActionData[]; diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index f2ce90015..937fdd84c 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -9,16 +9,15 @@ export type SourceContainer = { }; export type K8sActualSource = { + namespace: string; name: string; kind: string; - namespace: string; - reportedName: string; numberOfInstances: number; - selected?: boolean; - instrumentedApplicationDetails: { - containers: Array; - conditions: Array; - }; + // serviceName: string; + reportedName: string; + containers: Array; + conditions: Array; + selected?: boolean; // not from backend }; export type WorkloadId = { diff --git a/frontend/webapp/utils/constants/programming-languages.ts b/frontend/webapp/utils/constants/programming-languages.ts index 994a90879..33b09af38 100644 --- a/frontend/webapp/utils/constants/programming-languages.ts +++ b/frontend/webapp/utils/constants/programming-languages.ts @@ -19,31 +19,24 @@ export enum WORKLOAD_PROGRAMMING_LANGUAGES { } export const getMainContainerLanguage = (source: K8sActualSource): WORKLOAD_PROGRAMMING_LANGUAGES => { - const ia = source?.instrumentedApplicationDetails; + const { numberOfInstances, containers } = source; - if (!ia) { - if (source?.numberOfInstances > 0) { + if (!containers) { + if (numberOfInstances > 0) { return WORKLOAD_PROGRAMMING_LANGUAGES.PROCESSING; } else { return WORKLOAD_PROGRAMMING_LANGUAGES.NO_RUNNING_PODS; } } - const containers = ia.containers; - if (!containers) { - return WORKLOAD_PROGRAMMING_LANGUAGES.PROCESSING; - } - // we will filter out the ignored languages as we don't want to account them in the main language - const noneIgnoredLanguages = containers.filter((container) => container.language !== 'ignored'); - if (noneIgnoredLanguages.length === 0) { - return WORKLOAD_PROGRAMMING_LANGUAGES.NO_CONTAINERS; - } + const noneIgnoredLanguages = containers.filter((container) => container.language !== WORKLOAD_PROGRAMMING_LANGUAGES.IGNORED); + if (!noneIgnoredLanguages.length) return WORKLOAD_PROGRAMMING_LANGUAGES.NO_CONTAINERS; // find the first container with valid language - const mainContainer = noneIgnoredLanguages.find((container) => container.language !== 'unknown'); - if (!mainContainer) { - return WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN; // no valid language found, return the first one - } - return mainContainer.language as WORKLOAD_PROGRAMMING_LANGUAGES; + const mainContainer = noneIgnoredLanguages.find((container) => container.language !== WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN); + // no valid language found, return the first one + if (!mainContainer) return WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN; + + return mainContainer.language; }; diff --git a/frontend/webapp/utils/functions/strings/get-entity-label/index.ts b/frontend/webapp/utils/functions/strings/get-entity-label/index.ts index 930bad81e..ff6c0cae5 100644 --- a/frontend/webapp/utils/functions/strings/get-entity-label/index.ts +++ b/frontend/webapp/utils/functions/strings/get-entity-label/index.ts @@ -39,7 +39,7 @@ export const getEntityLabel = ( break; } - if (extended) return type + (name ? ` (${name})` : ''); + if (extended) return type + (name && name !== type ? ` (${name})` : ''); else if (prioritizeDisplayName) return name || type; else return type; }; diff --git a/frontend/webapp/utils/functions/strings/get-health-status/index.ts b/frontend/webapp/utils/functions/strings/get-health-status/index.ts index 247fc09d1..f5de8bcf7 100644 --- a/frontend/webapp/utils/functions/strings/get-health-status/index.ts +++ b/frontend/webapp/utils/functions/strings/get-health-status/index.ts @@ -2,7 +2,7 @@ import { BACKEND_BOOLEAN } from '@/utils'; import { STATUSES, type ActualDestination, type K8sActualSource } from '@/types'; export const getHealthStatus = (item: K8sActualSource | ActualDestination) => { - const conditions = (item as K8sActualSource)?.instrumentedApplicationDetails?.conditions || (item as ActualDestination)?.conditions || []; + const conditions = item?.conditions || []; const isUnhealthy = !conditions.length || !!conditions.find(({ status }) => status === BACKEND_BOOLEAN.FALSE); return isUnhealthy ? STATUSES.UNHEALTHY : STATUSES.HEALTHY; From 086b1c8f38d352a1d4bb207a1fad54f66994ad37 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 19:02:41 +0200 Subject: [PATCH 070/259] Remove 'sources' resource from UIClusterRole permissions to refine access control --- cli/cmd/resources/ui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index 6c50853f7..d0cea23a7 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -242,7 +242,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { }, { // Needed to watch instrumented applications APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances", "sources"}, + Resources: []string{"instrumentedapplications", "instrumentationinstances"}, Verbs: []string{"watch"}, }, }, From 55112cc8d34d0f120335d38c27ad6c19eb358cf8 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Sat, 28 Dec 2024 14:02:28 -0500 Subject: [PATCH 071/259] Implement Source CRD instrumentation (#2059) This introduces the basic implementation for instrumenting/uninstrumenting via `Source` objects (https://github.com/odigos-io/odigos/pull/2037) This does not remove current instrumentation with the `odigos-instrumentation` label, and does not do any checks to avoid conflicts etc. That work will follow. Right now this just drops in the basic workflow alongside the current label approach. Changes: * Add a `SourceReconciler` to the `startlangdetection` Instrumentor controller. * Parse workload from `Source` object * If the `Source` is not being deleted, add 2 finalizers (one used for `Source` deletion, and another used by the `deleteinstrumentedapplication` controller), add workload labels to the `Source` for lookup, and continue with flow of creating an `InstrumentationConfig` by calling `requestOdigletsToCalculateRuntimeDetails` * If the `Source` is being deleted (determined by a `DeletionTimestamp` being present in an Update event, with the finalizer blocking deletion), remove the source finalizer and delete the `InstrumentationConfig`. * Add a function `GetSourceListForWorkload` everywhere we currently call `workload.IsWorkloadInstrumentationEffectiveEnabled` that returns any `Source` objects referencing the workload. * Add `SourceReconciler` to `deleteinstrumentedapplication` Instrumentor controller * When `Source` object is deleted, remove instrumented application finalizer and continue with reconciling the workload, which should trigger uninstrumentation based on the new checks for `GetSourceListForWorkload` throughout I'm not a fan of using 2 finalizers and worry that we could end up with a deadlock/race. This is mostly due to InstrumentedApplication, and deprecating InstrumentedApplication should simplify the work required for this greatly. Still needs: * Namespace instrumentation and exclusion * Switch `odigos-instrumentation` label to be shorthand for creating a `Source` on that workload/namespace * This includes removing current label checks with `workload.IsWorkloadInstrumentationEffectiveEnabled` * UI integration * Webhook for validation (immutability) and defaulting (labels) --- .github/workflows/build.yaml | 3 +- .github/workflows/e2e.yaml | 1 + Makefile | 7 +- api/odigos/v1alpha1/source_types.go | 19 +++ cli/cmd/resources/instrumentor.go | 10 ++ cli/cmd/uninstall.go | 14 ++ .../templates/instrumentor/clusterrole.yaml | 18 +++ .../deleteinstrumentedapplication/common.go | 10 +- .../instrumentedapplication_controller.go | 16 +- .../deleteinstrumentedapplication/manager.go | 15 ++ .../source_controller.go | 71 +++++++++ .../controllers/startlangdetection/manager.go | 17 +- .../startlangdetection/source_controller.go | 85 ++++++++++ .../workload_controllers.go | 10 +- k8sutils/pkg/consts/consts.go | 10 ++ k8sutils/pkg/predicate/creation.go | 27 ++++ tests/e2e/source/01-sources.yaml | 54 +++++++ tests/e2e/source/chainsaw-test.yaml | 150 ++++++++++++++++++ .../source/tracesql/context-propagation.yaml | 13 ++ .../source/tracesql/resource-attributes.yaml | 14 ++ .../e2e/source/tracesql/span-attributes.yaml | 18 +++ tests/e2e/source/tracesql/wait-for-trace.yaml | 11 ++ 22 files changed, 584 insertions(+), 9 deletions(-) create mode 100644 instrumentor/controllers/deleteinstrumentedapplication/source_controller.go create mode 100644 instrumentor/controllers/startlangdetection/source_controller.go create mode 100644 k8sutils/pkg/predicate/creation.go create mode 100644 tests/e2e/source/01-sources.yaml create mode 100644 tests/e2e/source/chainsaw-test.yaml create mode 100644 tests/e2e/source/tracesql/context-propagation.yaml create mode 100644 tests/e2e/source/tracesql/resource-attributes.yaml create mode 100644 tests/e2e/source/tracesql/span-attributes.yaml create mode 100644 tests/e2e/source/tracesql/wait-for-trace.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index db62ff610..55dce9449 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,6 +6,7 @@ on: branches: - main - stable + - feature/source-crd - greatwall jobs: @@ -159,4 +160,4 @@ jobs: - name: Test procdiscovery module working-directory: ./procdiscovery run: | - go test -v ./... \ No newline at end of file + go test -v ./... diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index e65fa4fb4..cc88e1882 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -6,6 +6,7 @@ on: branches: - main - stable + - feature/source-crd - greatwall concurrency: diff --git a/Makefile b/Makefile index 4a67ca991..61f6679c7 100644 --- a/Makefile +++ b/Makefile @@ -201,6 +201,11 @@ cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) +.PHONY: cli-uninstall +cli-uninstall: + @echo "Uninstalling odigos from source. version: $(ODIGOS_CLI_VERSION)" + cd ./cli ; go run -tags=embed_manifests . uninstall + .PHONY: cli-upgrade cli-upgrade: @echo "Upgrading odigos from source. version: $(ODIGOS_CLI_VERSION)" @@ -256,4 +261,4 @@ dev-nop-destination: .PHONY: dev-add-backpressue-destination dev-backpressue-destination: - kubectl apply -f ./tests/backpressure-exporter.yaml \ No newline at end of file + kubectl apply -f ./tests/backpressure-exporter.yaml diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index 3dd31ef63..ae4ab1799 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -17,8 +17,13 @@ limitations under the License. package v1alpha1 import ( + "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) @@ -62,6 +67,20 @@ type SourceList struct { Items []Source `json:"items"` } +// GetSourceListForWorkload returns a SourceList of all Sources that have matching +// workload name, namespace, and kind labels for an object. In theory, this should only +// ever return a list with 0 or 1 items, but due diligence should handle unexpected cases. +func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (SourceList, error) { + sourceList := SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + consts.WorkloadNameLabel: obj.GetName(), + consts.WorkloadNamespaceLabel: obj.GetNamespace(), + consts.WorkloadKindLabel: obj.GetObjectKind().GroupVersionKind().Kind, + }) + err := kubeClient.List(ctx, &sourceList, &client.ListOptions{LabelSelector: selector}) + return sourceList, err +} + func init() { SchemeBuilder.Register(&Source{}, &SourceList{}) } diff --git a/cli/cmd/resources/instrumentor.go b/cli/cmd/resources/instrumentor.go index 89e3c7d8e..ef6c68fda 100644 --- a/cli/cmd/resources/instrumentor.go +++ b/cli/cmd/resources/instrumentor.go @@ -191,6 +191,16 @@ func NewInstrumentorClusterRole() *rbacv1.ClusterRole { Resources: []string{"instrumentationconfigs"}, Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources"}, + Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, + }, + { + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources/finalizers"}, + Verbs: []string{"update"}, + }, }, } } diff --git a/cli/cmd/uninstall.go b/cli/cmd/uninstall.go index c7998609f..608fa981f 100644 --- a/cli/cmd/uninstall.go +++ b/cli/cmd/uninstall.go @@ -387,6 +387,20 @@ func uninstallCRDs(ctx context.Context, cmd *cobra.Command, client *kube.Client, return err } + // Clear finalizers from Source objects so they can be uninstalled + sources, err := client.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) + for _, i := range sources.Items { + source, err := client.OdigosClient.Sources(i.Namespace).Get(ctx, i.Name, metav1.GetOptions{}) + if err != nil { + return err + } + source.SetFinalizers([]string{}) + _, err = client.OdigosClient.Sources(i.Namespace).Update(ctx, source, metav1.UpdateOptions{}) + if err != nil { + return err + } + } + for _, i := range list.Items { err = client.ApiExtensions.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, i.Name, metav1.DeleteOptions{}) if err != nil { diff --git a/helm/odigos/templates/instrumentor/clusterrole.yaml b/helm/odigos/templates/instrumentor/clusterrole.yaml index 29d3a4376..f4151e42c 100644 --- a/helm/odigos/templates/instrumentor/clusterrole.yaml +++ b/helm/odigos/templates/instrumentor/clusterrole.yaml @@ -78,3 +78,21 @@ rules: - patch - update - watch + - apiGroups: + - odigos.io + resources: + - sources + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - odigos.io + resources: + - sources/finalizers + verbs: + - update diff --git a/instrumentor/controllers/deleteinstrumentedapplication/common.go b/instrumentor/controllers/deleteinstrumentedapplication/common.go index d70b9c2f8..ef5ee898f 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/common.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/common.go @@ -3,6 +3,7 @@ package deleteinstrumentedapplication import ( "context" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -21,7 +22,14 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work } if instEffectiveEnabled { - return nil + // Check if a Source object exists for this workload + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, kubeClient, workloadObject) + if err != nil { + return err + } + if len(sourceList.Items) == 0 { + return nil + } } if err := deleteWorkloadInstrumentedApplication(ctx, kubeClient, workloadObject); err != nil { diff --git a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go index a3c504cca..52866a90b 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go @@ -20,9 +20,10 @@ import ( "context" "fmt" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -90,9 +91,16 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c } if !instEffectiveEnabled { - logger.Info("Deleting instrumented application for non-enabled workload") - err := r.Client.Delete(ctx, &instrumentedApplication) - return ctrl.Result{}, client.IgnoreNotFound(err) + // Check if a Source object exists for this workload + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, r.Client, workloadObject) + if err != nil { + return ctrl.Result{}, err + } + if len(sourceList.Items) == 0 { + logger.Info("Deleting instrumented application for non-enabled workload") + err := r.Client.Delete(ctx, &instrumentedApplication) + return ctrl.Result{}, client.IgnoreNotFound(err) + } } return ctrl.Result{}, nil diff --git a/instrumentor/controllers/deleteinstrumentedapplication/manager.go b/instrumentor/controllers/deleteinstrumentedapplication/manager.go index eb99d5347..da00e9827 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/manager.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/manager.go @@ -2,6 +2,8 @@ package deleteinstrumentedapplication import ( odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/predicate" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" @@ -74,6 +76,19 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } + err = builder. + ControllerManagedBy(mgr). + Named("deleteinstrumentedapplication-source"). + WithEventFilter(&k8sutils.OnlyUpdatesPredicate{}). + For(&odigosv1.Source{}). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) +if err != nil { + return err +} + return nil } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go new file mode 100644 index 000000000..a60656046 --- /dev/null +++ b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go @@ -0,0 +1,71 @@ +/* +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 deleteinstrumentedapplication + +import ( + "context" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +type SourceReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling Deleted Source object", "name", req.Name, "namespace", req.Namespace) + + source := &v1alpha1.Source{} + err := r.Get(ctx, req.NamespacedName, source) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + if !source.DeletionTimestamp.IsZero() { + logger.Info("Reconciling workload for deleted Source object", "name", req.Name, "namespace", req.Namespace) + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // TODO: Deleted objects should be filtered in the event filter + return ctrl.Result{}, err + } + + if controllerutil.ContainsFinalizer(source, consts.InstrumentedApplicationFinalizer) { + controllerutil.RemoveFinalizer(source, consts.InstrumentedApplicationFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + } + + err = reconcileWorkloadObject(ctx, r.Client, obj) + if err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil +} diff --git a/instrumentor/controllers/startlangdetection/manager.go b/instrumentor/controllers/startlangdetection/manager.go index 2be2646cd..6504edcbd 100644 --- a/instrumentor/controllers/startlangdetection/manager.go +++ b/instrumentor/controllers/startlangdetection/manager.go @@ -1,12 +1,14 @@ package startlangdetection import ( - odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/predicate" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" ) func SetupWithManager(mgr ctrl.Manager) error { @@ -74,5 +76,18 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } + err = builder. + ControllerManagedBy(mgr). + Named("startlangdetection-source"). + For(&v1alpha1.Source{}). + WithEventFilter(predicate.Or(&odigospredicate.CreationPredicate{}, &odigospredicate.OnlyUpdatesPredicate{})). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) + if err != nil { + return err + } + return nil } diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go new file mode 100644 index 000000000..6e4269981 --- /dev/null +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -0,0 +1,85 @@ +package startlangdetection + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" + k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/utils" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" +) + +type SourceReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling Source object", "name", req.Name, "namespace", req.Namespace) + source := &v1alpha1.Source{} + err := r.Get(ctx, req.NamespacedName, source) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // TODO: Deleted objects should be filtered in the event filter + return ctrl.Result{}, err + } + instConfigName := workload.CalculateWorkloadRuntimeObjectName(source.Spec.Workload.Name, source.Spec.Workload.Kind) + + if source.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(source, consts.SourceFinalizer) { + controllerutil.AddFinalizer(source, consts.SourceFinalizer) + // Removed by deleteinstrumentedapplication controller + controllerutil.AddFinalizer(source, consts.InstrumentedApplicationFinalizer) + + if source.Labels == nil { + source.Labels = make(map[string]string) + } + source.Labels[consts.WorkloadNameLabel] = source.Spec.Workload.Name + source.Labels[consts.WorkloadNamespaceLabel] = source.Spec.Workload.Namespace + source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) + + if err := r.Update(ctx, source); err != nil { + return k8sutils.K8SUpdateErrorHandler(err) + } + + err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) + return ctrl.Result{}, err + } + } else { + // Source is being deleted + if controllerutil.ContainsFinalizer(source, consts.SourceFinalizer) { + // Remove the finalizer first, because if the InstrumentationConfig is not found we + // will deadlock on the finalizer never getting removed. + // On the other hand, this could end up deleting a Source with an orphaned InstrumentationConfig. + controllerutil.RemoveFinalizer(source, consts.SourceFinalizer) + if err := r.Update(ctx, source); err != nil { + return ctrl.Result{}, err + } + + instConfig := &v1alpha1.InstrumentationConfig{} + err = r.Client.Get(ctx, types.NamespacedName{Name: instConfigName, Namespace: req.Namespace}, instConfig) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + err = r.Client.Delete(ctx, instConfig) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + } + } + + return ctrl.Result{}, err +} diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index b8085d4dc..0ef0e7489 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -10,6 +10,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" "k8s.io/apimachinery/pkg/types" @@ -59,7 +60,14 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor } if !instrumented { - return ctrl.Result{}, nil + // Check if a Source object exists for this workload + sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, k8sClient, obj) + if err != nil { + return ctrl.Result{}, err + } + if len(sourceList.Items) == 0 { + return ctrl.Result{}, nil + } } err = requestOdigletsToCalculateRuntimeDetails(ctx, k8sClient, instConfigName, req.Namespace, obj, scheme) diff --git a/k8sutils/pkg/consts/consts.go b/k8sutils/pkg/consts/consts.go index 686370f2a..7b17a3fcf 100644 --- a/k8sutils/pkg/consts/consts.go +++ b/k8sutils/pkg/consts/consts.go @@ -61,3 +61,13 @@ var ( // this value must be in sync with the one defined in the kubeVersion field in Chart.yaml MinK8SVersionForInstallation = version.MustParse("v1.20.15-0") ) + +const ( + SourceFinalizer = "odigos.io/source-finalizer" + // TODO: Needed until InstrumentedApplication is removed + InstrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" + + WorkloadNameLabel = "odigos.io/workload-name" + WorkloadNamespaceLabel = "odigos.io/workload-namespace" + WorkloadKindLabel = "odigos.io/workload-kind" +) diff --git a/k8sutils/pkg/predicate/creation.go b/k8sutils/pkg/predicate/creation.go new file mode 100644 index 000000000..921a62795 --- /dev/null +++ b/k8sutils/pkg/predicate/creation.go @@ -0,0 +1,27 @@ +package predicate + +import ( + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// CreationPredicate only allows create events. +type CreationPredicate struct{} + +func (i CreationPredicate) Create(e event.CreateEvent) bool { + return true +} + +func (i CreationPredicate) Update(e event.UpdateEvent) bool { + return false +} + +func (i CreationPredicate) Delete(e event.DeleteEvent) bool { + return false +} + +func (i CreationPredicate) Generic(e event.GenericEvent) bool { + return false +} + +var _ predicate.Predicate = &DeletionPredicate{} \ No newline at end of file diff --git a/tests/e2e/source/01-sources.yaml b/tests/e2e/source/01-sources.yaml new file mode 100644 index 000000000..3b82128ba --- /dev/null +++ b/tests/e2e/source/01-sources.yaml @@ -0,0 +1,54 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: frontend + namespace: default +spec: + workload: + name: frontend + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: coupon + namespace: default +spec: + workload: + name: coupon + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: inventory + namespace: default +spec: + workload: + name: inventory + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: pricing + namespace: default +spec: + workload: + name: pricing + namespace: default + kind: Deployment +--- +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: membership + namespace: default +spec: + workload: + name: membership + namespace: default + kind: Deployment diff --git a/tests/e2e/source/chainsaw-test.yaml b/tests/e2e/source/chainsaw-test.yaml new file mode 100644 index 000000000..7e3e4ff30 --- /dev/null +++ b/tests/e2e/source/chainsaw-test.yaml @@ -0,0 +1,150 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: source +spec: + description: This e2e test runs a multi-apps scenario with Source instrumentation + skipDelete: true + steps: + - name: Prepare destination + try: + - script: + timeout: 60s + content: | + helm repo add grafana https://grafana.github.io/helm-charts + helm repo update + helm install e2e-tests grafana/tempo -n traces --create-namespace --set tempo.storage.trace.block.version=vParquet4 \ + --set tempo.ingester.trace_idle_period=5s \ + --set tempo.ingester.max_block_duration=20s \ + --version 1.10.1 + - assert: + file: ../../common/assert/tempo-running.yaml + - name: Wait for destination to be ready + try: + - script: + timeout: 60s + content: ../../common/wait_for_dest.sh + - name: Install Odigos + try: + - script: + content: | + if [ "$MODE" = "cross-cloud-tests" ]; then + ../../../cli/odigos install --namespace odigos-test --version "$COMMIT_HASH" --image-prefix=public.ecr.aws/y2v0v6s7 + else + ../../../cli/odigos install --namespace odigos-test --version e2e-test + fi + timeout: 60s + - assert: + file: ../../common/assert/odigos-installed.yaml + + - name: Install Demo App + try: + - script: + timeout: 200s + content: | + if [ "$MODE" != "cross-cloud-tests" ]; then + docker pull keyval/odigos-demo-inventory:v0.1 + docker pull keyval/odigos-demo-membership:v0.1 + docker pull keyval/odigos-demo-coupon:v0.1 + docker pull keyval/odigos-demo-inventory:v0.1 + docker pull keyval/odigos-demo-frontend:v0.2 + kind load docker-image keyval/odigos-demo-inventory:v0.1 + kind load docker-image keyval/odigos-demo-membership:v0.1 + kind load docker-image keyval/odigos-demo-coupon:v0.1 + kind load docker-image keyval/odigos-demo-inventory:v0.1 + kind load docker-image keyval/odigos-demo-frontend:v0.2 + else + echo "Skipping docker pull and kind load for cross-cloud-tests mode" + fi + - apply: + file: ../../common/apply/install-simple-demo.yaml + - script: + timeout: 70s + content: | + kubectl wait --for=condition=ready pod -l app=frontend --timeout=60s + kubectl wait --for=condition=ready pod -l app=coupon --timeout=60s + kubectl wait --for=condition=ready pod -l app=inventory --timeout=60s + kubectl wait --for=condition=ready pod -l app=pricing --timeout=60s + kubectl wait --for=condition=ready pod -l app=membership --timeout=60s + - assert: + file: ../../common/assert/simple-demo-installed.yaml + - name: Instrument Deployments + try: + - apply: + file: 01-sources.yaml + - name: Assert Runtime Detected + try: + - assert: + timeout: 2m + file: ../../common/assert/simple-demo-runtime-detected.yaml + - name: Add Destination + try: + - apply: + file: ../../common/apply/add-tempo-traces-destination.yaml + - name: Odigos pipeline ready + try: + - assert: + file: ../../common/assert/pipeline-ready.yaml + - name: Simple-demo instrumented after destination added + try: + - assert: + file: ../../common/assert/simple-demo-instrumented.yaml + - name: Generate Traffic + try: + - script: + timeout: 300s + content: | + # Apply the job + kubectl apply -f ../../common/apply/generate-traffic-job.yaml + + # Wait for the job to complete + job_name=$(kubectl get -f ../../common/apply/generate-traffic-job.yaml -o=jsonpath='{.metadata.name}') + kubectl wait --for=condition=complete job/$job_name + + # Delete the job + kubectl delete -f ../../common/apply/generate-traffic-job.yaml + + while true; do + # wait for traces to be available + sleep 8 + + # Run the wait-for-trace script + echo "Running TraceQL test at $(date)" + ../../common/traceql_runner.sh tracesql/wait-for-trace.yaml + + if [ $? -eq 0 ]; then + break + else + ../../common/flush_traces.sh + sleep 5 + fi + done + - name: Verify Trace - Context Propagation + try: + - script: + timeout: 30s + content: | + ../../common/traceql_runner.sh tracesql/context-propagation.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Verify Trace - Resource Attributes + try: + - script: + content: | + ../../common/traceql_runner.sh tracesql/resource-attributes.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Verify Trace - Span Attributes + try: + - script: + timeout: 60s + content: | + ../../common/traceql_runner.sh tracesql/span-attributes.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system diff --git a/tests/e2e/source/tracesql/context-propagation.yaml b/tests/e2e/source/tracesql/context-propagation.yaml new file mode 100644 index 000000000..9c463f9b3 --- /dev/null +++ b/tests/e2e/source/tracesql/context-propagation.yaml @@ -0,0 +1,13 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: This test checks if the context propagation is working correctly between different languages +query: | + { resource.service.name = "frontend" && resource.telemetry.sdk.language = "java" && + span.http.request.method = "POST" && span.http.route = "/buy" && span:kind = server } + >> ( + { resource.service.name = "pricing" && resource.telemetry.sdk.language = "dotnet" } && + { resource.service.name = "inventory" && resource.telemetry.sdk.language = "python" } && + ({ resource.service.name = "coupon" && resource.telemetry.sdk.language = "nodejs" } + >> { resource.service.name = "membership" && resource.telemetry.sdk.language = "go" })) +expected: + count: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/resource-attributes.yaml b/tests/e2e/source/tracesql/resource-attributes.yaml new file mode 100644 index 000000000..f6b56aef5 --- /dev/null +++ b/tests/e2e/source/tracesql/resource-attributes.yaml @@ -0,0 +1,14 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: | + This test check the following resource attributes: + A. odigos.version attribute exists on all spans and has the correct value + B. Kubernetes attributes are correctly set on all spans + At the time of writing this test, TraceQL api does not support not equal to nil so we use regex instead. +query: | + { resource.odigos.version !~ ".*" || + resource.k8s.deployment.name !~ ".*" || + resource.k8s.node.name !~ "(kind-control-plane|aks-.*)" || + resource.k8s.pod.name !~ ".*" } +expected: + count: 0 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/span-attributes.yaml b/tests/e2e/source/tracesql/span-attributes.yaml new file mode 100644 index 000000000..d508d4a39 --- /dev/null +++ b/tests/e2e/source/tracesql/span-attributes.yaml @@ -0,0 +1,18 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: | + This test checks the span attributes for a specific trace. + TODO - JS, Python and DotNet SDK are not generating data in latest semconv. add additional checks when they are updated. +query: | + { resource.service.name = "frontend" && resource.telemetry.sdk.language = "java" && + span.http.request.method = "POST" && span.http.route = "/buy" && span:kind = server && + span.http.response.status_code = 200 && span.url.query = "id=123" } + >> ( + { resource.service.name = "pricing" && resource.telemetry.sdk.language = "dotnet" && span:kind = server } && + { resource.service.name = "inventory" && resource.telemetry.sdk.language = "python" && span:kind = server } && + ({ resource.service.name = "coupon" && resource.telemetry.sdk.language = "nodejs" && span:kind = server } + >> { resource.service.name = "membership" && resource.telemetry.sdk.language = "go" && + span.http.request.method = "GET" && span:kind = server && + span.http.response.status_code = 200 && span.url.path = "/isMember" })) +expected: + count: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/wait-for-trace.yaml b/tests/e2e/source/tracesql/wait-for-trace.yaml new file mode 100644 index 000000000..a88f58987 --- /dev/null +++ b/tests/e2e/source/tracesql/wait-for-trace.yaml @@ -0,0 +1,11 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: This test waits for a trace that goes from frontend to pricing, inventory, coupon, and membership services +query: | + { resource.service.name = "frontend" } && + { resource.service.name = "pricing" } && + { resource.service.name = "inventory" } && + { resource.service.name = "coupon" } && + { resource.service.name = "membership" } +expected: + count: 1 \ No newline at end of file From d2fa4ad9e841605a1126336534b4d1f8582d8090 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 09:35:06 +0200 Subject: [PATCH 072/259] fix: build error --- .../main/sources/source-drawer-container/build-drawer-item.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts index 8d190d9c9..1ed993fec 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts +++ b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts @@ -3,14 +3,13 @@ import type { K8sActualSource, WorkloadId } from '@/types'; const buildDrawerItem = (id: WorkloadId, formData: { reportedName: string }, drawerItem: K8sActualSource): K8sActualSource => { const { namespace, name, kind } = id; const { reportedName } = formData; - const { numberOfInstances, serviceName, conditions, containers, selected } = drawerItem; + const { numberOfInstances, conditions, containers, selected } = drawerItem; return { namespace, name, kind, numberOfInstances, - serviceName, reportedName, conditions, containers, From 1c9e6e9799d0d5583087b4ac977e94770863a6c6 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 10:31:04 +0200 Subject: [PATCH 073/259] fix: code review --- common/consts/consts.go | 5 +- frontend/graph/conversions.go | 1 - frontend/graph/generated.go | 55 ------------------- frontend/graph/model/models_gen.go | 1 - frontend/graph/schema.graphqls | 1 - frontend/graph/schema.resolvers.go | 2 +- frontend/services/namespaces.go | 2 +- frontend/services/sources.go | 41 ++++---------- .../graphql/queries/compute-platform.ts | 1 - frontend/webapp/types/sources.ts | 1 - k8sutils/pkg/workload/workload.go | 39 ------------- 11 files changed, 16 insertions(+), 133 deletions(-) diff --git a/common/consts/consts.go b/common/consts/consts.go index e84a8c08d..f053c7b61 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -15,6 +15,9 @@ const ( OdigosInstrumentationLabel = "odigos-instrumentation" InstrumentationEnabled = "enabled" InstrumentationDisabled = "disabled" + OdigosNamespaceAnnotation = "odigos.io/workload-namespace" + OdigosWorkloadKindAnnotation = "odigos.io/workload-kind" + OdigosWorkloadNameAnnotation = "odigos.io/workload-name" OdigosReportedNameAnnotation = "odigos.io/reported-name" // GatewayMaxConnectionAge and GatewayMaxConnectionAgeGrace are the default values for the gateway collector. @@ -32,7 +35,7 @@ const ( ) var ( - PodsNotFoundErr = errors.New("could not find a ready pod") + ErrorPodsNotFound = errors.New("could not find a ready pod") ) var ( diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index d4f093047..d7a06642e 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -77,7 +77,6 @@ func instrumentationConfigToActualSource(instruConfig v1alpha1.InstrumentationCo Kind: k8sKindToGql(instruConfig.OwnerReferences[0].Kind), Name: instruConfig.OwnerReferences[0].Name, NumberOfInstances: nil, - ServiceName: &instruConfig.Name, ReportedName: &instruConfig.Spec.ServiceName, Containers: containers, Conditions: conditions, diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index 5da74e178..22cd3662b 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -275,7 +275,6 @@ type ComplexityRoot struct { Namespace func(childComplexity int) int NumberOfInstances func(childComplexity int) int ReportedName func(childComplexity int) int - ServiceName func(childComplexity int) int } LatencySamplerAction struct { @@ -1458,13 +1457,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualSource.ReportedName(childComplexity), true - case "K8sActualSource.serviceName": - if e.complexity.K8sActualSource.ServiceName == nil { - break - } - - return e.complexity.K8sActualSource.ServiceName(childComplexity), true - case "LatencySamplerAction.details": if e.complexity.LatencySamplerAction.Details == nil { break @@ -4031,8 +4023,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) case "containers": @@ -8765,8 +8755,6 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) case "containers": @@ -8964,47 +8952,6 @@ func (ec *executionContext) fieldContext_K8sActualSource_numberOfInstances(_ con return fc, nil } -func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_serviceName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ServiceName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*string) - fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_K8sActualSource_serviceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "K8sActualSource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) if err != nil { @@ -19676,8 +19623,6 @@ func (ec *executionContext) _K8sActualSource(ctx context.Context, sel ast.Select } case "numberOfInstances": out.Values[i] = ec._K8sActualSource_numberOfInstances(ctx, field, obj) - case "serviceName": - out.Values[i] = ec._K8sActualSource_serviceName(ctx, field, obj) case "reportedName": out.Values[i] = ec._K8sActualSource_reportedName(ctx, field, obj) case "containers": diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 46078a80b..6e4b07780 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -311,7 +311,6 @@ type K8sActualSource struct { Name string `json:"name"` Kind K8sResourceKind `json:"kind"` NumberOfInstances *int `json:"numberOfInstances,omitempty"` - ServiceName *string `json:"serviceName,omitempty"` ReportedName *string `json:"reportedName,omitempty"` Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` Conditions []*Condition `json:"conditions,omitempty"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 49f4ba8d8..f2fb30fa6 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -78,7 +78,6 @@ type K8sActualSource { name: String! kind: K8sResourceKind! numberOfInstances: Int - serviceName: String reportedName: String containers: [SourceContainerRuntimeDetails!] conditions: [Condition!] diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index be2134cee..c18266f24 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -87,7 +87,7 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod // Convert each instrumented application to the K8sActualSource type for _, instruConfig := range instrumentationConfigs.Items { actualSource := instrumentationConfigToActualSource(instruConfig) - // services.AddHealthyInstrumentationInstancesCondition(ctx, &instruConfig, actualSource) + services.AddHealthyInstrumentationInstancesCondition(ctx, &instruConfig, actualSource) actualSources = append(actualSources, actualSource) } diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index a4fb57780..7b4e34a62 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -179,7 +179,7 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo for _, workload := range workloads { currWorkload := workload g.Go(func() error { - // Only label selected sources, ignore the rest + // Only instrument/uninstrument selected workloads, ignore the rest if currWorkload.Selected != nil { return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 51e7b483d..81bcc7f60 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -178,19 +178,12 @@ func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumenta var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().Deployments(namespace.Name).List, ctx, metav1.ListOptions{}, func(deps *appsv1.DeploymentList) error { for _, dep := range deps.Items { - // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(dep.GetLabels(), string(WorkloadKindDeployment), namespace.GetLabels()) - // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - // continue - // } numberOfInstances := int(dep.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ Namespace: dep.Namespace, Name: dep.Name, Kind: k8sKindToGql(string(WorkloadKindDeployment)), NumberOfInstances: &numberOfInstances, - // AutoInstrumented: autoInstrumented, - // AutoInstrumentedDecision: decisionText, - // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -207,19 +200,12 @@ func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentat var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().DaemonSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(dss *appsv1.DaemonSetList) error { for _, ds := range dss.Items { - // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ds.GetLabels(), string(WorkloadKindDaemonSet), namespace.GetLabels()) - // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - // continue - // } numberOfInstances := int(ds.Status.NumberReady) response = append(response, model.K8sActualSource{ Namespace: ds.Namespace, Name: ds.Name, Kind: k8sKindToGql(string(WorkloadKindDaemonSet)), NumberOfInstances: &numberOfInstances, - // AutoInstrumented: autoInstrumented, - // AutoInstrumentedDecision: decisionText, - // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -236,19 +222,12 @@ func getStatefulSets(ctx context.Context, namespace corev1.Namespace, instrument var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().StatefulSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(sss *appsv1.StatefulSetList) error { for _, ss := range sss.Items { - // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ss.GetLabels(), string(WorkloadKindStatefulSet), namespace.GetLabels()) - // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - // continue - // } numberOfInstances := int(ss.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ Namespace: ss.Namespace, Name: ss.Name, Kind: k8sKindToGql(string(WorkloadKindStatefulSet)), NumberOfInstances: &numberOfInstances, - // AutoInstrumented: autoInstrumented, - // AutoInstrumentedDecision: decisionText, - // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -320,26 +299,26 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s } func getSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { - sourceList, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-namespace": nsName, - "odigos.io/workload-name": workloadName, - "odigos.io/workload-kind": string(workloadKind), + source, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ + consts.OdigosNamespaceAnnotation: nsName, + consts.OdigosWorkloadNameAnnotation: workloadName, + consts.OdigosWorkloadKindAnnotation: string(workloadKind), }).String()}) if err != nil { return nil, err } - if len(sourceList.Items) == 0 { + if len(source.Items) == 0 { return nil, errors.New("\"" + workloadName + "\"" + " source not found") } - if len(sourceList.Items) > 1 { - return nil, errors.New("\"" + workloadName + "\"" + " expected to get 1 source got " + string(len(sourceList.Items))) + if len(source.Items) > 1 { + return nil, errors.New("\"" + workloadName + "\"" + " expected to get 1 source got " + string(len(source.Items))) } - crdName := sourceList.Items[0].Name - source, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) + crdName := source.Items[0].Name + crd, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) - return source, err + return crd, err } func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index f8c7b0dd8..afe9ca761 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -11,7 +11,6 @@ export const GET_COMPUTE_PLATFORM = gql` name kind numberOfInstances - # serviceName reportedName containers { containerName diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index 937fdd84c..62c207116 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -13,7 +13,6 @@ export type K8sActualSource = { name: string; kind: string; numberOfInstances: number; - // serviceName: string; reportedName: string; containers: Array; conditions: Array; diff --git a/k8sutils/pkg/workload/workload.go b/k8sutils/pkg/workload/workload.go index d8b6369a8..581540cd0 100644 --- a/k8sutils/pkg/workload/workload.go +++ b/k8sutils/pkg/workload/workload.go @@ -120,45 +120,6 @@ func GetInstrumentationLabelValue(labels map[string]string) *bool { return nil } -func GetInstrumentationLabelTexts(workloadLabels map[string]string, workloadKind string, nsLabels map[string]string) (workloadText, nsText, decisionText string, sourceInstrumented bool) { - workloadLabel, workloadFound := workloadLabels[consts.OdigosInstrumentationLabel] - nsLabel, nsFound := nsLabels[consts.OdigosInstrumentationLabel] - - if workloadFound { - workloadText = consts.OdigosInstrumentationLabel + "=" + workloadLabel - } else { - workloadText = consts.OdigosInstrumentationLabel + " label not set" - } - - if nsFound { - nsText = consts.OdigosInstrumentationLabel + "=" + nsLabel - } else { - nsText = consts.OdigosInstrumentationLabel + " label not set" - } - - if workloadFound { - sourceInstrumented = workloadLabel == consts.InstrumentationEnabled - if sourceInstrumented { - decisionText = "Workload is instrumented because the " + workloadKind + " contains the label '" + consts.OdigosInstrumentationLabel + "=" + workloadLabel + "'" - } else { - decisionText = "Workload is NOT instrumented because the " + workloadKind + " contains the label '" + consts.OdigosInstrumentationLabel + "=" + workloadLabel + "'" - } - } else { - sourceInstrumented = nsLabel == consts.InstrumentationEnabled - if sourceInstrumented { - decisionText = "Workload is instrumented because the " + workloadKind + " is not labeled, and the namespace is labeled with '" + consts.OdigosInstrumentationLabel + "=" + nsLabel + "'" - } else { - if nsFound { - decisionText = "Workload is NOT instrumented because the " + workloadKind + " is not labeled, and the namespace is labeled with '" + consts.OdigosInstrumentationLabel + "=" + nsLabel + "'" - } else { - decisionText = "Workload is NOT instrumented because neither the workload nor the namespace has the '" + consts.OdigosInstrumentationLabel + "' label set" - } - } - } - - return -} - func GetWorkloadObject(ctx context.Context, objectKey client.ObjectKey, kind WorkloadKind, kubeClient client.Client) (metav1.Object, error) { switch kind { case WorkloadKindDeployment: From b1a2354b043691ed19440605be1e73c26efff909 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 16:17:48 +0200 Subject: [PATCH 074/259] sync with main (#2094) --- README.md | 92 ++++++++++--------- agents/python/setup.py | 2 +- common/config/betterstack.go | 54 +++++++++++ common/config/dash0.go | 68 ++++++++++++++ common/config/groundcover.go | 71 ++++++++++++++ common/config/hyperdx.go | 48 ++++++++++ common/config/kloudmate.go | 48 ++++++++++ common/config/lumigo.go | 69 ++++++++++++++ common/config/root.go | 53 +++++++++-- common/dests.go | 6 ++ destinations/data/betterstack.yaml | 23 +++++ destinations/data/dash0.yaml | 29 ++++++ destinations/data/groundcover.yaml | 30 ++++++ destinations/data/hyperdx.yaml | 23 +++++ destinations/data/kloudmate.yaml | 23 +++++ destinations/data/lumigo.yaml | 30 ++++++ destinations/logos/betterstack.svg | 8 ++ destinations/logos/dash0.svg | 8 ++ destinations/logos/groundcover.svg | 6 ++ destinations/logos/hyperdx.svg | 7 ++ destinations/logos/kloudmate.svg | 8 ++ destinations/logos/lumigo.svg | 24 +++++ docs/backends-overview.mdx | 8 +- docs/backends/betterstack.mdx | 60 ++++++++++++ docs/backends/dash0.mdx | 61 ++++++++++++ docs/backends/groundcover.mdx | 60 ++++++++++++ docs/backends/hyperdx.mdx | 60 ++++++++++++ docs/backends/kloudmate.mdx | 60 ++++++++++++ docs/backends/lumigo.mdx | 58 ++++++++++++ docs/instrumentations/python/python.mdx | 4 +- docs/mint.json | 69 ++++---------- docs/quickstart/next-steps.mdx | 6 ++ .../choose-sources-body-fast/index.tsx | 2 +- .../sources-list/index.tsx | 3 +- .../choose-sources-body-simple/index.tsx | 2 +- .../condition-details/index.tsx | 2 +- frontend/webapp/styles/styled.tsx | 6 +- .../instrumentation/instrumentlang/python.go | 3 + .../01-assert-apps-installed.yaml | 19 ++++ .../01-assert-instrumented.yaml | 15 ++- .../01-assert-runtime-detected.yaml | 19 ++++ .../01-install-test-apps.yaml | 48 ++++++++++ .../e2e/workload-lifecycle/chainsaw-test.yaml | 2 + .../Dockerfile.python-other-agent | 22 +++++ 44 files changed, 1204 insertions(+), 115 deletions(-) create mode 100644 common/config/betterstack.go create mode 100644 common/config/dash0.go create mode 100644 common/config/groundcover.go create mode 100644 common/config/hyperdx.go create mode 100644 common/config/kloudmate.go create mode 100644 common/config/lumigo.go create mode 100644 destinations/data/betterstack.yaml create mode 100644 destinations/data/dash0.yaml create mode 100644 destinations/data/groundcover.yaml create mode 100644 destinations/data/hyperdx.yaml create mode 100644 destinations/data/kloudmate.yaml create mode 100644 destinations/data/lumigo.yaml create mode 100644 destinations/logos/betterstack.svg create mode 100644 destinations/logos/dash0.svg create mode 100644 destinations/logos/groundcover.svg create mode 100644 destinations/logos/hyperdx.svg create mode 100644 destinations/logos/kloudmate.svg create mode 100644 destinations/logos/lumigo.svg create mode 100644 docs/backends/betterstack.mdx create mode 100644 docs/backends/dash0.mdx create mode 100644 docs/backends/groundcover.mdx create mode 100644 docs/backends/hyperdx.mdx create mode 100644 docs/backends/kloudmate.mdx create mode 100644 docs/backends/lumigo.mdx create mode 100644 tests/e2e/workload-lifecycle/services/python-http-server/Dockerfile.python-other-agent diff --git a/README.md b/README.md index acb0d1509..9ce38b1f9 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ Odigos is an open-source distributed tracing solution that simplifyes and improv ## Key Features -* **Code-Free Instrumentation** : Set up distributed tracing in minutes, eliminating manual code modifications. -* **Multi-Language Support** : Works with Java, Python, .NET, Node.js, and Go applications. -* **eBPF-Powered** : Utilizes eBPF technology for high-performance instrumentation of Go applications. eBPF-based instrumentation for Java, Python, and Node.js is available in the enterprise edition. -* **OpenTelemetry Compatible** : Generates traces in OpenTelemetry format for broad tool compatibility. -* **Vendor Agnostic** : Integrates with various monitoring solutions, avoiding vendor lock-in. -* **Automatic Scaling** : Manages and scales OpenTelemetry collectors based on data volume. -* **Opinionated Defaults** : Supplies common defaults and best practices out-of-the-box, requiring no deep knowledge of OpenTelemetry. +- **Code-Free Instrumentation** : Set up distributed tracing in minutes, eliminating manual code modifications. +- **Multi-Language Support** : Works with Java, Python, .NET, Node.js, and Go applications. +- **eBPF-Powered** : Utilizes eBPF technology for high-performance instrumentation of Go applications. eBPF-based instrumentation for Java, Python, and Node.js is available in the enterprise edition. +- **OpenTelemetry Compatible** : Generates traces in OpenTelemetry format for broad tool compatibility. +- **Vendor Agnostic** : Integrates with various monitoring solutions, avoiding vendor lock-in. +- **Automatic Scaling** : Manages and scales OpenTelemetry collectors based on data volume. +- **Opinionated Defaults** : Supplies common defaults and best practices out-of-the-box, requiring no deep knowledge of OpenTelemetry. ## Why Choose Odigos @@ -85,48 +85,52 @@ For more details, see our [quickstart guide](https://docs.odigos.io/intro). ### Managed Destinations - | Destination | Traces | Metrics | Logs | -| ------------------------- | :------: | :-------: | :----: | -| AppDynamics | ✅ | ✅ | ✅ | -| Axiom | ✅ | | ✅ | -| AWS S3 | ✅ | | ✅ | -| Azure Blob Storage | ✅ | | ✅ | -| Causely | ✅ | | | -| Chronosphere | ✅ | ✅ | | -| Coralogix | ✅ | ✅ | ✅ | -| Datadog | ✅ | ✅ | ✅ | -| Dynatrace | ✅ | ✅ | ✅ | -| Gigapipe | ✅ | | | -| Google Cloud Monitoring | ✅ | ✅ | | -| Google Cloud Storage | ✅ | | ✅ | -| Grafana Cloud | ✅ | ✅ | ✅ | -| Honeycomb | ✅ | ✅ | ✅ | -| Last9 | ✅ | ✅ | | -| Lightstep | ✅ | | | -| Logz.io | ✅ | ✅ | ✅ | -| New Relic | ✅ | ✅ | ✅ | -| OpsVerse | ✅ | ✅ | ✅ | -| Sentry | ✅ | | | -| Splunk | ✅ | | | -| Sumo Logic | ✅ | ✅ | ✅ | +| ----------------------- | :----: | :-----: | :--: | +| AppDynamics | ✅ | ✅ | ✅ | +| Axiom | ✅ | | ✅ | +| AWS S3 | ✅ | | ✅ | +| Azure Blob Storage | ✅ | | ✅ | +| Better Stack | | ✅ | ✅ | +| Causely | ✅ | | | +| Chronosphere | ✅ | ✅ | | +| Coralogix | ✅ | ✅ | ✅ | +| Dash0 | ✅ | ✅ | ✅ | +| Datadog | ✅ | ✅ | ✅ | +| Dynatrace | ✅ | ✅ | ✅ | +| Gigapipe | ✅ | | | +| Google Cloud Monitoring | ✅ | ✅ | | +| Google Cloud Storage | ✅ | | ✅ | +| Grafana Cloud | ✅ | ✅ | ✅ | +| Groundcover inCloud | ✅ | ✅ | ✅ | +| Honeycomb | ✅ | ✅ | ✅ | +| HyperDX | ✅ | ✅ | ✅ | +| KloudMate | ✅ | ✅ | ✅ | +| Last9 | ✅ | ✅ | | +| Lightstep | ✅ | | | +| Logz.io | ✅ | ✅ | ✅ | +| Lumigo | ✅ | ✅ | ✅ | +| New Relic | ✅ | ✅ | ✅ | +| OpsVerse | ✅ | ✅ | ✅ | +| Sentry | ✅ | | | +| Splunk | ✅ | | | +| Sumo Logic | ✅ | ✅ | ✅ | ## Self-Hosted (Open Source) Destinations - | Destination | Traces | Metrics | Logs | -| --------------- | :------: | :-------: | :----: | -| ClickHouse | ✅ | ✅ | ✅ | -| Elasticsearch | ✅ | | ✅ | -| Jaeger | ✅ | | | -| Loki | | | ✅ | -| OTLP | ✅ | ✅ | ✅ | -| OTLP HTTP | ✅ | ✅ | ✅ | -| Prometheus | | ✅ | | -| Quickwit | ✅ | | | -| qryn | ✅ | ✅ | ✅ | -| SigNoz | ✅ | ✅ | ✅ | -| Tempo | ✅ | | | +| ------------- | :----: | :-----: | :--: | +| ClickHouse | ✅ | ✅ | ✅ | +| Elasticsearch | ✅ | | ✅ | +| Jaeger | ✅ | | | +| Loki | | | ✅ | +| OTLP | ✅ | ✅ | ✅ | +| OTLP HTTP | ✅ | ✅ | ✅ | +| Prometheus | | ✅ | | +| Quickwit | ✅ | | | +| qryn | ✅ | ✅ | ✅ | +| SigNoz | ✅ | ✅ | ✅ | +| Tempo | ✅ | | | Can't find the destination you need? Help us by following our quick [add new destination](https://docs.odigos.io/adding-new-dest) guide and submitting a PR. diff --git a/agents/python/setup.py b/agents/python/setup.py index d11618359..7054ee2be 100644 --- a/agents/python/setup.py +++ b/agents/python/setup.py @@ -6,7 +6,7 @@ # index_url = 'http://host.docker.internal:8080/packages/odigos_opentelemetry_python-0.1.1-py3-none-any.whl' install_requires = [ - f"odigos-opentelemetry-python @ {index_url}" if index_url else "odigos-opentelemetry-python" + f"odigos-opentelemetry-python @ {index_url}" if index_url else "odigos-opentelemetry-python==1.0.22" ] setup( diff --git a/common/config/betterstack.go b/common/config/betterstack.go new file mode 100644 index 000000000..a4e21584d --- /dev/null +++ b/common/config/betterstack.go @@ -0,0 +1,54 @@ +package config + +import ( + "github.com/odigos-io/odigos/common" +) + +type BetterStack struct{} + +func (j *BetterStack) DestType() common.DestinationType { + return common.BetterStackDestinationType +} + +func (j *BetterStack) ModifyConfig(dest ExporterConfigurer, cfg *Config) error { + uniqueUri := "betterstack-" + dest.GetID() + + processorName := "attributes/" + uniqueUri + cfg.Processors[processorName] = GenericMap{ + "actions": []GenericMap{ + { + "key": "better_stack_source_token", + "value": "${BETTERSTACK_TOKEN}", + "action": "insert", + }, + }, + } + + if isMetricsEnabled(dest) { + exporterName := "prometheusremotewrite/" + uniqueUri + cfg.Exporters[exporterName] = GenericMap{ + "endpoint": "https://in-otel.logs.betterstack.com/metrics", + } + + pipeName := "metrics/" + uniqueUri + cfg.Service.Pipelines[pipeName] = Pipeline{ + Processors: []string{processorName}, + Exporters: []string{exporterName}, + } + } + + if isLoggingEnabled(dest) { + exporterName := "otlp/" + uniqueUri + cfg.Exporters[exporterName] = GenericMap{ + "endpoint": "https://in-otel.logs.betterstack.com:443", + } + + pipeName := "logs/" + uniqueUri + cfg.Service.Pipelines[pipeName] = Pipeline{ + Processors: []string{processorName}, + Exporters: []string{exporterName}, + } + } + + return nil +} diff --git a/common/config/dash0.go b/common/config/dash0.go new file mode 100644 index 000000000..2fb169389 --- /dev/null +++ b/common/config/dash0.go @@ -0,0 +1,68 @@ +package config + +import ( + "errors" + + "github.com/odigos-io/odigos/common" +) + +const ( + Dash0Endpoint = "DASH0_ENDPOINT" +) + +var ( + ErrorDash0EndpointMissing = errors.New("Dash0 is missing a required field (\"DASH0_ENDPOINT\"), Dash0 will not be configured") +) + +type Dash0 struct{} + +func (j *Dash0) DestType() common.DestinationType { + return common.Dash0DestinationType +} + +func (j *Dash0) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error { + config := dest.GetConfig() + uniqueUri := "dash0-" + dest.GetID() + + url, exists := config[Dash0Endpoint] + if !exists { + return ErrorDash0EndpointMissing + } + endpoint, err := parseOtlpGrpcUrl(url, true) + if err != nil { + return err + } + + exporterName := "otlp/" + uniqueUri + exporterConfig := GenericMap{ + "endpoint": endpoint, + "headers": GenericMap{ + "Authorization": "Bearer ${DASH0_TOKEN}", + }, + } + + currentConfig.Exporters[exporterName] = exporterConfig + + if isTracingEnabled(dest) { + pipeName := "traces/" + uniqueUri + currentConfig.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isMetricsEnabled(dest) { + pipeName := "metrics/" + uniqueUri + currentConfig.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isLoggingEnabled(dest) { + pipeName := "logs/" + uniqueUri + currentConfig.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + return nil +} diff --git a/common/config/groundcover.go b/common/config/groundcover.go new file mode 100644 index 000000000..24941fbca --- /dev/null +++ b/common/config/groundcover.go @@ -0,0 +1,71 @@ +package config + +import ( + "errors" + + "github.com/odigos-io/odigos/common" +) + +const ( + GroundcoverEndpoint = "GROUNDCOVER_ENDPOINT" + GroundcoverApiKey = "GROUNDCOVER_API_KEY" +) + +var ( + ErrorGroundcoverEndpointMissing = errors.New("Groundcover is missing a required field (\"GROUNDCOVER_ENDPOINT\"), Groundcover will not be configured") + ErrorGroundcoverApiKeyMissing = errors.New("Groundcover is missing a required field (\"GROUNDCOVER_API_KEY\"), Groundcover will not be configured") +) + +type Groundcover struct{} + +func (j *Groundcover) DestType() common.DestinationType { + return common.GroundcoverDestinationType +} + +func (j *Groundcover) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error { + config := dest.GetConfig() + uniqueUri := "groundcover-" + dest.GetID() + + url, exists := config[GroundcoverEndpoint] + if !exists { + return ErrorGroundcoverEndpointMissing + } + + endpoint, err := parseOtlpGrpcUrl(url, true) + if err != nil { + return err + } + + exporterName := "otlp/" + uniqueUri + exporterConfig := GenericMap{ + "endpoint": endpoint, + "headers": GenericMap{ + "apikey": "${GROUNDCOVER_API_KEY}", + }, + } + + currentConfig.Exporters[exporterName] = exporterConfig + + if isTracingEnabled(dest) { + tracesPipelineName := "traces/" + uniqueUri + currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isMetricsEnabled(dest) { + tracesPipelineName := "metrics/" + uniqueUri + currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isLoggingEnabled(dest) { + tracesPipelineName := "logs/" + uniqueUri + currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + return nil +} diff --git a/common/config/hyperdx.go b/common/config/hyperdx.go new file mode 100644 index 000000000..8068956ea --- /dev/null +++ b/common/config/hyperdx.go @@ -0,0 +1,48 @@ +package config + +import ( + "github.com/odigos-io/odigos/common" +) + +type HyperDX struct{} + +func (j *HyperDX) DestType() common.DestinationType { + return common.HyperDxDestinationType +} + +func (j *HyperDX) ModifyConfig(dest ExporterConfigurer, cfg *Config) error { + uniqueUri := "hdx-" + dest.GetID() + + exporterName := "otlp/" + uniqueUri + exporterConfig := GenericMap{ + "endpoint": "in-otel.hyperdx.io:4317", + "headers": GenericMap{ + "authorization": "${HYPERDX_API_KEY}", + }, + } + + cfg.Exporters[exporterName] = exporterConfig + + if isTracingEnabled(dest) { + pipeName := "traces/" + uniqueUri + cfg.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isMetricsEnabled(dest) { + pipeName := "metrics/" + uniqueUri + cfg.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isLoggingEnabled(dest) { + pipeName := "logs/" + uniqueUri + cfg.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + return nil +} diff --git a/common/config/kloudmate.go b/common/config/kloudmate.go new file mode 100644 index 000000000..815c634a2 --- /dev/null +++ b/common/config/kloudmate.go @@ -0,0 +1,48 @@ +package config + +import ( + "github.com/odigos-io/odigos/common" +) + +type KloudMate struct{} + +func (j *KloudMate) DestType() common.DestinationType { + return common.KloudMateDestinationType +} + +func (j *KloudMate) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error { + uniqueUri := "kloudmate-" + dest.GetID() + + exporterName := "otlphttp/" + uniqueUri + exporterConfig := GenericMap{ + "endpoint": "https://otel.kloudmate.com:4318", + "headers": GenericMap{ + "Authorization": "${KLOUDMATE_API_KEY}", + }, + } + + currentConfig.Exporters[exporterName] = exporterConfig + + if isTracingEnabled(dest) { + pipeName := "traces/" + uniqueUri + currentConfig.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isMetricsEnabled(dest) { + pipeName := "metrics/" + uniqueUri + currentConfig.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isLoggingEnabled(dest) { + pipeName := "logs/" + uniqueUri + currentConfig.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + return nil +} diff --git a/common/config/lumigo.go b/common/config/lumigo.go new file mode 100644 index 000000000..1dcdeba5e --- /dev/null +++ b/common/config/lumigo.go @@ -0,0 +1,69 @@ +package config + +import ( + "errors" + + "github.com/odigos-io/odigos/common" +) + +const ( + LumigoEndpoint = "LUMIGO_ENDPOINT" +) + +var ( + ErrorLumigoEndpointMissing = errors.New("Lumigo is missing a required field (\"LUMIGO_ENDPOINT\"), Lumigo will not be configured") +) + +type Lumigo struct{} + +func (j *Lumigo) DestType() common.DestinationType { + return common.LumigoDestinationType +} + +func (j *Lumigo) ModifyConfig(dest ExporterConfigurer, cfg *Config) error { + config := dest.GetConfig() + uniqueUri := "lumigo-" + dest.GetID() + + url, exists := config[LumigoEndpoint] + if !exists { + return ErrorLumigoEndpointMissing + } + + endpoint, err := parseOtlpHttpEndpoint(url) + if err != nil { + return err + } + + exporterName := "otlphttp/" + uniqueUri + exporterConfig := GenericMap{ + "endpoint": endpoint, + "headers": GenericMap{ + "Authorization": "LumigoToken ${LUMIGO_TOKEN}", + }, + } + + cfg.Exporters[exporterName] = exporterConfig + + if isTracingEnabled(dest) { + pipeName := "traces/" + uniqueUri + cfg.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isMetricsEnabled(dest) { + pipeName := "metrics/" + uniqueUri + cfg.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isLoggingEnabled(dest) { + pipeName := "logs/" + uniqueUri + cfg.Service.Pipelines[pipeName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + return nil +} diff --git a/common/config/root.go b/common/config/root.go index 8893aebb7..fbbebb5bf 100644 --- a/common/config/root.go +++ b/common/config/root.go @@ -15,12 +15,53 @@ const ( ) var availableConfigers = []Configer{ - &Middleware{}, &AppDynamics{}, &Honeycomb{}, &GrafanaCloudPrometheus{}, &GrafanaCloudTempo{}, - &GrafanaCloudLoki{}, &Datadog{}, &NewRelic{}, &Logzio{}, &Last9{}, &Prometheus{}, - &Tempo{}, &Loki{}, &Jaeger{}, &GenericOTLP{}, &OTLPHttp{}, &Elasticsearch{}, &Quickwit{}, &Signoz{}, &Qryn{}, - &OpsVerse{}, &Splunk{}, &Lightstep{}, &GoogleCloud{}, &GoogleCloudStorage{}, &Sentry{}, &AzureBlobStorage{}, - &AWSS3{}, &Dynatrace{}, &Chronosphere{}, &ElasticAPM{}, &Axiom{}, &SumoLogic{}, &Coralogix{}, &Clickhouse{}, - &Causely{}, &Uptrace{}, &Debug{}, &QrynOSS{}, &Nop{}, &Mock{}, + &AppDynamics{}, + &Axiom{}, + &AWSS3{}, + &AzureBlobStorage{}, + &BetterStack{}, + &Causely{}, + &Chronosphere{}, + &Clickhouse{}, + &Coralogix{}, + &Dash0{}, + &Datadog{}, + &Debug{}, + &Dynatrace{}, + &ElasticAPM{}, + &Elasticsearch{}, + &GenericOTLP{}, + &GoogleCloud{}, + &GoogleCloudStorage{}, + &GrafanaCloudLoki{}, + &GrafanaCloudPrometheus{}, + &GrafanaCloudTempo{}, + &Groundcover{}, + &Honeycomb{}, + &HyperDX{}, + &Jaeger{}, + &KloudMate{}, + &Last9{}, + &Lightstep{}, + &Logzio{}, + &Loki{}, + &Lumigo{}, + &Middleware{}, + &Mock{}, + &NewRelic{}, + &Nop{}, + &OpsVerse{}, + &OTLPHttp{}, + &Prometheus{}, + &Qryn{}, + &QrynOSS{}, + &Quickwit{}, + &Sentry{}, + &Signoz{}, + &Splunk{}, + &SumoLogic{}, + &Tempo{}, + &Uptrace{}, } type Configer interface { diff --git a/common/dests.go b/common/dests.go index e0aaef5e6..5c2002029 100644 --- a/common/dests.go +++ b/common/dests.go @@ -7,10 +7,12 @@ const ( AWSS3DestinationType DestinationType = "s3" AxiomDestinationType DestinationType = "axiom" AzureBlobDestinationType DestinationType = "azureblob" + BetterStackDestinationType DestinationType = "betterstack" CauselyDestinationType DestinationType = "causely" ChronosphereDestinationType DestinationType = "chronosphere" ClickhouseDestinationType DestinationType = "clickhouse" CoralogixDestinationType DestinationType = "coralogix" + Dash0DestinationType DestinationType = "dash0" DatadogDestinationType DestinationType = "datadog" DebugDestinationType DestinationType = "debug" DynatraceDestinationType DestinationType = "dynatrace" @@ -22,12 +24,16 @@ const ( GrafanaCloudLokiDestinationType DestinationType = "grafanacloudloki" GrafanaCloudPrometheusDestinationType DestinationType = "grafanacloudprometheus" GrafanaCloudTempoDestinationType DestinationType = "grafanacloudtempo" + GroundcoverDestinationType DestinationType = "groundcover" HoneycombDestinationType DestinationType = "honeycomb" + HyperDxDestinationType DestinationType = "hyperdx" JaegerDestinationType DestinationType = "jaeger" + KloudMateDestinationType DestinationType = "kloudmate" Last9DestinationType DestinationType = "last9" LightstepDestinationType DestinationType = "lightstep" LogzioDestinationType DestinationType = "logzio" LokiDestinationType DestinationType = "loki" + LumigoDestinationType DestinationType = "lumigo" MiddlewareDestinationType DestinationType = "middleware" MockDestinationType DestinationType = "mock" NewRelicDestinationType DestinationType = "newrelic" diff --git a/destinations/data/betterstack.yaml b/destinations/data/betterstack.yaml new file mode 100644 index 000000000..efc535021 --- /dev/null +++ b/destinations/data/betterstack.yaml @@ -0,0 +1,23 @@ +apiVersion: internal.odigos.io/v1beta1 +kind: Destination +metadata: + type: betterstack + displayName: Better Stack + category: managed +spec: + image: betterstack.svg + signals: + traces: + supported: false + metrics: + supported: true + logs: + supported: true + fields: + - name: BETTERSTACK_TOKEN + displayName: Better Stack Source Token + componentType: input + secret: true + componentProps: + type: password + required: true diff --git a/destinations/data/dash0.yaml b/destinations/data/dash0.yaml new file mode 100644 index 000000000..0bb87a8b8 --- /dev/null +++ b/destinations/data/dash0.yaml @@ -0,0 +1,29 @@ +apiVersion: internal.odigos.io/v1beta1 +kind: Destination +metadata: + type: dash0 + displayName: Dash0 + category: managed +spec: + image: dash0.svg + signals: + traces: + supported: true + metrics: + supported: true + logs: + supported: true + fields: + - name: DASH0_ENDPOINT + displayName: Dash0 OTLP gRPC Endpoint + componentType: input + componentProps: + type: text + required: true + - name: DASH0_TOKEN + displayName: Dash0 Bearer Token + componentType: input + secret: true + componentProps: + type: password + required: true diff --git a/destinations/data/groundcover.yaml b/destinations/data/groundcover.yaml new file mode 100644 index 000000000..8a0261e1b --- /dev/null +++ b/destinations/data/groundcover.yaml @@ -0,0 +1,30 @@ +apiVersion: internal.odigos.io/v1beta1 +kind: Destination +metadata: + type: groundcover + displayName: Groundcover inCloud + category: managed +spec: + image: groundcover.svg + signals: + traces: + supported: true + metrics: + supported: true + logs: + supported: true + fields: + - name: GROUNDCOVER_ENDPOINT + displayName: Groundcover inCloud Site + componentType: input + componentProps: + type: text + required: true + tooltip: 'Your inCloud Site is part of the configuration provided to you by groundcover when setting up the managed inCloud backend.' + - name: GROUNDCOVER_API_KEY + displayName: Groundcover API Key + componentType: input + secret: true + componentProps: + type: password + required: true diff --git a/destinations/data/hyperdx.yaml b/destinations/data/hyperdx.yaml new file mode 100644 index 000000000..f11c577ed --- /dev/null +++ b/destinations/data/hyperdx.yaml @@ -0,0 +1,23 @@ +apiVersion: internal.odigos.io/v1beta1 +kind: Destination +metadata: + type: hyperdx + displayName: HyperDX + category: managed +spec: + image: hyperdx.svg + signals: + traces: + supported: true + metrics: + supported: true + logs: + supported: true + fields: + - name: HYPERDX_API_KEY + displayName: HyperDX API Key + componentType: input + secret: true + componentProps: + type: password + required: true diff --git a/destinations/data/kloudmate.yaml b/destinations/data/kloudmate.yaml new file mode 100644 index 000000000..d3ef6587a --- /dev/null +++ b/destinations/data/kloudmate.yaml @@ -0,0 +1,23 @@ +apiVersion: internal.odigos.io/v1beta1 +kind: Destination +metadata: + type: kloudmate + displayName: KloudMate + category: managed +spec: + image: kloudmate.svg + signals: + traces: + supported: true + metrics: + supported: true + logs: + supported: true + fields: + - name: KLOUDMATE_API_KEY + displayName: KloudMate API Key + componentType: input + secret: true + componentProps: + type: password + required: true diff --git a/destinations/data/lumigo.yaml b/destinations/data/lumigo.yaml new file mode 100644 index 000000000..cfca18012 --- /dev/null +++ b/destinations/data/lumigo.yaml @@ -0,0 +1,30 @@ +apiVersion: internal.odigos.io/v1beta1 +kind: Destination +metadata: + type: lumigo + displayName: Lumigo + category: managed +spec: + image: lumigo.svg + signals: + traces: + supported: true + metrics: + supported: true + logs: + supported: true + fields: + - name: LUMIGO_ENDPOINT + displayName: Lumigo OTLP HTTP Endpoint + componentType: input + initialValue: 'https://ga-otlp.lumigo-tracer-edge.golumigo.com' + componentProps: + type: text + required: true + - name: LUMIGO_TOKEN + displayName: Lumigo Token + componentType: input + secret: true + componentProps: + type: password + required: true diff --git a/destinations/logos/betterstack.svg b/destinations/logos/betterstack.svg new file mode 100644 index 000000000..ac246d230 --- /dev/null +++ b/destinations/logos/betterstack.svg @@ -0,0 +1,8 @@ + + BetterStack + + + + + + \ No newline at end of file diff --git a/destinations/logos/dash0.svg b/destinations/logos/dash0.svg new file mode 100644 index 000000000..1c5d1bbe8 --- /dev/null +++ b/destinations/logos/dash0.svg @@ -0,0 +1,8 @@ + + + Dash0 + + + + + diff --git a/destinations/logos/groundcover.svg b/destinations/logos/groundcover.svg new file mode 100644 index 000000000..9790b8072 --- /dev/null +++ b/destinations/logos/groundcover.svg @@ -0,0 +1,6 @@ + + Groundcover + + + + \ No newline at end of file diff --git a/destinations/logos/hyperdx.svg b/destinations/logos/hyperdx.svg new file mode 100644 index 000000000..1f81e3f20 --- /dev/null +++ b/destinations/logos/hyperdx.svg @@ -0,0 +1,7 @@ + + HyperDX + + + + + \ No newline at end of file diff --git a/destinations/logos/kloudmate.svg b/destinations/logos/kloudmate.svg new file mode 100644 index 000000000..44a3d0306 --- /dev/null +++ b/destinations/logos/kloudmate.svg @@ -0,0 +1,8 @@ + + KloudMate + + + + + + \ No newline at end of file diff --git a/destinations/logos/lumigo.svg b/destinations/logos/lumigo.svg new file mode 100644 index 000000000..c486b5ae6 --- /dev/null +++ b/destinations/logos/lumigo.svg @@ -0,0 +1,24 @@ + + Lumigo + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/backends-overview.mdx b/docs/backends-overview.mdx index 903dba68b..e1d7bfcff 100644 --- a/docs/backends-overview.mdx +++ b/docs/backends-overview.mdx @@ -10,10 +10,12 @@ Odigos has destinations for many observability backends. | Axiom | Managed | ✅ | | ✅ | | AWS S3 | Managed | ✅ | | ✅ | | Azure Blob Storage | Managed | ✅ | | ✅ | +| Better Stack | Managed | | ✅ | ✅ | | Causely | Managed | ✅ | | | | Chronosphere | Managed | ✅ | ✅ | | | ClickHouse | Self-Hosted | ✅ | ✅ | ✅ | | Coralogix | Managed | ✅ | ✅ | ✅ | +| Dash0 | Managed | ✅ | ✅ | ✅ | | Datadog | Managed | ✅ | ✅ | ✅ | | Dynatrace | Managed | ✅ | ✅ | ✅ | | Elasticsearch | Self-Hosted | ✅ | | ✅ | @@ -21,12 +23,16 @@ Odigos has destinations for many observability backends. | Google Cloud Monitoring | Managed | ✅ | ✅ | | | Google Cloud Storage | Managed | ✅ | | ✅ | | Grafana Cloud | Managed | ✅ | ✅ | ✅ | +| Groundcover inCloud | Managed | ✅ | ✅ | ✅ | | Honeycomb | Managed | ✅ | ✅ | ✅ | +| HyperDX | Managed | ✅ | ✅ | ✅ | | Jaeger | Self-Hosted | ✅ | | | +| KloudMate | Managed | ✅ | ✅ | ✅ | | Last9 | Managed | ✅ | ✅ | | | Lightstep | Managed | ✅ | | | -| Loki | Self-Hosted | | | ✅ | | Logz.io | Managed | ✅ | ✅ | ✅ | +| Loki | Self-Hosted | | | ✅ | +| Lumigo | Managed | ✅ | ✅ | ✅ | | New Relic | Managed | ✅ | ✅ | ✅ | | OpsVerse | Managed | ✅ | ✅ | ✅ | | OTLP | Managed | ✅ | ✅ | ✅ | diff --git a/docs/backends/betterstack.mdx b/docs/backends/betterstack.mdx new file mode 100644 index 000000000..de94b1066 --- /dev/null +++ b/docs/backends/betterstack.mdx @@ -0,0 +1,60 @@ +--- +title: 'Better Stack' +--- + +## Configuring Backend + +- **BETTERSTACK_TOKEN** - Source Token, generated from Better Stack UI. + +**Note:** +We handle the endpoint internally, so you don't need to provide it. +- The endpoint for metrics is `https://in-otel.logs.betterstack.com/metrics`. +- The endpoint for logs is `https://in-otel.logs.betterstack.com:443`. + +## Adding a Destination to Odigos + +Odigos makes it simple to add and configure destinations, allowing you to select the specific signals [traces/logs/metrics] that you want to send to each destination. There are two primary methods for configuring destinations in Odigos: + +1. **Using the UI** + +Use the [Odigos CLI](https://docs.odigos.io/cli/odigos_ui) to access the UI: + +```bash +odigos ui +``` + +2. **Using kubernetes manifests** + +Save the YAML below to a file (e.g., `destination.yaml`) and apply it using `kubectl`: + +```bash +kubectl apply -f destination.yaml +``` + + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: Destination +metadata: + name: betterstack-example + namespace: odigos-system +spec: + data: {} + destinationName: betterstack + secretRef: + name: betterstack-secret + signals: + - METRICS + - LOGS + type: betterstack + +--- +apiVersion: v1 +data: + BETTERSTACK_TOKEN: +kind: Secret +metadata: + name: betterstack-secret + namespace: odigos-system +type: Opaque +``` \ No newline at end of file diff --git a/docs/backends/dash0.mdx b/docs/backends/dash0.mdx new file mode 100644 index 000000000..44d94344a --- /dev/null +++ b/docs/backends/dash0.mdx @@ -0,0 +1,61 @@ +--- +title: 'Dash0' +--- + +## Configuring Backend + +- **DASH0_ENDPOINT** - OpenTelemetry gRPC Endpoint, the format is `host:port`. + - host is required, located in Dash0 UI - OpenTelemetry Collector. + - port is optional and defaults to the default OTLP gRPC port `4317`. +- **DASH0_TOKEN** - Dash0 Authorization Token, located in Dash0 UI - OpenTelemetry Collector. + + +## Adding a Destination to Odigos + +Odigos makes it simple to add and configure destinations, allowing you to select the specific signals [traces/logs/metrics] that you want to send to each destination. There are two primary methods for configuring destinations in Odigos: + +1. **Using the UI** + +Use the [Odigos CLI](https://docs.odigos.io/cli/odigos_ui) to access the UI: + +```bash +odigos ui +``` + +2. **Using kubernetes manifests** + +Save the YAML below to a file (e.g., `destination.yaml`) and apply it using `kubectl`: + +```bash +kubectl apply -f destination.yaml +``` + + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: Destination +metadata: + name: dash0-example + namespace: odigos-system +spec: + data: + DASH0_ENDPOINT: + destinationName: dash0 + secretRef: + name: dash0-secret + signals: + - TRACES + - METRICS + - LOGS + type: dash0 + +--- +apiVersion: v1 +data: + DASH0_TOKEN: +kind: Secret +metadata: + name: dash0-secret + namespace: odigos-system +type: Opaque +``` \ No newline at end of file diff --git a/docs/backends/groundcover.mdx b/docs/backends/groundcover.mdx new file mode 100644 index 000000000..795b83d8c --- /dev/null +++ b/docs/backends/groundcover.mdx @@ -0,0 +1,60 @@ +--- +title: 'Groundcover inCloud' +--- + +## Configuring Backend + +- **GROUNDCOVER_ENDPOINT** - Endpoint, the format is `host:port`. + - `host` is required, also known as your `inCloud_Site`, it is part of the configuration provided to you by Groundcover when setting up the [inCloud Managed](https://docs.groundcover.com/architecture/incloud-managed) backend. + - `port` is optional, and defaults to the default OpenTelemetry gRPC port `4317`. +- **GROUNDCOVER_API_KEY** - API Key provided by Groundcover, refer to [these docs](https://docs.groundcover.com/architecture/incloud-managed/ingestion-endpoints#fetching-the-api-key) for more info. + +## Adding a Destination to Odigos + +Odigos makes it simple to add and configure destinations, allowing you to select the specific signals [traces/logs/metrics] that you want to send to each destination. There are two primary methods for configuring destinations in Odigos: + +1. **Using the UI** + +Use the [Odigos CLI](https://docs.odigos.io/cli/odigos_ui) to access the UI: + +```bash +odigos ui +``` + +2. **Using kubernetes manifests** + +Save the YAML below to a file (e.g., `destination.yaml`) and apply it using `kubectl`: + +```bash +kubectl apply -f destination.yaml +``` + + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: Destination +metadata: + name: groundcover-example + namespace: odigos-system +spec: + data: + GROUNDCOVER_ENDPOINT: + destinationName: groundcover + secretRef: + name: groundcover-secret + signals: + - TRACES + - METRICS + - LOGS + type: groundcover + +--- +apiVersion: v1 +data: + GROUNDCOVER_API_KEY: +kind: Secret +metadata: + name: groundcover-secret + namespace: odigos-system +type: Opaque +``` diff --git a/docs/backends/hyperdx.mdx b/docs/backends/hyperdx.mdx new file mode 100644 index 000000000..fdac90ab0 --- /dev/null +++ b/docs/backends/hyperdx.mdx @@ -0,0 +1,60 @@ +--- +title: 'HyperDX' +--- + +## Configuring Backend + +- **HYPERDX_API_KEY** - HyperDX API Key. + +**Note:** +We handle the endpoint internally, so you don't need to provide it. +- The endpoint is `in-otel.hyperdx.io:4317`. + +## Adding a Destination to Odigos + +Odigos makes it simple to add and configure destinations, allowing you to select the specific signals [traces/logs/metrics] that you want to send to each destination. There are two primary methods for configuring destinations in Odigos: + +1. **Using the UI** + +Use the [Odigos CLI](https://docs.odigos.io/cli/odigos_ui) to access the UI: + +```bash +odigos ui +``` + +2. **Using kubernetes manifests** + +Save the YAML below to a file (e.g., `destination.yaml`) and apply it using `kubectl`: + +```bash +kubectl apply -f destination.yaml +``` + + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: Destination +metadata: + name: hyperdx-example + namespace: odigos-system +spec: + data: {} + destinationName: hyperdx + secretRef: + name: hyperdx-secret + signals: + - TRACES + - METRICS + - LOGS + type: hyperdx + +--- +apiVersion: v1 +data: + HYPERDX_API_KEY: +kind: Secret +metadata: + name: hyperdx-secret + namespace: odigos-system +type: Opaque +``` diff --git a/docs/backends/kloudmate.mdx b/docs/backends/kloudmate.mdx new file mode 100644 index 000000000..929398d86 --- /dev/null +++ b/docs/backends/kloudmate.mdx @@ -0,0 +1,60 @@ +--- +title: 'KloudMate' +--- + +## Configuring Backend + +- **KLOUDMATE_API_KEY** - KloudMate API Key, required. + +**Note:** +We handle the endpoint internally, so you don't need to provide it. +- The endpoint is `https://otel.kloudmate.com:4318`. + +## Adding a Destination to Odigos + +Odigos makes it simple to add and configure destinations, allowing you to select the specific signals [traces/logs/metrics] that you want to send to each destination. There are two primary methods for configuring destinations in Odigos: + +1. **Using the UI** + +Use the [Odigos CLI](https://docs.odigos.io/cli/odigos_ui) to access the UI: + +```bash +odigos ui +``` + +2. **Using kubernetes manifests** + +Save the YAML below to a file (e.g., `destination.yaml`) and apply it using `kubectl`: + +```bash +kubectl apply -f destination.yaml +``` + + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: Destination +metadata: + name: kloudmate-example + namespace: odigos-system +spec: + data: {} + destinationName: kloudmate + secretRef: + name: kloudmate-secret + signals: + - TRACES + - METRICS + - LOGS + type: kloudmate + +--- +apiVersion: v1 +data: + KLOUDMATE_API_KEY: +kind: Secret +metadata: + name: kloudmate-secret + namespace: odigos-system +type: Opaque +``` \ No newline at end of file diff --git a/docs/backends/lumigo.mdx b/docs/backends/lumigo.mdx new file mode 100644 index 000000000..9c23eaa64 --- /dev/null +++ b/docs/backends/lumigo.mdx @@ -0,0 +1,58 @@ +--- +title: 'Lumigo' +--- + +## Configuring Backend + +- **LUMIGO_ENDPOINT** - OpenTelemetry HTTP Endpoint, defaults to `https://ga-otlp.lumigo-tracer-edge.golumigo.com`. +- **LUMIGO_TOKEN** - Lumigo Authorization Token. + +## Adding a Destination to Odigos + +Odigos makes it simple to add and configure destinations, allowing you to select the specific signals [traces/logs/metrics] that you want to send to each destination. There are two primary methods for configuring destinations in Odigos: + +1. **Using the UI** + +Use the [Odigos CLI](https://docs.odigos.io/cli/odigos_ui) to access the UI: + +```bash +odigos ui +``` + +2. **Using kubernetes manifests** + +Save the YAML below to a file (e.g., `destination.yaml`) and apply it using `kubectl`: + +```bash +kubectl apply -f destination.yaml +``` + + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: Destination +metadata: + name: lumigo-example + namespace: odigos-system +spec: + data: + LUMIGO_ENDPOINT: + destinationName: lumigo + secretRef: + name: lumigo-secret + signals: + - TRACES + - METRICS + - LOGS + type: lumigo + +--- +apiVersion: v1 +data: + LUMIGO_TOKEN: +kind: Secret +metadata: + name: lumigo-secret + namespace: odigos-system +type: Opaque +``` diff --git a/docs/instrumentations/python/python.mdx b/docs/instrumentations/python/python.mdx index 21b4eb6c3..a718463d0 100644 --- a/docs/instrumentations/python/python.mdx +++ b/docs/instrumentations/python/python.mdx @@ -82,8 +82,10 @@ The following Python modules will be auto instrumented by Odigos: ### Other - [`asyncio`](https://pypi.org/project/asyncio/) -### Loggers +### Gen AI +- [`openai`](https://pypi.org/project/openai/) +### Loggers Automatic injection of trace context (trace id and span id) into log records for the following loggers: - [`logging`](https://docs.python.org/3/library/logging.html) \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json index 4ff543be3..85e7cec89 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -70,11 +70,7 @@ }, { "group": "Architecture", - "pages": [ - "architecture/components", - "architecture/sources", - "architecture/troubleshooting" - ] + "pages": ["architecture/components", "architecture/sources", "architecture/troubleshooting"] }, { "group": "CLI Reference", @@ -99,38 +95,23 @@ "instrumentations/overview", { "group": "Go", - "pages": [ - "instrumentations/golang/golang", - "instrumentations/golang/enrichment" - ] + "pages": ["instrumentations/golang/golang", "instrumentations/golang/enrichment"] }, { "group": "Node.js", - "pages": [ - "instrumentations/nodejs/nodejs", - "instrumentations/nodejs/enrichment", - "instrumentations/nodejs/ebpf" - ] + "pages": ["instrumentations/nodejs/nodejs", "instrumentations/nodejs/enrichment", "instrumentations/nodejs/ebpf"] }, { "group": "Python", - "pages": [ - "instrumentations/python/python", - "instrumentations/python/enrichment" - ] + "pages": ["instrumentations/python/python", "instrumentations/python/enrichment"] }, { "group": "Java", - "pages": [ - "instrumentations/java/java", - "instrumentations/java/ebpf" - ] + "pages": ["instrumentations/java/java", "instrumentations/java/ebpf"] }, { "group": ".NET", - "pages": [ - "instrumentations/dotnet/dotnet" - ] + "pages": ["instrumentations/dotnet/dotnet"] } ] }, @@ -150,10 +131,7 @@ }, { "group": "Instrumentation Rules", - "pages": [ - "pipeline/rules/overview", - "pipeline/rules/payloadcollection" - ] + "pages": ["pipeline/rules/overview", "pipeline/rules/payloadcollection"] }, { "group": "Actions", @@ -189,14 +167,7 @@ "pages": [ { "group": "Enrich with OpenTelemetry APIs", - "pages": [ - "data/otel-apis", - "data/java", - "data/python", - "data/javascript", - "data/golang", - "data/dotnet" - ] + "pages": ["data/otel-apis", "data/java", "data/python", "data/javascript", "data/golang", "data/dotnet"] } ] }, @@ -211,10 +182,12 @@ "backends/s3", "backends/axiom", "backends/azureblob", + "backends/betterstack", "backends/causely", "backends/chronosphere", "backends/clickhouse", "backends/coralogix", + "backends/dash0", "backends/datadog", "backends/dynatrace", "backends/elasticsearch", @@ -224,12 +197,16 @@ "backends/grafanacloudloki", "backends/grafanacloudprometheus", "backends/grafanacloudtempo", + "backends/groundcover", "backends/honeycomb", + "backends/hyperdx", "backends/jaeger", + "backends/kloudmate", "backends/last9", "backends/lightstep", "backends/logzio", "backends/loki", + "backends/lumigo", "backends/newrelic", "backends/opsverse", "backends/otlp", @@ -248,13 +225,7 @@ }, { "group": "Concepts", - "pages": [ - "telemetry-types", - "custom-resources", - "uninstall", - "usage-reports", - "benchmarks" - ] + "pages": ["telemetry-types", "custom-resources", "uninstall", "usage-reports", "benchmarks"] }, { "group": "Contibution Guidelines", @@ -282,20 +253,14 @@ "vmagent/setup/installation", { "group": "Configuration", - "pages": [ - "vmagent/setup/configuration/service", - "vmagent/setup/configuration/agent" - ] + "pages": ["vmagent/setup/configuration/service", "vmagent/setup/configuration/agent"] }, "vmagent/setup/uninstall" ] }, { "group": "Configuration", - "pages": [ - "vmagent/configuration/reference", - "vmagent/configuration/updating" - ] + "pages": ["vmagent/configuration/reference", "vmagent/configuration/updating"] } ] } diff --git a/docs/quickstart/next-steps.mdx b/docs/quickstart/next-steps.mdx index 5e5a5c761..bf39ded81 100644 --- a/docs/quickstart/next-steps.mdx +++ b/docs/quickstart/next-steps.mdx @@ -21,9 +21,11 @@ Select the relevant backend for your use case below to connect it to Odigos. + + @@ -33,12 +35,16 @@ Select the relevant backend for your use case below to connect it to Odigos. + + + + diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/index.tsx index 8788f78ba..25ab0c23c 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/index.tsx @@ -10,7 +10,7 @@ interface Props extends UseSourceFormDataResponse { export const ChooseSourcesBodyFast: React.FC = (props) => { return ( - + diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx index 4eefe39df..97231f57f 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx @@ -14,9 +14,8 @@ const List = styled.div<{ $isModal: Props['isModal'] }>` flex-direction: column; align-items: center; gap: 12px; - max-height: ${({ $isModal }) => ($isModal ? 'calc(100vh - 548px)' : 'calc(100vh - 360px)')}; + max-height: ${({ $isModal }) => ($isModal ? 'calc(100vh - 510px)' : 'calc(100vh - 310px)')}; height: fit-content; - padding-bottom: ${({ $isModal }) => ($isModal ? '48px' : '0')}; overflow-y: scroll; `; diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/index.tsx index 7ffedb225..3f1e6e27d 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/index.tsx @@ -10,7 +10,7 @@ interface Props extends UseSourceFormDataResponse { export const ChooseSourcesBodySimple: React.FC = (props) => { return ( - + diff --git a/frontend/webapp/reuseable-components/condition-details/index.tsx b/frontend/webapp/reuseable-components/condition-details/index.tsx index 7f7a06de2..42ac4a9bf 100644 --- a/frontend/webapp/reuseable-components/condition-details/index.tsx +++ b/frontend/webapp/reuseable-components/condition-details/index.tsx @@ -39,7 +39,7 @@ const Row = styled.div` gap: 12px; `; -export const ConditionDetails: React.FC = ({ conditions }) => { +export const ConditionDetails: React.FC = ({ conditions = [] }) => { const [extend, setExtend] = useState(false); const loading = useMemo(() => !conditions.length, [conditions]); diff --git a/frontend/webapp/styles/styled.tsx b/frontend/webapp/styles/styled.tsx index 88e5eb37a..5a0c81c4d 100644 --- a/frontend/webapp/styles/styled.tsx +++ b/frontend/webapp/styles/styled.tsx @@ -21,10 +21,10 @@ export const Overlay = styled.div` // this is to control modal size + scroll // note: add-destinations does not use this (yet), because it has a custom sidebar -export const ModalBody = styled.div` +export const ModalBody = styled.div<{ $isNotModal?: boolean }>` width: 640px; - height: calc(100vh - 350px); - margin: 64px 7vw 32px 7vw; + height: ${({ $isNotModal }) => ($isNotModal ? 'fit-content' : 'calc(100vh - 350px)')}; + margin: ${({ $isNotModal }) => ($isNotModal ? '64px 0 0 0' : '64px 7vw 32px 7vw')}; overflow-y: scroll; `; diff --git a/odiglet/pkg/instrumentation/instrumentlang/python.go b/odiglet/pkg/instrumentation/instrumentlang/python.go index 8c6eec05d..d305837ce 100644 --- a/odiglet/pkg/instrumentation/instrumentlang/python.go +++ b/odiglet/pkg/instrumentation/instrumentlang/python.go @@ -17,6 +17,8 @@ const ( envLogCorrelation = "OTEL_PYTHON_LOG_CORRELATION" envPythonPath = "PYTHONPATH" envOtelExporterOTLPTracesProtocol = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL" + pythonConfiguratorEnvVar = "OTEL_PYTHON_CONFIGURATOR" + pythonConfiguratorValue = "odigos-python-configurator" envOtelExporterOTLPMetricsProtocol = "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL" httpProtobufProtocol = "http/protobuf" pythonOdigosOpampServer = "ODIGOS_OPAMP_SERVER_HOST" @@ -45,6 +47,7 @@ func Python(deviceId string, uniqueDestinationSignals map[common.ObservabilitySi pythonOdigosOpampServer: opampServerHost, envLogCorrelation: "true", envPythonPath: pythonpathVal, + pythonConfiguratorEnvVar: pythonConfiguratorValue, "OTEL_EXPORTER_OTLP_ENDPOINT": otlpEndpoint, envOtelTracesExporter: tracesExporter, envOtelMetricsExporter: metricsExporter, diff --git a/tests/e2e/workload-lifecycle/01-assert-apps-installed.yaml b/tests/e2e/workload-lifecycle/01-assert-apps-installed.yaml index 8bceaebeb..b897f2a44 100644 --- a/tests/e2e/workload-lifecycle/01-assert-apps-installed.yaml +++ b/tests/e2e/workload-lifecycle/01-assert-apps-installed.yaml @@ -216,6 +216,25 @@ status: --- apiVersion: v1 kind: Pod +metadata: + labels: + app: python-other-agent + namespace: default +spec: + containers: + - env: + - name: DJANGO_SETTINGS_MODULE + (value != null): true +status: + containerStatuses: + - name: python-other-agent + ready: true + restartCount: 0 + started: true + phase: Running +--- +apiVersion: v1 +kind: Pod metadata: labels: app: python-alpine diff --git a/tests/e2e/workload-lifecycle/01-assert-instrumented.yaml b/tests/e2e/workload-lifecycle/01-assert-instrumented.yaml index 33714094c..48959e724 100644 --- a/tests/e2e/workload-lifecycle/01-assert-instrumented.yaml +++ b/tests/e2e/workload-lifecycle/01-assert-instrumented.yaml @@ -352,8 +352,19 @@ status: reason: RuntimeVersionNotSupported status: "False" type: AppliedInstrumentationDevice - - +--- +apiVersion: odigos.io/v1alpha1 +kind: InstrumentedApplication +metadata: + namespace: default + name: deployment-python-other-agent +status: + conditions: + - message: "device not added to any container due to the presence of another agent" + observedGeneration: 1 + reason: ErrApplyingInstrumentationDevice + status: "False" + type: AppliedInstrumentationDevice --- apiVersion: odigos.io/v1alpha1 kind: InstrumentationInstance diff --git a/tests/e2e/workload-lifecycle/01-assert-runtime-detected.yaml b/tests/e2e/workload-lifecycle/01-assert-runtime-detected.yaml index 79a26d088..fd9d001a7 100644 --- a/tests/e2e/workload-lifecycle/01-assert-runtime-detected.yaml +++ b/tests/e2e/workload-lifecycle/01-assert-runtime-detected.yaml @@ -289,6 +289,25 @@ spec: --- apiVersion: odigos.io/v1alpha1 kind: InstrumentedApplication +metadata: + name: deployment-python-other-agent + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: python-other-agent +spec: + runtimeDetails: + - containerName: python-other-agent + language: python + otherAgent: + name: New Relic Agent + (runtimeVersion != null): true +--- +apiVersion: odigos.io/v1alpha1 +kind: InstrumentedApplication metadata: name: deployment-python-min-version namespace: default diff --git a/tests/e2e/workload-lifecycle/01-install-test-apps.yaml b/tests/e2e/workload-lifecycle/01-install-test-apps.yaml index be9dbd894..234277136 100644 --- a/tests/e2e/workload-lifecycle/01-install-test-apps.yaml +++ b/tests/e2e/workload-lifecycle/01-install-test-apps.yaml @@ -572,6 +572,54 @@ spec: --- kind: Deployment apiVersion: apps/v1 +metadata: + name: python-other-agent + namespace: default + labels: + app: python-other-agent +spec: + selector: + matchLabels: + app: python-other-agent + template: + metadata: + labels: + app: python-other-agent + spec: + containers: + - name: python-other-agent + image: python-other-agent:v0.0.1 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8000 + env: + - name: DJANGO_SETTINGS_MODULE + value: "myapp.settings" + livenessProbe: + httpGet: + path: /health/ + port: 8000 + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + successThreshold: 1 +--- +kind: Service +apiVersion: v1 +metadata: + name: python-other-agent + namespace: default +spec: + selector: + app: python-other-agent + ports: + - protocol: TCP + port: 3000 + targetPort: 8000 +--- +kind: Deployment +apiVersion: apps/v1 metadata: name: python-alpine namespace: default diff --git a/tests/e2e/workload-lifecycle/chainsaw-test.yaml b/tests/e2e/workload-lifecycle/chainsaw-test.yaml index 1c0360e5d..a08c235a1 100644 --- a/tests/e2e/workload-lifecycle/chainsaw-test.yaml +++ b/tests/e2e/workload-lifecycle/chainsaw-test.yaml @@ -42,6 +42,8 @@ spec: docker build -t python-latest-version:v0.0.1 -f services/python-http-server/Dockerfile.python-latest services/python-http-server kind load docker-image python-latest-version:v0.0.1 + docker build -t python-other-agent:v0.0.1 -f services/python-http-server/Dockerfile.python-other-agent services/python-http-server + kind load docker-image python-other-agent:v0.0.1 docker build -t python-alpine:v0.0.1 -f services/python-http-server/Dockerfile.python-alpine services/python-http-server kind load docker-image python-alpine:v0.0.1 docker build -t python-not-supported:v0.0.1 -f services/python-http-server/Dockerfile.python-not-supported-version services/python-http-server diff --git a/tests/e2e/workload-lifecycle/services/python-http-server/Dockerfile.python-other-agent b/tests/e2e/workload-lifecycle/services/python-http-server/Dockerfile.python-other-agent new file mode 100644 index 000000000..eba3e8dcf --- /dev/null +++ b/tests/e2e/workload-lifecycle/services/python-http-server/Dockerfile.python-other-agent @@ -0,0 +1,22 @@ +FROM python + +WORKDIR /app +COPY . /app + +# Install dependencies from requirements.txt +COPY requirements.txt /app/requirements.txt + +RUN apt-get update && apt-get install sqlite3 -y + +RUN pip install --no-cache-dir -r requirements.txt + +# Supress health check endpoint from tracing +ENV OTEL_PYTHON_DJANGO_EXCLUDED_URLS=health/ + +COPY entrypoint.sh /entrypoint.sh + +ENV NEW_RELIC_CONFIG_FILE=/app/newrelic.ini + +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file From 0a6cd621007b698935fbb6445fe1c95a427a09c4 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 17:45:05 +0200 Subject: [PATCH 075/259] fix: remove duplicates --- Makefile | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Makefile b/Makefile index 28174d8ea..96005d933 100644 --- a/Makefile +++ b/Makefile @@ -206,16 +206,6 @@ cli-uninstall: @echo "Uninstalling odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . uninstall -.PHONY: cli-uninstall -cli-uninstall: - @echo "Uninstalling odigos from source. version: $(ODIGOS_CLI_VERSION)" - cd ./cli ; go run -tags=embed_manifests . uninstall - -.PHONY: cli-uninstall -cli-uninstall: - @echo "Uninstalling odigos from source. version: $(ODIGOS_CLI_VERSION)" - cd ./cli ; go run -tags=embed_manifests . uninstall - .PHONY: cli-upgrade cli-upgrade: @echo "Upgrading odigos from source. version: $(ODIGOS_CLI_VERSION)" From 154fb4f22430192acd64065c0cb9fd352851e770 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 17:45:22 +0200 Subject: [PATCH 076/259] feat: add more status to SSE --- frontend/endpoints/sse/sse.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/endpoints/sse/sse.go b/frontend/endpoints/sse/sse.go index afffa9115..4215a8a49 100644 --- a/frontend/endpoints/sse/sse.go +++ b/frontend/endpoints/sse/sse.go @@ -11,22 +11,25 @@ import ( type MessageType string const ( - MessageTypeSuccess MessageType = "success" + MessageTypeWarning MessageType = "warning" MessageTypeError MessageType = "error" + MessageTypeSuccess MessageType = "success" + MessageTypeInfo MessageType = "info" + MessageTypeDefault MessageType = "default" ) type MessageEvent string const ( - MessageEventDeleted MessageEvent = "Deleted" - MessageEventModified MessageEvent = "Modified" MessageEventAdded MessageEvent = "Added" + MessageEventDeleted MessageEvent = "Deleted" + MessageEventModified MessageEvent = "Modified" ) type SSEMessage struct { Type MessageType `json:"type"` - Data string `json:"data"` Event MessageEvent `json:"event"` + Data string `json:"data"` Target string `json:"target"` CRDType string `json:"crdType"` } From 4c5a84ea04cea00f3c3fd454f829cbc022cba392 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 17:46:18 +0200 Subject: [PATCH 077/259] feat: replace instru. apps watcher with istru. configs watcher --- cli/cmd/resources/ui.go | 4 +- ...r.go => instrumentation_config_watcher.go} | 42 ++++++++++--------- frontend/main.go | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) rename frontend/kube/watchers/{instrumented_application_watcher.go => instrumentation_config_watcher.go} (54%) diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index d0cea23a7..5d0fbdab6 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -232,7 +232,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { }, { // Needed to read Odigos entities APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances", "instrumentationconfigs"}, + Resources: []string{"instrumentationinstances", "instrumentationconfigs"}, Verbs: []string{"get", "list"}, }, { // Needed to instrument / uninstrument applications @@ -242,7 +242,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { }, { // Needed to watch instrumented applications APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances"}, + Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, Verbs: []string{"watch"}, }, }, diff --git a/frontend/kube/watchers/instrumented_application_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go similarity index 54% rename from frontend/kube/watchers/instrumented_application_watcher.go rename to frontend/kube/watchers/instrumentation_config_watcher.go index b17271289..fa0e3e74f 100644 --- a/frontend/kube/watchers/instrumented_application_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -15,11 +15,11 @@ import ( var addedEventBatcher *EventBatcher var deletedEventBatcher *EventBatcher -func StartInstrumentedApplicationWatcher(ctx context.Context, namespace string) error { +func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { addedEventBatcher = NewEventBatcher( EventBatcherConfig{ Event: sse.MessageEventAdded, - CRDType: "InstrumentedApplication", + CRDType: "InstrumentationConfigs", SuccessBatchMessageFunc: func(count int, crdType string) string { return fmt.Sprintf("successfully added %d sources", count) }, @@ -32,7 +32,7 @@ func StartInstrumentedApplicationWatcher(ctx context.Context, namespace string) deletedEventBatcher = NewEventBatcher( EventBatcherConfig{ Event: sse.MessageEventDeleted, - CRDType: "InstrumentedApplication", + CRDType: "InstrumentationConfigs", SuccessBatchMessageFunc: func(count int, crdType string) string { return fmt.Sprintf("successfully deleted %d sources", count) }, @@ -42,16 +42,16 @@ func StartInstrumentedApplicationWatcher(ctx context.Context, namespace string) }, ) - watcher, err := kube.DefaultClient.OdigosClient.InstrumentedApplications(namespace).Watch(context.Background(), metav1.ListOptions{}) + watcher, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { return fmt.Errorf("error creating watcher: %v", err) } - go handleInstrumentedApplicationWatchEvents(ctx, watcher) + go handleInstrumentationConfigWatchEvents(ctx, watcher) return nil } -func handleInstrumentedApplicationWatchEvents(ctx context.Context, watcher watch.Interface) { +func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() defer addedEventBatcher.Cancel() defer deletedEventBatcher.Cancel() @@ -66,32 +66,36 @@ func handleInstrumentedApplicationWatchEvents(ctx context.Context, watcher watch } switch event.Type { case watch.Added: - handleAddedEvent(event.Object.(*v1alpha1.InstrumentedApplication)) + handleAddedEvent(event.Object.(*v1alpha1.InstrumentationConfig)) case watch.Deleted: - handleDeletedEvent(event.Object.(*v1alpha1.InstrumentedApplication)) + handleDeletedEvent(event.Object.(*v1alpha1.InstrumentationConfig)) } } } } -func handleAddedEvent(app *v1alpha1.InstrumentedApplication) { - name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(app.Name) +func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { - genericErrorMessage(sse.MessageEventAdded, "InstrumentedApplication", "error getting workload info") + genericErrorMessage(sse.MessageEventAdded, "InstrumentationConfig", err.Error()) return } - namespace := app.Namespace - target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf("InstrumentedApplication %s created", name) + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf("InstrumentationConfig %s created successfully", name) addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } -func handleDeletedEvent(app *v1alpha1.InstrumentedApplication) { - name, _, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(app.Name) +func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { - genericErrorMessage(sse.MessageEventDeleted, "InstrumentedApplication", "error getting workload info") + genericErrorMessage(sse.MessageEventDeleted, "InstrumentationConfig", err.Error()) return } - data := fmt.Sprintf("Source %s deleted successfully", name) - deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, "") + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf("InstrumentationConfig %s deleted successfully", name) + deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/main.go b/frontend/main.go index ce99dbd7b..a340138ef 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -318,7 +318,7 @@ func main() { } // Start watchers - err = watchers.StartInstrumentedApplicationWatcher(ctx, "") + err = watchers.StartInstrumentationConfigWatcher(ctx, "") if err != nil { log.Printf("Error starting InstrumentedApplication watcher: %v", err) } From f2aa2e1600f2b0cff0318b7956b4de05b49b3216 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 18:03:27 +0200 Subject: [PATCH 078/259] revert pull Ron PR --- .../odigos.io_instrumentationconfigs.yaml | 56 ------------------- .../v1alpha1/instrumentationconfigstatus.go | 20 +------ .../v1alpha1/instrumentationconfig_types.go | 37 ------------ .../v1alpha1/instrumentedapplication_types.go | 27 +++++---- api/odigos/v1alpha1/zz_generated.deepcopy.go | 7 --- .../odigos.io_instrumentationconfigs.yaml | 56 ------------------- .../instrumentationdevice/common.go | 46 +++++++-------- .../instrumentation/instrumentation.go | 20 +++---- 8 files changed, 51 insertions(+), 218 deletions(-) diff --git a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml index 5a49486ff..8fb450866 100644 --- a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml +++ b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml @@ -423,62 +423,6 @@ spec: type: object status: properties: - conditions: - 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 runtimeDetailsByContainer: description: Capture Runtime Details for the workloads that this CR applies to. diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go index f6796dea5..02b0468ee 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go @@ -17,15 +17,10 @@ limitations under the License. package v1alpha1 -import ( - v1 "k8s.io/client-go/applyconfigurations/meta/v1" -) - // InstrumentationConfigStatusApplyConfiguration represents a declarative configuration of the InstrumentationConfigStatus type for use // with apply. type InstrumentationConfigStatusApplyConfiguration struct { RuntimeDetailsByContainer []RuntimeDetailsByContainerApplyConfiguration `json:"runtimeDetailsByContainer,omitempty"` - Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` } // InstrumentationConfigStatusApplyConfiguration constructs a declarative configuration of the InstrumentationConfigStatus type for use with @@ -45,17 +40,4 @@ func (b *InstrumentationConfigStatusApplyConfiguration) WithRuntimeDetailsByCont b.RuntimeDetailsByContainer = append(b.RuntimeDetailsByContainer, *values[i]) } return b -} - -// 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 *InstrumentationConfigStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationConfigStatusApplyConfiguration { - for i := range values { - if values[i] == nil { - panic("nil value passed to WithConditions") - } - b.Conditions = append(b.Conditions, *values[i]) - } - return b -} +} \ No newline at end of file diff --git a/api/odigos/v1alpha1/instrumentationconfig_types.go b/api/odigos/v1alpha1/instrumentationconfig_types.go index a8158d713..0c157f3a3 100644 --- a/api/odigos/v1alpha1/instrumentationconfig_types.go +++ b/api/odigos/v1alpha1/instrumentationconfig_types.go @@ -5,16 +5,8 @@ import ( "github.com/odigos-io/odigos/common" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) -// +kubebuilder:object:generate=false -type WorkloadDetailsObject interface { - client.Object - RuntimeDetailsByContainer() []RuntimeDetailsByContainer - Conditions() *[]metav1.Condition -} - // +genclient // +kubebuilder:object:root=true // +kubebuilder:subresource:status @@ -28,38 +20,9 @@ type InstrumentationConfig struct { Status InstrumentationConfigStatus `json:"status,omitempty"` } -var _ WorkloadDetailsObject = &InstrumentationConfig{} - -func (ic *InstrumentationConfig) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { - return ic.Status.RuntimeDetailsByContainer -} - -func (ic *InstrumentationConfig) Conditions() *[]metav1.Condition { - return &ic.Status.Conditions -} - -// +kubebuilder:object:generate=true -type RuntimeDetailsByContainer struct { - ContainerName string `json:"containerName"` - Language common.ProgrammingLanguage `json:"language"` - RuntimeVersion string `json:"runtimeVersion,omitempty"` - EnvVars []EnvVar `json:"envVars,omitempty"` - OtherAgent *OtherAgent `json:"otherAgent,omitempty"` - LibCType *common.LibCType `json:"libCType,omitempty"` - - // Stores the error message from the CRI runtime if returned to prevent instrumenting the container if an error exists. - CriErrorMessage *string `json:"criErrorMessage,omitempty"` - // Holds the environment variables retrieved from the container runtime. - EnvFromContainerRuntime []EnvVar `json:"envFromContainerRuntime,omitempty"` - // A temporary variable used during migration to track whether the new runtime detection process has been executed. If empty, it indicates the process has not yet been run. This field may be removed later. - RuntimeUpdateState *ProcessingState `json:"runtimeUpdateState,omitempty"` -} - type InstrumentationConfigStatus struct { // Capture Runtime Details for the workloads that this CR applies to. RuntimeDetailsByContainer []RuntimeDetailsByContainer `json:"runtimeDetailsByContainer,omitempty"` - - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" protobuf:"bytes,1,rep,name=conditions"` } // Config for the OpenTelemeetry SDKs that should be applied to a workload. diff --git a/api/odigos/v1alpha1/instrumentedapplication_types.go b/api/odigos/v1alpha1/instrumentedapplication_types.go index b66a6c6a2..cbedca502 100644 --- a/api/odigos/v1alpha1/instrumentedapplication_types.go +++ b/api/odigos/v1alpha1/instrumentedapplication_types.go @@ -51,6 +51,23 @@ const ( ProcessingStateSkipped ProcessingState = "Skipped" ) +// +kubebuilder:object:generate=true +type RuntimeDetailsByContainer struct { + ContainerName string `json:"containerName"` + Language common.ProgrammingLanguage `json:"language"` + RuntimeVersion string `json:"runtimeVersion,omitempty"` + EnvVars []EnvVar `json:"envVars,omitempty"` + OtherAgent *OtherAgent `json:"otherAgent,omitempty"` + LibCType *common.LibCType `json:"libCType,omitempty"` + + // Stores the error message from the CRI runtime if returned to prevent instrumenting the container if an error exists. + CriErrorMessage *string `json:"criErrorMessage,omitempty"` + // Holds the environment variables retrieved from the container runtime. + EnvFromContainerRuntime []EnvVar `json:"envFromContainerRuntime,omitempty"` + // A temporary variable used during migration to track whether the new runtime detection process has been executed. If empty, it indicates the process has not yet been run. This field may be removed later. + RuntimeUpdateState *ProcessingState `json:"runtimeUpdateState,omitempty"` +} + // +kubebuilder:object:generate=true type OptionByContainer struct { ContainerName string `json:"containerName"` @@ -93,16 +110,6 @@ type InstrumentedApplicationList struct { Items []InstrumentedApplication `json:"items"` } -var _ WorkloadDetailsObject = &InstrumentedApplication{} - -func (ia *InstrumentedApplication) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { - return ia.Spec.RuntimeDetails -} - -func (ia *InstrumentedApplication) Conditions() *[]metav1.Condition { - return &ia.Status.Conditions -} - func init() { SchemeBuilder.Register(&InstrumentedApplication{}, &InstrumentedApplicationList{}) } diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 4d703eb33..5c7edb7ad 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -484,13 +484,6 @@ func (in *InstrumentationConfigStatus) DeepCopyInto(out *InstrumentationConfigSt (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationConfigStatus. diff --git a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml index 5a49486ff..8fb450866 100644 --- a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml +++ b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml @@ -423,62 +423,6 @@ spec: type: object status: properties: - conditions: - 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 runtimeDetailsByContainer: description: Capture Runtime Details for the workloads that this CR applies to. diff --git a/instrumentor/controllers/instrumentationdevice/common.go b/instrumentor/controllers/instrumentationdevice/common.go index 7adaecab1..a0de5727a 100644 --- a/instrumentor/controllers/instrumentationdevice/common.go +++ b/instrumentor/controllers/instrumentationdevice/common.go @@ -66,13 +66,13 @@ func isDataCollectionReady(ctx context.Context, c client.Client) bool { return nodeCollectorsGroup.Status.Ready } -func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetailsObject) (error, bool) { +func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, runtimeDetails *odigosv1.InstrumentedApplication) (error, bool) { // devicePartiallyApplied is used to indicate that the instrumentation device was partially applied for some of the containers. devicePartiallyApplied := false deviceNotAppliedDueToPresenceOfAnotherAgent := false logger := log.FromContext(ctx) - obj, err := getWorkloadObject(ctx, kubeClient, workloadDetails) + obj, err := getWorkloadObject(ctx, kubeClient, runtimeDetails) if err != nil { return err, false } @@ -133,7 +133,7 @@ func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.C agentsCanRunConcurrently = *odigosConfiguration.AllowConcurrentAgents } - err, deviceApplied, deviceSkippedDueToOtherAgent := instrumentation.ApplyInstrumentationDevicesToPodTemplate(podSpec, workloadDetails.RuntimeDetailsByContainer(), otelSdkToUse, obj, logger, agentsCanRunConcurrently) + err, deviceApplied, deviceSkippedDueToOtherAgent := instrumentation.ApplyInstrumentationDevicesToPodTemplate(podSpec, runtimeDetails, otelSdkToUse, obj, logger, agentsCanRunConcurrently) if err != nil { return err } @@ -213,8 +213,8 @@ func removeInstrumentationDeviceFromWorkload(ctx context.Context, kubeClient cli return nil } -func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDetailsObject client.Object) (client.Object, error) { - name, kind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(runtimeDetailsObject.GetName()) +func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDetails *odigosv1.InstrumentedApplication) (client.Object, error) { + name, kind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(runtimeDetails.Name) if err != nil { return nil, err } @@ -225,7 +225,7 @@ func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDet } err = kubeClient.Get(ctx, client.ObjectKey{ - Namespace: runtimeDetailsObject.GetNamespace(), + Namespace: runtimeDetails.Namespace, Name: name, }, workloadObject) if err != nil { @@ -251,45 +251,45 @@ func getPodSpecFromObject(obj client.Object) (*corev1.PodTemplateSpec, error) { // reconciles a single workload, which might be triggered by a change in multiple resources. // each time a relevant resource changes, this function is called to reconcile the workload // and always writes the status into the InstrumentedApplication CR -func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetailsObject, isNodeCollectorReady bool) error { +func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, instrumentedApplication *odigosv1.InstrumentedApplication, isNodeCollectorReady bool) error { - workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(workloadDetails.GetName()) + workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedApplication.Name) if err != nil { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) return err } if !isNodeCollectorReady { - err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonDataCollectionNotReady) + err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonDataCollectionNotReady) if err == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonDataCollectionNotReady), "OpenTelemetry pipeline not yet ready to receive data") + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonDataCollectionNotReady), "OpenTelemetry pipeline not yet ready to receive data") } else { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) } return err } - if len(workloadDetails.RuntimeDetailsByContainer()) == 0 { - err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonNoRuntimeDetails) + if len(instrumentedApplication.Spec.RuntimeDetails) == 0 { + err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonNoRuntimeDetails) if err == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonNoRuntimeDetails), "No runtime details found") + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonNoRuntimeDetails), "No runtime details found") } else { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) } return err } - runtimeVersionSupport, err := versionsupport.IsRuntimeVersionSupported(ctx, workloadDetails.RuntimeDetailsByContainer()) + runtimeVersionSupport, err := versionsupport.IsRuntimeVersionSupported(ctx, instrumentedApplication.Spec.RuntimeDetails) if !runtimeVersionSupport { - errRemove := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported) + errRemove := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported) if errRemove == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported), err.Error()) } else { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), errRemove.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), errRemove.Error()) } return nil } - err, devicePartiallyApplied := addInstrumentationDeviceToWorkload(ctx, kubeClient, workloadDetails) + err, devicePartiallyApplied := addInstrumentationDeviceToWorkload(ctx, kubeClient, instrumentedApplication) if err == nil { var successMessage string if devicePartiallyApplied { @@ -297,9 +297,9 @@ func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, work } else { successMessage = "Instrumentation device applied successfully" } - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionTrue, appliedInstrumentationDeviceType, "InstrumentationDeviceApplied", successMessage) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionTrue, appliedInstrumentationDeviceType, "InstrumentationDeviceApplied", successMessage) } else { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrApplying), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrApplying), err.Error()) } return err } diff --git a/instrumentor/instrumentation/instrumentation.go b/instrumentor/instrumentation/instrumentation.go index 5451356e3..f5afd09b4 100644 --- a/instrumentor/instrumentation/instrumentation.go +++ b/instrumentor/instrumentation/instrumentation.go @@ -22,7 +22,7 @@ var ( ErrPatchEnvVars = errors.New("failed to patch env vars") ) -func ApplyInstrumentationDevicesToPodTemplate(original *corev1.PodTemplateSpec, runtimeDetails []odigosv1.RuntimeDetailsByContainer, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object, +func ApplyInstrumentationDevicesToPodTemplate(original *corev1.PodTemplateSpec, runtimeDetails *odigosv1.InstrumentedApplication, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object, logger logr.Logger, agentsCanRunConcurrently bool) (error, bool, bool) { // delete any existing instrumentation devices. // this is necessary for example when migrating from community to enterprise, @@ -154,8 +154,8 @@ func RevertInstrumentationDevices(original *corev1.PodTemplateSpec) bool { return changed } -func getLanguageOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) common.ProgrammingLanguage { - for _, l := range runtimeDetails { +func getLanguageOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) common.ProgrammingLanguage { + for _, l := range instrumentation.Spec.RuntimeDetails { if l.ContainerName == containerName { return l.Language } @@ -164,8 +164,8 @@ func getLanguageOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, return common.UnknownProgrammingLanguage } -func getContainerOtherAgents(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) *odigosv1.OtherAgent { - for _, l := range runtimeDetails { +func getContainerOtherAgents(instrumentation *odigosv1.InstrumentedApplication, containerName string) *odigosv1.OtherAgent { + for _, l := range instrumentation.Spec.RuntimeDetails { if l.ContainerName == containerName { if l.OtherAgent != nil && *l.OtherAgent != (odigosv1.OtherAgent{}) { return l.OtherAgent @@ -175,8 +175,8 @@ func getContainerOtherAgents(runtimeDetails []odigosv1.RuntimeDetailsByContainer return nil } -func getLibCTypeOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) *common.LibCType { - for _, l := range runtimeDetails { +func getLibCTypeOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) *common.LibCType { + for _, l := range instrumentation.Spec.RuntimeDetails { if l.ContainerName == containerName { return l.LibCType } @@ -187,10 +187,10 @@ func getLibCTypeOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, // getEnvVarsOfContainer returns the env vars which are defined for the given container and are used for instrumentation purposes. // This function also returns env vars which are declared in the container build. -func getEnvVarsOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) map[string]string { +func getEnvVarsOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) map[string]string { envVars := make(map[string]string) - for _, l := range runtimeDetails { + for _, l := range instrumentation.Spec.RuntimeDetails { if l.ContainerName == containerName { for _, env := range l.EnvVars { envVars[env.Name] = env.Value @@ -204,7 +204,7 @@ func getEnvVarsOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, // when otelsdk is nil, it means that the container is not instrumented. // this will trigger reverting of any existing env vars which were set by odigos before. -func patchEnvVarsForContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, container *corev1.Container, sdk *common.OtelSdk, programmingLanguage common.ProgrammingLanguage, manifestEnvOriginal *envoverwrite.OrigWorkloadEnvValues) error { +func patchEnvVarsForContainer(runtimeDetails *odigosv1.InstrumentedApplication, container *corev1.Container, sdk *common.OtelSdk, programmingLanguage common.ProgrammingLanguage, manifestEnvOriginal *envoverwrite.OrigWorkloadEnvValues) error { observedEnvs := getEnvVarsOfContainer(runtimeDetails, container.Name) From 4ae87c1fa01bd83f9b5f387a595cc18ae53db86a Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 18:05:36 +0200 Subject: [PATCH 079/259] fix: empty line --- .../odigos/v1alpha1/instrumentationconfigstatus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go index 02b0468ee..efab8f15b 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go @@ -40,4 +40,4 @@ func (b *InstrumentationConfigStatusApplyConfiguration) WithRuntimeDetailsByCont b.RuntimeDetailsByContainer = append(b.RuntimeDetailsByContainer, *values[i]) } return b -} \ No newline at end of file +} From c233f5bfcda81796ad03bcf4e595825a29b07895 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 18:40:55 +0200 Subject: [PATCH 080/259] fix: build error --- frontend/graph/conversions.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index d7a06642e..695c1c86a 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -61,15 +61,15 @@ func instrumentationConfigToActualSource(instruConfig v1alpha1.InstrumentationCo // Map the conditions var conditions []*model.Condition - for _, condition := range instruConfig.Status.Conditions { - conditions = append(conditions, &model.Condition{ - Status: k8sConditionStatusToGql(condition.Status), - Type: condition.Type, - Reason: &condition.Reason, - Message: &condition.Message, - LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), - }) - } + // for _, condition := range instruConfig.Status.Conditions { + // conditions = append(conditions, &model.Condition{ + // Status: k8sConditionStatusToGql(condition.Status), + // Type: condition.Type, + // Reason: &condition.Reason, + // Message: &condition.Message, + // LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), + // }) + // } // Return the converted K8sActualSource object return &model.K8sActualSource{ From d12c5fcb9ef81be8e94b6cb7978a48026660f240 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 18:49:25 +0200 Subject: [PATCH 081/259] fix: improve health status determination logic --- .../webapp/utils/functions/strings/get-health-status/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/webapp/utils/functions/strings/get-health-status/index.ts b/frontend/webapp/utils/functions/strings/get-health-status/index.ts index f5de8bcf7..ff0169151 100644 --- a/frontend/webapp/utils/functions/strings/get-health-status/index.ts +++ b/frontend/webapp/utils/functions/strings/get-health-status/index.ts @@ -3,7 +3,7 @@ import { STATUSES, type ActualDestination, type K8sActualSource } from '@/types' export const getHealthStatus = (item: K8sActualSource | ActualDestination) => { const conditions = item?.conditions || []; - const isUnhealthy = !conditions.length || !!conditions.find(({ status }) => status === BACKEND_BOOLEAN.FALSE); + const isUnhealthy = !!conditions.find(({ status }) => status === BACKEND_BOOLEAN.FALSE); return isUnhealthy ? STATUSES.UNHEALTHY : STATUSES.HEALTHY; }; From 527ef33dad6cfd6330da30884bb52830255d2ba4 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 19:00:47 +0200 Subject: [PATCH 082/259] fix: improve error messages in source management functions --- frontend/services/sources.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 81bcc7f60..c0dfb0f34 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -2,7 +2,6 @@ package services import ( "context" - "errors" "fmt" "time" @@ -309,10 +308,10 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, err } if len(source.Items) == 0 { - return nil, errors.New("\"" + workloadName + "\"" + " source not found") + return nil, fmt.Errorf(`source "%s" not found`, workloadName) } if len(source.Items) > 1 { - return nil, errors.New("\"" + workloadName + "\"" + " expected to get 1 source got " + string(len(source.Items))) + return nil, fmt.Errorf(`expected to get 1 source "%s", got %s`, workloadName, fmt.Sprint(len(source.Items))) } crdName := source.Items[0].Name @@ -329,7 +328,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return errors.New("\"" + workloadName + "\"" + " source already exists") + return fmt.Errorf(`source "%s" already exists`, workloadName) } newSource := &v1alpha1.Source{ @@ -366,7 +365,7 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { if enabled == nil { - return errors.New("enabled must be provided") + return fmt.Errorf("enabled must be provided") } if *enabled { From bd951222d826f9922537479543c09bf5940e0aa0 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 19:01:12 +0200 Subject: [PATCH 083/259] fix: correct terminology for InstrumentationConfig in watchers and logs --- frontend/kube/watchers/instrumentation_config_watcher.go | 4 ++-- frontend/main.go | 2 +- frontend/webapp/hooks/notification/useClickNotif.ts | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index fa0e3e74f..b762d585a 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -19,7 +19,7 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er addedEventBatcher = NewEventBatcher( EventBatcherConfig{ Event: sse.MessageEventAdded, - CRDType: "InstrumentationConfigs", + CRDType: "InstrumentationConfig", SuccessBatchMessageFunc: func(count int, crdType string) string { return fmt.Sprintf("successfully added %d sources", count) }, @@ -32,7 +32,7 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er deletedEventBatcher = NewEventBatcher( EventBatcherConfig{ Event: sse.MessageEventDeleted, - CRDType: "InstrumentationConfigs", + CRDType: "InstrumentationConfig", SuccessBatchMessageFunc: func(count int, crdType string) string { return fmt.Sprintf("successfully deleted %d sources", count) }, diff --git a/frontend/main.go b/frontend/main.go index a340138ef..5b5bb13a4 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -320,7 +320,7 @@ func main() { // Start watchers err = watchers.StartInstrumentationConfigWatcher(ctx, "") if err != nil { - log.Printf("Error starting InstrumentedApplication watcher: %v", err) + log.Printf("Error starting InstrumentationConfig watcher: %v", err) } err = watchers.StartDestinationWatcher(ctx, flags.Namespace) diff --git a/frontend/webapp/hooks/notification/useClickNotif.ts b/frontend/webapp/hooks/notification/useClickNotif.ts index a9ba5a970..1f9fc090b 100644 --- a/frontend/webapp/hooks/notification/useClickNotif.ts +++ b/frontend/webapp/hooks/notification/useClickNotif.ts @@ -29,6 +29,7 @@ export const useClickNotif = () => { break; case OVERVIEW_ENTITY_TYPES.SOURCE: + case 'InstrumentationConfig': case 'InstrumentedApplication': case 'InstrumentationInstance': drawerItem['type'] = OVERVIEW_ENTITY_TYPES.SOURCE; From 0eb35fe8c5dc2d25bbedfc0d9eb19e77bd921bf2 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 19:04:07 +0200 Subject: [PATCH 084/259] fix: remove obsolete case for InstrumentedApplication in useClickNotif hook --- frontend/webapp/hooks/notification/useClickNotif.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/webapp/hooks/notification/useClickNotif.ts b/frontend/webapp/hooks/notification/useClickNotif.ts index 1f9fc090b..0be9bc130 100644 --- a/frontend/webapp/hooks/notification/useClickNotif.ts +++ b/frontend/webapp/hooks/notification/useClickNotif.ts @@ -30,7 +30,6 @@ export const useClickNotif = () => { case OVERVIEW_ENTITY_TYPES.SOURCE: case 'InstrumentationConfig': - case 'InstrumentedApplication': case 'InstrumentationInstance': drawerItem['type'] = OVERVIEW_ENTITY_TYPES.SOURCE; drawerItem['id'] = getIdFromSseTarget(target, OVERVIEW_ENTITY_TYPES.SOURCE); From 91d69aedb0107a13d0db39f96300073093d0587b Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Sun, 29 Dec 2024 12:31:45 -0500 Subject: [PATCH 085/259] Enable source e2e test scenario (#2096) Missed this when adding the test. Now it will actually run --- .github/workflows/e2e.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index cc88e1882..cd21b3d58 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -75,6 +75,7 @@ jobs: - 'helm-chart' - 'cli-upgrade' - 'workload-lifecycle' + - 'source' include: - kube-version: '1.20.15' kind-image: 'kindest/node:v1.20.15@sha256:a32bf55309294120616886b5338f95dd98a2f7231519c7dedcec32ba29699394' From 2cfcb92c0a68262797d48db4170b566abc133d3a Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Mon, 30 Dec 2024 10:03:54 +0200 Subject: [PATCH 086/259] chore: init custom processor --- .../Makefile | 1 + .../README.md | 54 +++++++ .../config.go | 38 +++++ .../factory.go | 45 ++++++ .../generated_component_test.go | 135 ++++++++++++++++ .../generated_package_test.go | 12 ++ .../go.mod | 65 ++++++++ .../go.sum | 152 ++++++++++++++++++ .../internal/metadata/generated_status.go | 15 ++ .../metadata.yaml | 12 ++ .../processor.go | 51 ++++++ 11 files changed, 580 insertions(+) create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/Makefile create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/README.md create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/config.go create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/factory.go create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/generated_package_test.go create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/go.mod create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/go.sum create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml create mode 100644 collector/processors/odigossourcetodestinationfilterprocessor/processor.go diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/Makefile b/collector/processors/odigossourcetodestinationfilterprocessor/Makefile new file mode 100644 index 000000000..ded7a3609 --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/README.md b/collector/processors/odigossourcetodestinationfilterprocessor/README.md new file mode 100644 index 000000000..9ec21bb9a --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/README.md @@ -0,0 +1,54 @@ +# Odigos Sampling processor + +This processor samples traces based on the following supported rules, grouping into these categories: + +1. Endpoint Rules: + +- HTTP Latency Rule: This rule allows you to configure service, endpoint, and threshold. Traces with a duration less than the specified threshold will be deleted. + + +``` yaml + groupbytrace: + wait_duration: 10s + odigossampling: + rules: + endpoint_rules: + - name: "http-latency-test" + type: "http_latency" + rule_details: + "threshold": 1050 + "http_route": "/buy" + "service_name": "frontend" + "fallback_sampling_ratio": 20.0 + ``` +- threshold: The maximum allowable trace duration in milliseconds. Traces with a duration less than this value will be deleted. +- endpoint: The specific HTTP route prefix to match for sampling. Only traces with routes starting with this prefix will be considered. For example, configuring /buy will also match /buy/product. +- service: The name of the service for which the rule applies. Only traces from this service will be considered. +- fallback_sampling_ratio: specifies the percentage of traces that meet the service/http_route filter but fall below the threshold that you still want to retain. For example, if a rule is set for service A and http_route B with a minimum latency threshold of 1 second, you might still want to keep some traces below this threshold. Setting the ratio to 20% ensures that 20% of these traces will be retained. + +2. Global Rules: +- Error Rule: This rule allows you to configure a list of status codes [ERROR/OK/UNSET]. traces with a status code that not configured will be delete. + +``` yaml +rules: + global_rules: + - name: "error-rule" + type: error + rule_details: + fallback_sampling_ratio: 50 +``` +- fallback_sampling_ratio: This parameter specifies the percentage of non-error traces you want to retain. For instance, setting it to 50 means you will see 100% of error traces and 50% of non-error traces. + + +**Notes:** +- When using the `odigossampling` processor, it is mandatory to use the `groupbytrace` processor beforehand. +``` +service: + pipelines: + traces: + receivers: + processors: + - groupbytrace + - odigossampling + exporters: +``` \ No newline at end of file diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/config.go b/collector/processors/odigossourcetodestinationfilterprocessor/config.go new file mode 100644 index 000000000..374fed2f1 --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/config.go @@ -0,0 +1,38 @@ +package odigossourcetodestinationfilterprocessor + +import ( + "errors" + + "go.opentelemetry.io/collector/component" +) + +type Config struct { + MatchConditions []MatchCondition `mapstructure:"match_conditions"` +} + +var _ component.Config = (*Config)(nil) + +func (cfg *Config) Validate() error { + if len(cfg.MatchConditions) == 0 { + return errors.New("at least one match condition must be specified") + } + for _, condition := range cfg.MatchConditions { + if err := condition.Validate(); err != nil { + return err + } + } + return nil +} + +type MatchCondition struct { + Name string `mapstructure:"name"` + Namespace string `mapstructure:"namespace"` + Kind string `mapstructure:"kind"` +} + +func (mc *MatchCondition) Validate() error { + if mc.Name == "" || mc.Namespace == "" || mc.Kind == "" { + return errors.New("all match condition fields (name, namespace, kind) must be specified") + } + return nil +} diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/factory.go b/collector/processors/odigossourcetodestinationfilterprocessor/factory.go new file mode 100644 index 000000000..037961252 --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/factory.go @@ -0,0 +1,45 @@ +package odigossourcetodestinationfilterprocessor + +import ( + "context" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/processor" + "go.opentelemetry.io/collector/processor/processorhelper" +) + +func NewFactory() processor.Factory { + return processor.NewFactory( + component.MustNewType("odigossourcetodestinationfilterprocessor"), + createDefaultConfig, + processor.WithTraces(createTracesProcessor, component.StabilityLevelBeta), + ) +} + +func createDefaultConfig() component.Config { + return &Config{ + MatchConditions: []MatchCondition{}, + } +} + +func createTracesProcessor( + ctx context.Context, + set processor.Settings, + cfg component.Config, + nextConsumer consumer.Traces) (processor.Traces, error) { + + filterProc := &filterProcessor{ + logger: set.Logger, + config: cfg.(*Config), + } + + return processorhelper.NewTracesProcessor( + ctx, + set, + cfg, + nextConsumer, + filterProc.processTraces, + processorhelper.WithCapabilities(consumer.Capabilities{MutatesData: true}), + ) +} diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go b/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go new file mode 100644 index 000000000..2442a1317 --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go @@ -0,0 +1,135 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package odigossourcetodestinationfilterprocessor + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/confmap/confmaptest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" + "go.opentelemetry.io/collector/processor" + "go.opentelemetry.io/collector/processor/processortest" +) + +func TestComponentFactoryType(t *testing.T) { + require.Equal(t, "odigossourcetodestinationfilterprocessor", NewFactory().Type().String()) +} + +func TestComponentConfigStruct(t *testing.T) { + require.NoError(t, componenttest.CheckConfigStruct(NewFactory().CreateDefaultConfig())) +} + +func TestComponentLifecycle(t *testing.T) { + factory := NewFactory() + + tests := []struct { + name string + createFn func(ctx context.Context, set processor.Settings, cfg component.Config) (component.Component, error) + }{ + + { + name: "traces", + createFn: func(ctx context.Context, set processor.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateTracesProcessor(ctx, set, cfg, consumertest.NewNop()) + }, + }, + } + + cm, err := confmaptest.LoadConf("metadata.yaml") + require.NoError(t, err) + cfg := factory.CreateDefaultConfig() + sub, err := cm.Sub("tests::config") + require.NoError(t, err) + require.NoError(t, sub.Unmarshal(&cfg)) + + for _, test := range tests { + t.Run(test.name+"-shutdown", func(t *testing.T) { + c, err := test.createFn(context.Background(), processortest.NewNopSettings(), cfg) + require.NoError(t, err) + err = c.Shutdown(context.Background()) + require.NoError(t, err) + }) + t.Run(test.name+"-lifecycle", func(t *testing.T) { + c, err := test.createFn(context.Background(), processortest.NewNopSettings(), cfg) + require.NoError(t, err) + host := componenttest.NewNopHost() + err = c.Start(context.Background(), host) + require.NoError(t, err) + require.NotPanics(t, func() { + switch test.name { + case "logs": + e, ok := c.(processor.Logs) + require.True(t, ok) + logs := generateLifecycleTestLogs() + if !e.Capabilities().MutatesData { + logs.MarkReadOnly() + } + err = e.ConsumeLogs(context.Background(), logs) + case "metrics": + e, ok := c.(processor.Metrics) + require.True(t, ok) + metrics := generateLifecycleTestMetrics() + if !e.Capabilities().MutatesData { + metrics.MarkReadOnly() + } + err = e.ConsumeMetrics(context.Background(), metrics) + case "traces": + e, ok := c.(processor.Traces) + require.True(t, ok) + traces := generateLifecycleTestTraces() + if !e.Capabilities().MutatesData { + traces.MarkReadOnly() + } + err = e.ConsumeTraces(context.Background(), traces) + } + }) + require.NoError(t, err) + err = c.Shutdown(context.Background()) + require.NoError(t, err) + }) + } +} + +func generateLifecycleTestLogs() plog.Logs { + logs := plog.NewLogs() + rl := logs.ResourceLogs().AppendEmpty() + rl.Resource().Attributes().PutStr("resource", "R1") + l := rl.ScopeLogs().AppendEmpty().LogRecords().AppendEmpty() + l.Body().SetStr("test log message") + l.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) + return logs +} + +func generateLifecycleTestMetrics() pmetric.Metrics { + metrics := pmetric.NewMetrics() + rm := metrics.ResourceMetrics().AppendEmpty() + rm.Resource().Attributes().PutStr("resource", "R1") + m := rm.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty() + m.SetName("test_metric") + dp := m.SetEmptyGauge().DataPoints().AppendEmpty() + dp.Attributes().PutStr("test_attr", "value_1") + dp.SetIntValue(123) + dp.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) + return metrics +} + +func generateLifecycleTestTraces() ptrace.Traces { + traces := ptrace.NewTraces() + rs := traces.ResourceSpans().AppendEmpty() + rs.Resource().Attributes().PutStr("resource", "R1") + span := rs.ScopeSpans().AppendEmpty().Spans().AppendEmpty() + span.Attributes().PutStr("test_attr", "value_1") + span.SetName("test_span") + span.SetStartTimestamp(pcommon.NewTimestampFromTime(time.Now().Add(-1 * time.Second))) + span.SetEndTimestamp(pcommon.NewTimestampFromTime(time.Now())) + return traces +} diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/generated_package_test.go b/collector/processors/odigossourcetodestinationfilterprocessor/generated_package_test.go new file mode 100644 index 000000000..9a82c579b --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/generated_package_test.go @@ -0,0 +1,12 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package odigossourcetodestinationfilterprocessor + +import ( + "go.uber.org/goleak" + "testing" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/go.mod b/collector/processors/odigossourcetodestinationfilterprocessor/go.mod new file mode 100644 index 000000000..f33882c3f --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/go.mod @@ -0,0 +1,65 @@ +module github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor + +go 1.23.0 + +require ( + go.opentelemetry.io/collector/component v0.106.0 + go.opentelemetry.io/collector/consumer v0.106.0 + go.opentelemetry.io/collector/pdata v1.12.0 + go.opentelemetry.io/collector/processor v0.106.0 + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.uber.org/zap v1.27.0 +) + +require ( + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/confmap v0.106.0 + go.opentelemetry.io/collector/consumer/consumertest v0.106.0 + go.uber.org/goleak v1.3.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/knadh/koanf/providers/confmap v0.1.0 // indirect + github.com/knadh/koanf/v2 v2.1.1 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector v0.106.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.106.0 // indirect + go.opentelemetry.io/collector/consumer/consumerprofiles v0.106.0 // indirect + go.opentelemetry.io/collector/featuregate v1.12.0 // indirect + go.opentelemetry.io/collector/internal/globalgates v0.106.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.106.0 // indirect + go.opentelemetry.io/collector/pdata/testdata v0.106.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.50.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/go.sum b/collector/processors/odigossourcetodestinationfilterprocessor/go.sum new file mode 100644 index 000000000..d47ec5d45 --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/go.sum @@ -0,0 +1,152 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +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/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= +github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= +github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= +github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= +github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/collector v0.106.0 h1:SeKdfCT84j1P2GlfWxNPpuGl4qBRX641fxC6ONeGsGQ= +go.opentelemetry.io/collector v0.106.0/go.mod h1:qm5bE3SaL95zwV/hL8Su170GMUGKNDfNL72wJ+87xD8= +go.opentelemetry.io/collector/component v0.106.0 h1:2CJbqV8taL7nBKuHxeULvJySa4+gfsgvAENb+lS/vYM= +go.opentelemetry.io/collector/component v0.106.0/go.mod h1:lAcHA7k8WVzJRxlkOtOWqgf9+2uRJLfLUiYJapRbr5w= +go.opentelemetry.io/collector/config/configtelemetry v0.106.0 h1:UEUO2pkLpPoAi92MoLhVu3q7mVZONFAxAGorJCtF+aE= +go.opentelemetry.io/collector/config/configtelemetry v0.106.0/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= +go.opentelemetry.io/collector/confmap v0.106.0 h1:oZ/QfGjtOTz6sEbNkp8CxSwDFRHXej8u6MywvhTzjqI= +go.opentelemetry.io/collector/confmap v0.106.0/go.mod h1:X+nvuiQs3zdeXKkrEX1Ta3R49eLZ2/NYZLs3KUp1pik= +go.opentelemetry.io/collector/consumer v0.106.0 h1:sXq7AmpqILo6+RaVvuXtHNJNCI7uriVkgCX7zYvysyo= +go.opentelemetry.io/collector/consumer v0.106.0/go.mod h1:a1pWsQC+Gw8WbLVN4Tk+y1FzU5dTX27C04WrLiBGDmw= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.106.0 h1:RjjHTkfo4FkATyZ5drzigXpcWjUOoPpwdsPxMcE3wWc= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.106.0/go.mod h1:CEjdD/YIK9eh0CCLFfD330Zf3upMM0JkRmi9IVjiMnI= +go.opentelemetry.io/collector/consumer/consumertest v0.106.0 h1:gLBal8/6axYDlUDAwIdQs8tWq5Vp2Qv15OYLATWgVL0= +go.opentelemetry.io/collector/consumer/consumertest v0.106.0/go.mod h1:oLDV3fCrndcWxsDGr8iMDejwq9qy0STb0ntfTsHFLfQ= +go.opentelemetry.io/collector/featuregate v1.12.0 h1:l5WbV2vMQd2bL8ubfGrbKNtZaeJRckE12CTHvRe47Tw= +go.opentelemetry.io/collector/featuregate v1.12.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= +go.opentelemetry.io/collector/internal/globalgates v0.106.0 h1:Rg6ZM2DROO4nx93nEFoNInisUGLHBq4IAU0oK1/T7jw= +go.opentelemetry.io/collector/internal/globalgates v0.106.0/go.mod h1:Z5US6O2xkZAtxVSSBnHAPFZwPhFoxlyKLUvS67Vx4gc= +go.opentelemetry.io/collector/pdata v1.12.0 h1:Xx5VK1p4VO0md8MWm2icwC1MnJ7f8EimKItMWw46BmA= +go.opentelemetry.io/collector/pdata v1.12.0/go.mod h1:MYeB0MmMAxeM0hstCFrCqWLzdyeYySim2dG6pDT6nYI= +go.opentelemetry.io/collector/pdata/pprofile v0.106.0 h1:LvZD2ARCBmPUGghXaHDD+lD3tnS60pytYh3lp1fH/SU= +go.opentelemetry.io/collector/pdata/pprofile v0.106.0/go.mod h1:chr7lMJIzyXkccnPRkIPhyXtqLZLSReZYhwsggOGEfg= +go.opentelemetry.io/collector/pdata/testdata v0.106.0 h1:LNVLj+r/rPWOniDtoFCJAWJ1ShlDiFkB5N5Ui/K9kSE= +go.opentelemetry.io/collector/pdata/testdata v0.106.0/go.mod h1:2TXzn9v74UPUH+ktzioP6cFaFd6/E8TDe8NVPyA4CiA= +go.opentelemetry.io/collector/processor v0.106.0 h1:9LjBUk/ILbhww2BHG0Om8vYR2aQoBJrJjhAwJ0x2V4U= +go.opentelemetry.io/collector/processor v0.106.0/go.mod h1:fc1mKAaG0YsP/wa+PWisiLqJPVVsH+42fLQBhPqhfMo= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= +go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= +go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go b/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go new file mode 100644 index 000000000..2a3ee7781 --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go @@ -0,0 +1,15 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" +) + +var ( + Type = component.MustNewType("odigossourcetodestinationfilterprocessor") +) + +const ( + TracesStability = component.StabilityLevelBeta +) diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml b/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml new file mode 100644 index 000000000..21c1b03d9 --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml @@ -0,0 +1,12 @@ +type: odigossourcetodestinationfilterprocessor + +status: + class: processor + stability: + beta: + - traces + distributions: + - contrib + codeowners: + active: + - along diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/processor.go b/collector/processors/odigossourcetodestinationfilterprocessor/processor.go new file mode 100644 index 000000000..aaa4c007d --- /dev/null +++ b/collector/processors/odigossourcetodestinationfilterprocessor/processor.go @@ -0,0 +1,51 @@ +package odigossourcetodestinationfilterprocessor + +import ( + "context" + + "go.opentelemetry.io/collector/pdata/ptrace" + "go.uber.org/zap" +) + +type filterProcessor struct { + logger *zap.Logger + config *Config +} + +func (fp *filterProcessor) processTraces(ctx context.Context, td ptrace.Traces) (ptrace.Traces, error) { + rspans := td.ResourceSpans() + + for i := 0; i < rspans.Len(); i++ { + resourceSpan := rspans.At(i) + ilSpans := resourceSpan.ScopeSpans() + + for j := 0; j < ilSpans.Len(); j++ { + scopeSpan := ilSpans.At(j) + spans := scopeSpan.Spans() + + spans.RemoveIf(func(span ptrace.Span) bool { + return !fp.matches(span, resourceSpan) + }) + } + } + + return td, nil +} + +func (fp *filterProcessor) matches(span ptrace.Span, resourceSpan ptrace.ResourceSpans) bool { + attributes := resourceSpan.Resource().Attributes() + + name, _ := attributes.Get("name") + namespace, _ := attributes.Get("namespace") + kind, _ := attributes.Get("kind") + + for _, condition := range fp.config.MatchConditions { + if name.AsString() == condition.Name && + namespace.AsString() == condition.Namespace && + kind.AsString() == condition.Kind { + return true + } + } + + return false +} From 976ad4a69b1e780fc0f925755fa9ddf11f99dc21 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Mon, 30 Dec 2024 10:08:42 +0200 Subject: [PATCH 087/259] chore: build collector config --- collector/builder-config.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/collector/builder-config.yaml b/collector/builder-config.yaml index 1329edcf4..c213ec28a 100644 --- a/collector/builder-config.yaml +++ b/collector/builder-config.yaml @@ -85,6 +85,7 @@ processors: - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor v0.106.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/remotetapprocessor v0.106.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/odigos/processor/odigostrafficmetrics v0.106.0 + - gomod: github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor v0.106.0 receivers: - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.106.0 @@ -119,4 +120,5 @@ replaces: - github.com/open-telemetry/opentelemetry-collector-contrib/odigos/exporter/googlecloudstorageexporter => ../exporters/googlecloudstorageexporter - github.com/open-telemetry/opentelemetry-collector-contrib/odigos/exporter/mockdestinationexporter => ../exporters/mockdestinationexporter - github.com/open-telemetry/opentelemetry-collector-contrib/odigos/processor/odigostrafficmetrics => ../processors/odigostrafficmetrics - - go.opentelemetry.io/collector/odigos/providers/odigosfileprovider => ../providers/odigosfileprovider \ No newline at end of file + - go.opentelemetry.io/collector/odigos/providers/odigosfileprovider => ../providers/odigosfileprovider + - github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor => ../processors/odigossourcetodestinationfilterprocessor From 638160edb33a34883fce92ebd5d9ba96d9096dee Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 10:33:05 +0200 Subject: [PATCH 088/259] feat: add CRD types and improve error message formatting --- common/consts/consts.go | 6 ++++++ frontend/kube/watchers/batcher.go | 2 +- frontend/services/sources.go | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/common/consts/consts.go b/common/consts/consts.go index f053c7b61..a98d34179 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -29,9 +29,15 @@ const ( // or odigos is uninstalled. // Should only be used for environment variables that are modified by odigos. ManifestEnvOriginalValAnnotation = "odigos.io/manifest-env-original-val" + // Used to label instrumentation instances by the corresponding // instrumented app for better query performance. InstrumentedAppNameLabel = "instrumented-app" + + // CRD types + InstrumentationConfig = "InstrumentationConfig" + InstrumentationInstance = "InstrumentationInstance" + Destination = "Destination" ) var ( diff --git a/frontend/kube/watchers/batcher.go b/frontend/kube/watchers/batcher.go index a0aa74cf2..5cf020319 100644 --- a/frontend/kube/watchers/batcher.go +++ b/frontend/kube/watchers/batcher.go @@ -32,7 +32,7 @@ type EventBatcher struct { } type EventBatcherConfig struct { - // Event to batch, not configuring this value (empty string) will cause to all events to be batched + // Event to batch, not configuring this value (empty string) will cause all events to be batched Event sse.MessageEvent // Message type to batch, not configuring this value (empty string) will cause all messages to be batched MessageType sse.MessageType diff --git a/frontend/services/sources.go b/frontend/services/sources.go index c0dfb0f34..fc53b250f 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -311,7 +311,7 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, fmt.Errorf(`source "%s" not found`, workloadName) } if len(source.Items) > 1 { - return nil, fmt.Errorf(`expected to get 1 source "%s", got %s`, workloadName, fmt.Sprint(len(source.Items))) + return nil, fmt.Errorf(`expected to get 1 source "%s", got %d`, workloadName, len(source.Items)) } crdName := source.Items[0].Name From afcf5bab4e3b69b58444c0130fe51fd328383725 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 10:34:05 +0200 Subject: [PATCH 089/259] refactor: improve event handling and message formatting for destination and instrumentation config watchers --- frontend/kube/watchers/destination_watcher.go | 70 ++++++++++++------- .../instrumentation_config_watcher.go | 32 +++++---- .../instrumentation_instance_watcher.go | 42 +++++------ 3 files changed, 78 insertions(+), 66 deletions(-) diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index c08058405..a7c348eb8 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -3,9 +3,9 @@ package watchers import ( "context" "fmt" - "log" "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/endpoints/sse" "github.com/odigos-io/odigos/frontend/kube" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -35,50 +35,66 @@ func handleDestinationWatchEvents(ctx context.Context, watcher watch.Interface) } switch event.Type { case watch.Added: - handleAddedDestination(event) + handleAddedDestination(event.Object.(*v1alpha1.Destination)) case watch.Modified: - handleModifiedDestination(event) + handleModifiedDestination(event.Object.(*v1alpha1.Destination)) case watch.Deleted: - handleDeletedDestination(event) - default: - log.Printf("unexpected type: %T", event.Object) + handleDeletedDestination(event.Object.(*v1alpha1.Destination)) } } } } -func handleAddedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventAdded, "Destination", "error type assertion") +func handleAddedDestination(destination *v1alpha1.Destination) { + name := destination.Spec.DestinationName + if name == "" { + name = string(destination.Spec.Type) } - data := fmt.Sprintf("Destination %s created", destination.Spec.DestinationName) - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventAdded, Type: "success", Target: destination.Name, Data: data, CRDType: "Destination"}) + + data := fmt.Sprintf(`Successfully added "%s" %s`, name, consts.Destination) + sse.SendMessageToClient(sse.SSEMessage{ + Type: sse.MessageTypeSuccess, + Event: sse.MessageEventAdded, + Data: data, + CRDType: consts.Destination, + Target: destination.Name, + }) } -func handleModifiedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventModified, "Destination", "error type assertion") - } - if len(destination.Status.Conditions) == 0 { +func handleModifiedDestination(destination *v1alpha1.Destination) { + length := len(destination.Status.Conditions) + if length == 0 { return } - lastCondition := destination.Status.Conditions[len(destination.Status.Conditions)-1] + lastCondition := destination.Status.Conditions[length-1] data := lastCondition.Message conditionType := sse.MessageTypeSuccess - if lastCondition.Status == "False" { + if lastCondition.Status == metav1.ConditionFalse { conditionType = sse.MessageTypeError } - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventModified, Type: conditionType, Target: destination.Name, Data: data, CRDType: "Destination"}) + + sse.SendMessageToClient(sse.SSEMessage{ + Type: conditionType, + Event: sse.MessageEventModified, + Data: data, + CRDType: consts.Destination, + Target: destination.Name, + }) } -func handleDeletedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventDeleted, "Destination", "error type assertion") +func handleDeletedDestination(destination *v1alpha1.Destination) { + name := destination.Spec.DestinationName + if name == "" { + name = string(destination.Spec.Type) } - data := fmt.Sprintf("Destination %s deleted successfully", destination.Spec.DestinationName) - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventDeleted, Type: "success", Target: "", Data: data, CRDType: "Destination"}) + + data := fmt.Sprintf(`Successfully removed "%s" %s`, name, consts.Destination) + sse.SendMessageToClient(sse.SSEMessage{ + Type: sse.MessageTypeSuccess, + Event: sse.MessageEventDeleted, + Data: data, + CRDType: consts.Destination, + Target: destination.Name, + }) } diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index b762d585a..9b230a1e3 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -3,8 +3,10 @@ package watchers import ( "context" "fmt" + "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/endpoints/sse" "github.com/odigos-io/odigos/frontend/kube" commonutils "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -18,33 +20,37 @@ var deletedEventBatcher *EventBatcher func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { addedEventBatcher = NewEventBatcher( EventBatcherConfig{ - Event: sse.MessageEventAdded, - CRDType: "InstrumentationConfig", + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventAdded, + CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("successfully added %d sources", count) + return fmt.Sprintf("Successfully added %d sources", count) }, FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("failed to add %d sources", count) + return fmt.Sprintf("Failed to add %d sources", count) }, }, ) deletedEventBatcher = NewEventBatcher( EventBatcherConfig{ - Event: sse.MessageEventDeleted, - CRDType: "InstrumentationConfig", + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventDeleted, + CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("successfully deleted %d sources", count) + return fmt.Sprintf("Successfully removed %d sources", count) }, FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("failed to delete %d sources", count) + return fmt.Sprintf("Failed to remove %d sources", count) }, }, ) watcher, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { - return fmt.Errorf("error creating watcher: %v", err) + return fmt.Errorf("error creating watcher: %w", err) } go handleInstrumentationConfigWatchEvents(ctx, watcher) @@ -78,12 +84,12 @@ func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { - genericErrorMessage(sse.MessageEventAdded, "InstrumentationConfig", err.Error()) + genericErrorMessage(sse.MessageEventAdded, consts.InstrumentationConfig, err.Error()) return } target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf("InstrumentationConfig %s created successfully", name) + data := fmt.Sprintf(`Successfully added "%s" source`, name) addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } @@ -91,11 +97,11 @@ func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { - genericErrorMessage(sse.MessageEventDeleted, "InstrumentationConfig", err.Error()) + genericErrorMessage(sse.MessageEventDeleted, consts.InstrumentationConfig, err.Error()) return } target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf("InstrumentationConfig %s deleted successfully", name) + data := fmt.Sprintf(`Successfully removed "%s" source`, name) deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index feb1e7b4d..412fdbbf1 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -19,10 +19,11 @@ var modifiedBatcher *EventBatcher func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) error { modifiedBatcher = NewEventBatcher( EventBatcherConfig{ - Event: sse.MessageEventModified, - MessageType: sse.MessageTypeError, - Duration: 10 * time.Second, - CRDType: "InstrumentationInstance", + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventModified, + MessageType: sse.MessageTypeError, + CRDType: consts.InstrumentationInstance, FailureBatchMessageFunc: func(batchSize int, crd string) string { return fmt.Sprintf("Failed to instrument %d instances", batchSize) }, @@ -51,48 +52,37 @@ func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch } switch event.Type { case watch.Modified: - handleModifiedInstrumentationInstance(event) + handleModifiedInstrumentationInstance(event.Object.(*v1alpha1.InstrumentationInstance)) } } } } -func handleModifiedInstrumentationInstance(event watch.Event) { - instrumentedInstance, ok := event.Object.(*v1alpha1.InstrumentationInstance) - if !ok { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error type assertion") - } - healthy := instrumentedInstance.Status.Healthy - - if healthy == nil { - return - } - - if *healthy { +func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.InstrumentationInstance) { + healthy := instruInsta.Status.Healthy + if healthy == nil || *healthy { // send notification to frontend only if the instance is not healthy return } - labels := instrumentedInstance.GetLabels() + labels := instruInsta.GetLabels() if labels == nil { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting labels") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting labels") } instrumentedAppName, ok := labels[consts.InstrumentedAppNameLabel] if !ok { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting instrumented app name from labels") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting instrumented app name from labels") } + namespace := instruInsta.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedAppName) if err != nil { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting workload info") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting workload info") } - namespace := instrumentedInstance.Namespace - target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf("%s %s", instrumentedInstance.Status.Reason, instrumentedInstance.Status.Message) - - fmt.Printf("InstrumentationInstance %s modified\n", name) + data := fmt.Sprintf("%s %s", instruInsta.Status.Reason, instruInsta.Status.Message) + fmt.Printf("%s %s modified\n", consts.InstrumentationInstance, name) modifiedBatcher.AddEvent(sse.MessageTypeError, data, target) } From 83289a350e9033a99066f38f99cdff5c3079d9b9 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 10:39:30 +0200 Subject: [PATCH 090/259] fix: correct message formatting in SSEMessage and improve error handling in instrumentation watcher --- frontend/kube/watchers/batcher.go | 4 ++-- frontend/kube/watchers/common.go | 10 +++++----- .../kube/watchers/instrumentation_instance_watcher.go | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/kube/watchers/batcher.go b/frontend/kube/watchers/batcher.go index 5cf020319..ae50a9d3e 100644 --- a/frontend/kube/watchers/batcher.go +++ b/frontend/kube/watchers/batcher.go @@ -83,11 +83,11 @@ func (eb *EventBatcher) AddEvent(msgType sse.MessageType, data, target string) e defer eb.mu.Unlock() message := sse.SSEMessage{ - Event: eb.config.Event, Type: msgType, - Target: target, + Event: eb.config.Event, Data: data, CRDType: eb.config.CRDType, + Target: target, } eb.batch = append(eb.batch, message) diff --git a/frontend/kube/watchers/common.go b/frontend/kube/watchers/common.go index bf4343c84..ebc70c4cd 100644 --- a/frontend/kube/watchers/common.go +++ b/frontend/kube/watchers/common.go @@ -6,10 +6,10 @@ import ( func genericErrorMessage(event sse.MessageEvent, crd string, data string) { sse.SendMessageToClient(sse.SSEMessage{ - Event: event, - Type: sse.MessageTypeError, - Target: "", - Data: "Something went wrong: " + data, + Type: sse.MessageTypeError, + Event: event, + Data: "Something went wrong: " + data, CRDType: crd, + Target: "", }) -} \ No newline at end of file +} diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 412fdbbf1..2af8df875 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -78,7 +78,7 @@ func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.Instrumentation namespace := instruInsta.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedAppName) if err != nil { - genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting workload info") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, err.Error()) } target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) From 01acd833ff6d62b83fb044a8aae8c43452b4ec3a Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 11:11:06 +0200 Subject: [PATCH 091/259] fix: improve message formatting for destination and instrumentation events --- autoscaler/controllers/gateway/configmap.go | 2 +- frontend/kube/watchers/batcher.go | 8 ++++---- frontend/kube/watchers/destination_watcher.go | 11 +++++++---- .../kube/watchers/instrumentation_config_watcher.go | 12 ++++++------ .../watchers/instrumentation_instance_watcher.go | 5 ++--- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/autoscaler/controllers/gateway/configmap.go b/autoscaler/controllers/gateway/configmap.go index c94c90327..f4070571f 100644 --- a/autoscaler/controllers/gateway/configmap.go +++ b/autoscaler/controllers/gateway/configmap.go @@ -150,7 +150,7 @@ func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.Proc logger.Error(err, "Failed to update destination error status conditions") } } else { - err := odgiosK8s.UpdateStatusConditions(ctx, c, &dest, &dest.Status.Conditions, metav1.ConditionTrue, destinationConfiguredType, "TransformedToOtelcolConfig", "destination successfully transformed to otelcol configuration") + err := odgiosK8s.UpdateStatusConditions(ctx, c, &dest, &dest.Status.Conditions, metav1.ConditionTrue, destinationConfiguredType, "TransformedToOtelcolConfig", "Destination successfully transformed to otelcol configuration") if err != nil { logger.Error(err, "Failed to update destination success status conditions") } diff --git a/frontend/kube/watchers/batcher.go b/frontend/kube/watchers/batcher.go index ae50a9d3e..18e4dac03 100644 --- a/frontend/kube/watchers/batcher.go +++ b/frontend/kube/watchers/batcher.go @@ -141,21 +141,21 @@ func (eb *EventBatcher) prepareBatchMessage() []sse.SSEMessage { var result []sse.SSEMessage if successCount > 0 { result = append(result, sse.SSEMessage{ - Event: eb.config.Event, Type: sse.MessageTypeSuccess, - Target: "", + Event: eb.config.Event, Data: eb.config.SuccessBatchMessageFunc(successCount, eb.config.CRDType), CRDType: eb.config.CRDType, + Target: "", }) } if failureCount > 0 { result = append(result, sse.SSEMessage{ - Event: eb.config.Event, Type: sse.MessageTypeError, - Target: "", + Event: eb.config.Event, Data: eb.config.FailureBatchMessageFunc(failureCount, eb.config.CRDType), CRDType: eb.config.CRDType, + Target: "", }) } return result diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index a7c348eb8..a12b07488 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -51,7 +51,7 @@ func handleAddedDestination(destination *v1alpha1.Destination) { name = string(destination.Spec.Type) } - data := fmt.Sprintf(`Successfully added "%s" %s`, name, consts.Destination) + data := fmt.Sprintf(`%s "%s" created`, consts.Destination, name) sse.SendMessageToClient(sse.SSEMessage{ Type: sse.MessageTypeSuccess, Event: sse.MessageEventAdded, @@ -69,8 +69,11 @@ func handleModifiedDestination(destination *v1alpha1.Destination) { lastCondition := destination.Status.Conditions[length-1] data := lastCondition.Message - conditionType := sse.MessageTypeSuccess - if lastCondition.Status == metav1.ConditionFalse { + + conditionType := sse.MessageTypeInfo + if lastCondition.Status == metav1.ConditionTrue { + conditionType = sse.MessageTypeSuccess + } else if lastCondition.Status == metav1.ConditionFalse { conditionType = sse.MessageTypeError } @@ -89,7 +92,7 @@ func handleDeletedDestination(destination *v1alpha1.Destination) { name = string(destination.Spec.Type) } - data := fmt.Sprintf(`Successfully removed "%s" %s`, name, consts.Destination) + data := fmt.Sprintf(`%s "%s" deleted`, consts.Destination, name) sse.SendMessageToClient(sse.SSEMessage{ Type: sse.MessageTypeSuccess, Event: sse.MessageEventDeleted, diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index 9b230a1e3..feccf2f67 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -25,10 +25,10 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er Event: sse.MessageEventAdded, CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("Successfully added %d sources", count) + return fmt.Sprintf("Successfully created %d sources", count) }, FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("Failed to add %d sources", count) + return fmt.Sprintf("Failed to create %d sources", count) }, }, ) @@ -40,10 +40,10 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er Event: sse.MessageEventDeleted, CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("Successfully removed %d sources", count) + return fmt.Sprintf("Successfully deleted %d sources", count) }, FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("Failed to remove %d sources", count) + return fmt.Sprintf("Failed to delete %d sources", count) }, }, ) @@ -89,7 +89,7 @@ func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { } target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf(`Successfully added "%s" source`, name) + data := fmt.Sprintf(`Source "%s" created`, name) addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } @@ -102,6 +102,6 @@ func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { } target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf(`Successfully removed "%s" source`, name) + data := fmt.Sprintf(`Source "%s" deleted`, name) deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 2af8df875..15dddf9ab 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -21,8 +21,8 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, - Event: sse.MessageEventModified, MessageType: sse.MessageTypeError, + Event: sse.MessageEventModified, CRDType: consts.InstrumentationInstance, FailureBatchMessageFunc: func(batchSize int, crd string) string { return fmt.Sprintf("Failed to instrument %d instances", batchSize) @@ -82,7 +82,6 @@ func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.Instrumentation } target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf("%s %s", instruInsta.Status.Reason, instruInsta.Status.Message) - fmt.Printf("%s %s modified\n", consts.InstrumentationInstance, name) + data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInsta.Status.Reason, instruInsta.Status.Message) modifiedBatcher.AddEvent(sse.MessageTypeError, data, target) } From 03de9b656e1d66a424e9408fa4a72f3da937913f Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:09:33 +0200 Subject: [PATCH 092/259] refactor: streamline error handling and notification messages across hooks --- .../destinations/add-destination/index.tsx | 5 +- .../webapp/hooks/actions/useActionCRUD.ts | 46 +++++++-------- .../compute-platform/useComputePlatform.ts | 16 +++--- .../hooks/compute-platform/useNamespace.ts | 17 +++--- .../hooks/destinations/useDestinationCRUD.ts | 46 +++++---------- .../destinations/useDestinationFormData.ts | 2 +- .../useInstrumentationRuleCRUD.ts | 56 ++++++++----------- frontend/webapp/hooks/notification/useSSE.ts | 2 +- .../webapp/hooks/sources/useSourceCRUD.ts | 36 ++++++------ 9 files changed, 95 insertions(+), 131 deletions(-) diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index 9720c7bb2..a1bb1530e 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -61,12 +61,11 @@ export function AddDestinationContainer() { await createSources(configuredSources, configuredFutureApps); await Promise.all(configuredDestinations.map(async ({ form }) => await createDestination(form))); - // Delay redirect by 3 seconds to allow the sources to be created on the backend 1st, - // otherwise we would have to apply polling on the overview page on every mount. + // Delay redirect by 1 seconds to allow the data to reach the backend 1st setTimeout(() => { resetState(); router.push(ROUTES.OVERVIEW); - }, 3000); + }, 1000); }; const isSourcesListEmpty = () => !Object.values(configuredSources).some((sources) => !!sources.length); diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index f261ee47e..695b78a51 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -25,52 +25,48 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + const handleComplete = (actionType: string) => { refetch(); - params?.onSuccess?.(title); + params?.onSuccess?.(actionType); }; const [createAction, cState] = useMutation<{ createAction: { id: string } }>(CREATE_ACTION, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: (res, req) => { - const id = res.createAction.id; - const type = req?.variables?.action.type; - const name = req?.variables?.action.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `action "${label}" was created`, id); - }, + onCompleted: () => handleComplete(ACTION.CREATE), }); const [updateAction, uState] = useMutation<{ updateAction: { id: string } }>(UPDATE_ACTION, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = res.updateAction.id; - const type = req?.variables?.action.type; - const name = req?.variables?.action.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `action "${label}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); const [deleteAction, dState] = useMutation<{ deleteAction: boolean }>(DELETE_ACTION, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.id; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.ACTION)); - handleComplete(ACTION.DELETE, `action "${id}" was deleted`); + handleComplete(ACTION.DELETE); }, }); return { loading: cState.loading || uState.loading || dState.loading, - actions: data?.computePlatform.actions || [], + actions: data?.computePlatform?.actions || [], - createAction: (action: ActionInput) => createAction({ variables: { action } }), - updateAction: (id: string, action: ActionInput) => updateAction({ variables: { id, action } }), - deleteAction: (id: string, actionType: ActionsType) => deleteAction({ variables: { id, actionType } }), + createAction: (action: ActionInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating pipeline action...'); + createAction({ variables: { action } }); + }, + updateAction: (id: string, action: ActionInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating pipeline action...'); + updateAction({ variables: { id, action } }); + }, + deleteAction: (id: string, actionType: ActionsType) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting pipeline action...'); + deleteAction({ variables: { id, actionType } }); + }, }; }; diff --git a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts index d9597d5cd..8af6b93de 100644 --- a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts +++ b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts @@ -1,9 +1,9 @@ -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { useQuery } from '@apollo/client'; import { useNotificationStore } from '@/store'; import { GET_COMPUTE_PLATFORM } from '@/graphql'; import { useFilterStore } from '@/store/useFilterStore'; -import { BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils'; +import { ACTION, BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils'; import { NOTIFICATION_TYPE, type ActionItem, type ComputePlatform, type ComputePlatformMapped } from '@/types'; type UseComputePlatformHook = { @@ -15,19 +15,17 @@ type UseComputePlatformHook = { }; export const useComputePlatform = (): UseComputePlatformHook => { - const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM); const { addNotification } = useNotificationStore(); const filters = useFilterStore(); - useEffect(() => { - if (error) { + const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM, { + onError: (error) => addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: error.name, + title: error.name || ACTION.FETCH, message: error.cause?.message, - }); - } - }, [error]); + }), + }); const mappedData = useMemo(() => { if (!data) return undefined; diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index fe95b9ea5..3b7df92f8 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -1,3 +1,4 @@ +import { ACTION } from '@/utils'; import { useNotificationStore } from '@/store'; import { useMutation, useQuery } from '@apollo/client'; import { useComputePlatform } from './useComputePlatform'; @@ -8,14 +9,6 @@ export const useNamespace = (namespaceName?: string, instrumentationLabeled = nu const { addNotification } = useNotificationStore(); const cp = useComputePlatform(); - const handleError = (title: string, message: string) => { - addNotification({ type: NOTIFICATION_TYPE.ERROR, title, message }); - }; - - const handleComplete = (title: string, message: string) => { - addNotification({ type: NOTIFICATION_TYPE.SUCCESS, title, message }); - }; - const { data, loading, error } = useQuery(GET_NAMESPACES, { skip: !namespaceName, fetchPolicy: 'cache-first', @@ -23,8 +16,12 @@ export const useNamespace = (namespaceName?: string, instrumentationLabeled = nu }); const [persistNamespaceMutation] = useMutation(PERSIST_NAMESPACE, { - onError: (error) => handleError('', error.message), - onCompleted: (res, req) => {}, + onError: (error) => + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: error.name || ACTION.FETCH, + message: error.message, + }), }); return { diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 4870b6617..31eeb1c34 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -25,63 +25,47 @@ export const useDestinationCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + const handleComplete = (actionType: string) => { refetch(); - params?.onSuccess?.(title); + params?.onSuccess?.(actionType); }; - const [createDestination, cState] = useMutation<{ - createNewDestination: { id: string }; - }>(CREATE_DESTINATION, { + const [createDestination, cState] = useMutation<{ createNewDestination: { id: string } }>(CREATE_DESTINATION, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: (res, req) => { - const id = res.createNewDestination.id; - const type = req?.variables?.destination.type; - const name = req?.variables?.destination.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `destination "${label}" was created`, id); - }, + onCompleted: () => handleComplete(ACTION.CREATE), }); - const [updateDestination, uState] = useMutation<{ - updateDestination: { id: string }; - }>(UPDATE_DESTINATION, { + const [updateDestination, uState] = useMutation<{ updateDestination: { id: string } }>(UPDATE_DESTINATION, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = res.updateDestination.id; - const type = req?.variables?.destination.type; - const name = req?.variables?.destination.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `destination "${label}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); - const [deleteDestination, dState] = useMutation<{ - deleteDestination: boolean; - }>(DELETE_DESTINATION, { + const [deleteDestination, dState] = useMutation<{ deleteDestination: boolean }>(DELETE_DESTINATION, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.id; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.DESTINATION)); - handleComplete(ACTION.DELETE, `destination "${id}" was deleted`); + handleComplete(ACTION.DELETE); }, }); return { loading: cState.loading || uState.loading || dState.loading, - destinations: data?.computePlatform.destinations || [], + destinations: data?.computePlatform?.destinations || [], createDestination: (destination: DestinationInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating destination...'); createDestination({ variables: { destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, updateDestination: (id: string, destination: DestinationInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating destination...'); updateDestination({ variables: { id, destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, deleteDestination: (id: string) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting destination...'); deleteDestination({ variables: { id } }); }, }; diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 8fbb73bb9..1e230085b 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -104,7 +104,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp onError: (error) => addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: ACTION.FETCH, + title: error.name || ACTION.FETCH, message: error.message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, }), diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 407c8961b..6c8718365 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -1,7 +1,7 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; +import { ACTION, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; -import { ACTION, deriveTypeFromRule, getSseTargetFromId } from '@/utils'; import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput } from '@/types'; import { CREATE_INSTRUMENTATION_RULE, UPDATE_INSTRUMENTATION_RULE, DELETE_INSTRUMENTATION_RULE } from '@/graphql/mutations'; @@ -25,58 +25,48 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); + const handleError = (title: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, title, message); params?.onError?.(title); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + const handleComplete = (title: string) => { refetch(); params?.onSuccess?.(title); }; - const [createInstrumentationRule, cState] = useMutation<{ - createInstrumentationRule: { ruleId: string }; - }>(CREATE_INSTRUMENTATION_RULE, { + const [createInstrumentationRule, cState] = useMutation<{ createInstrumentationRule: { ruleId: string } }>(CREATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: (res, req) => { - const id = res.createInstrumentationRule.ruleId; - const type = deriveTypeFromRule(req?.variables?.instrumentationRule); - const name = req?.variables?.instrumentationRule.ruleName; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `instrumentation rule "${label}" was created`, id); - }, + onCompleted: () => handleComplete(ACTION.CREATE), }); - const [updateInstrumentationRule, uState] = useMutation<{ - updateInstrumentationRule: { ruleId: string }; - }>(UPDATE_INSTRUMENTATION_RULE, { + const [updateInstrumentationRule, uState] = useMutation<{ updateInstrumentationRule: { ruleId: string } }>(UPDATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = res.updateInstrumentationRule.ruleId; - const type = deriveTypeFromRule(req?.variables?.instrumentationRule); - const name = req?.variables?.instrumentationRule.ruleName; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `instrumentation rule "${label}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); - const [deleteInstrumentationRule, dState] = useMutation<{ - deleteInstrumentationRule: boolean; - }>(DELETE_INSTRUMENTATION_RULE, { + const [deleteInstrumentationRule, dState] = useMutation<{ deleteInstrumentationRule: boolean }>(DELETE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.ruleId; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.RULE)); - handleComplete(ACTION.DELETE, `instrumentation rule "${id}" was deleted`); + handleComplete(ACTION.DELETE); }, }); return { loading: cState.loading || uState.loading || dState.loading, - instrumentationRules: data?.computePlatform.instrumentationRules || [], + instrumentationRules: data?.computePlatform?.instrumentationRules || [], - createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => createInstrumentationRule({ variables: { instrumentationRule } }), - updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }), - deleteInstrumentationRule: (ruleId: string) => deleteInstrumentationRule({ variables: { ruleId } }), + createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating instrumentation rule...'); + createInstrumentationRule({ variables: { instrumentationRule } }); + }, + updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating instrumentation rule...'); + updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }); + }, + deleteInstrumentationRule: (ruleId: string) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting instrumentation rule...'); + deleteInstrumentationRule({ variables: { ruleId } }); + }, }; }; diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts index 4fc506a42..dc69a0e92 100644 --- a/frontend/webapp/hooks/notification/useSSE.ts +++ b/frontend/webapp/hooks/notification/useSSE.ts @@ -40,7 +40,7 @@ export const useSSE = () => { target: data.target, }; - notification.type = modifyType(notification); + // notification.type = modifyType(notification); // Dispatch the notification to the store addNotification(notification); diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index f70c0d706..48b79bd92 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -41,14 +41,13 @@ export const useSourceCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string, id?: WorkloadId) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); + const handleError = (title: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, title, message); params?.onError?.(title); }; - const handleComplete = (title: string, message: string, id?: WorkloadId) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); - startPolling(); + const handleComplete = (title: string) => { + refetch(); params?.onSuccess?.(title); }; @@ -60,31 +59,25 @@ export const useSourceCRUD = (params?: Params) => { handleError(action, error.message); }, onCompleted: (res, req) => { + const count = req?.variables?.sources.length; const namespace = req?.variables?.namespace; const { name, kind, selected } = req?.variables?.sources?.[0] || {}; - - const count = req?.variables?.sources.length; const action = selected ? ACTION.CREATE : ACTION.DELETE; - const fromOrIn = selected ? 'in' : 'from'; if (count > 1) { - handleComplete(action, `${count} sources were ${action.toLowerCase()}d ${fromOrIn} "${namespace}"`); + handleComplete(action); } else { - const id = { kind, name, namespace }; + const id = { namespace, name, kind }; if (!selected) removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE)); if (!selected) setConfiguredSources({ ...configuredSources, [namespace]: configuredSources[namespace]?.filter((source) => source.name !== name) || [] }); - handleComplete(action, `source "${name}" was ${action.toLowerCase()}d ${fromOrIn} "${namespace}"`, selected ? id : undefined); + handleComplete(action); } }, }); const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = req?.variables?.sourceId; - const name = id?.name; - handleComplete(ACTION.UPDATE, `source "${name}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); const persistNamespaces = async (items: { [key: string]: boolean }) => { @@ -113,10 +106,17 @@ export const useSourceCRUD = (params?: Params) => { sources: data?.computePlatform.k8sActualSources || [], createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating sources...'); await persistNamespaces(futureSelectAppsList); await persistSources(selectAppsList, true); }, - updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => await updateSource({ variables: { sourceId, patchSourceRequest } }), - deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => await persistSources(selectAppsList, false), + updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating sources...'); + await updateSource({ variables: { sourceId, patchSourceRequest } }); + }, + deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting sources...'); + await persistSources(selectAppsList, false); + }, }; }; From 8a6e8456fbdeb3a66d7c4268ea03fc5c588234e8 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:11:24 +0200 Subject: [PATCH 093/259] refactor: rename event batcher variables for instrumentation config and instance watchers --- .../watchers/instrumentation_config_watcher.go | 16 ++++++++-------- .../instrumentation_instance_watcher.go | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index feccf2f67..c09107fe0 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -14,11 +14,11 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var addedEventBatcher *EventBatcher -var deletedEventBatcher *EventBatcher +var instruConfigAddedEventBatcher *EventBatcher +var instruConfigDeletedEventBatcher *EventBatcher func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { - addedEventBatcher = NewEventBatcher( + instruConfigAddedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -33,7 +33,7 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er }, ) - deletedEventBatcher = NewEventBatcher( + instruConfigDeletedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -59,8 +59,8 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer addedEventBatcher.Cancel() - defer deletedEventBatcher.Cancel() + defer instruConfigAddedEventBatcher.Cancel() + defer instruConfigDeletedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -90,7 +90,7 @@ func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) data := fmt.Sprintf(`Source "%s" created`, name) - addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) + instruConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { @@ -103,5 +103,5 @@ func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) data := fmt.Sprintf(`Source "%s" deleted`, name) - deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) + instruConfigDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 15dddf9ab..60b858771 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -14,10 +14,10 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var modifiedBatcher *EventBatcher +var instruInstanceModifiedBatcher *EventBatcher func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) error { - modifiedBatcher = NewEventBatcher( + instruInstanceModifiedBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -40,7 +40,7 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer modifiedBatcher.Cancel() + defer instruInstanceModifiedBatcher.Cancel() for { select { case <-ctx.Done(): @@ -58,14 +58,14 @@ func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch } } -func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.InstrumentationInstance) { - healthy := instruInsta.Status.Healthy +func handleModifiedInstrumentationInstance(instruInstance *v1alpha1.InstrumentationInstance) { + healthy := instruInstance.Status.Healthy if healthy == nil || *healthy { // send notification to frontend only if the instance is not healthy return } - labels := instruInsta.GetLabels() + labels := instruInstance.GetLabels() if labels == nil { genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting labels") } @@ -75,13 +75,13 @@ func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.Instrumentation genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting instrumented app name from labels") } - namespace := instruInsta.Namespace + namespace := instruInstance.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedAppName) if err != nil { genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, err.Error()) } target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInsta.Status.Reason, instruInsta.Status.Message) - modifiedBatcher.AddEvent(sse.MessageTypeError, data, target) + data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInstance.Status.Reason, instruInstance.Status.Message) + instruInstanceModifiedBatcher.AddEvent(sse.MessageTypeError, data, target) } From fe051d4c57659004b4209d08f5a1c386082876bd Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:20:24 +0200 Subject: [PATCH 094/259] feat: implement event batching for destination and instrumentation instance watchers --- frontend/kube/watchers/destination_watcher.go | 80 ++++++++++++++----- .../instrumentation_instance_watcher.go | 1 + 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index a12b07488..706fd6928 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -3,6 +3,7 @@ package watchers import ( "context" "fmt" + "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" @@ -12,7 +13,56 @@ import ( "k8s.io/apimachinery/pkg/watch" ) +var destinationAddedEventBatcher *EventBatcher +var destinationModifiedBatcher *EventBatcher +var destinationDeletedEventBatcher *EventBatcher + func StartDestinationWatcher(ctx context.Context, namespace string) error { + destinationAddedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventAdded, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Successfully created %d destinations", count) + }, + FailureBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Failed to create %d destinations", count) + }, + }, + ) + + destinationModifiedBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventModified, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Successfully transformed %d destinations to otelcol configuration", batchSize) + }, + FailureBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Failed to transform %d destinations to otelcol configuration", batchSize) + }, + }, + ) + + destinationDeletedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventDeleted, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Successfully deleted %d destinations", count) + }, + FailureBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Failed to delete %d destinations", count) + }, + }, + ) + watcher, err := kube.DefaultClient.OdigosClient.Destinations(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { return fmt.Errorf("error creating watcher: %v", err) @@ -24,6 +74,9 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { func handleDestinationWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() + defer destinationAddedEventBatcher.Cancel() + defer destinationModifiedBatcher.Cancel() + defer destinationDeletedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -51,14 +104,9 @@ func handleAddedDestination(destination *v1alpha1.Destination) { name = string(destination.Spec.Type) } + target := destination.Name data := fmt.Sprintf(`%s "%s" created`, consts.Destination, name) - sse.SendMessageToClient(sse.SSEMessage{ - Type: sse.MessageTypeSuccess, - Event: sse.MessageEventAdded, - Data: data, - CRDType: consts.Destination, - Target: destination.Name, - }) + destinationAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } func handleModifiedDestination(destination *v1alpha1.Destination) { @@ -67,6 +115,7 @@ func handleModifiedDestination(destination *v1alpha1.Destination) { return } + target := destination.Name lastCondition := destination.Status.Conditions[length-1] data := lastCondition.Message @@ -77,13 +126,7 @@ func handleModifiedDestination(destination *v1alpha1.Destination) { conditionType = sse.MessageTypeError } - sse.SendMessageToClient(sse.SSEMessage{ - Type: conditionType, - Event: sse.MessageEventModified, - Data: data, - CRDType: consts.Destination, - Target: destination.Name, - }) + destinationModifiedBatcher.AddEvent(conditionType, data, target) } func handleDeletedDestination(destination *v1alpha1.Destination) { @@ -92,12 +135,7 @@ func handleDeletedDestination(destination *v1alpha1.Destination) { name = string(destination.Spec.Type) } + target := destination.Name data := fmt.Sprintf(`%s "%s" deleted`, consts.Destination, name) - sse.SendMessageToClient(sse.SSEMessage{ - Type: sse.MessageTypeSuccess, - Event: sse.MessageEventDeleted, - Data: data, - CRDType: consts.Destination, - Target: destination.Name, - }) + destinationDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 60b858771..5f3bc3c03 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -29,6 +29,7 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) }, }, ) + watcher, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { return fmt.Errorf("error creating watcher: %v", err) From 0d426c6d72f0e32dc10752bd38da23288f35494f Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:29:34 +0200 Subject: [PATCH 095/259] feat: add hideFromHistory option to notifications and improve error message handling --- frontend/webapp/app/page.tsx | 6 +++--- .../notification/notification-manager.tsx | 3 ++- .../instrumentation-rules/rule-drawer/index.tsx | 9 ++++++++- frontend/webapp/hooks/actions/useActionCRUD.ts | 9 +++++---- frontend/webapp/hooks/actions/useActionFormData.ts | 1 + .../hooks/compute-platform/useComputePlatform.ts | 2 +- .../webapp/hooks/compute-platform/useNamespace.ts | 2 +- .../hooks/destinations/useDestinationCRUD.ts | 9 +++++---- .../hooks/destinations/useDestinationFormData.ts | 3 ++- .../useInstrumentationRuleCRUD.ts | 9 +++++---- .../useInstrumentationRuleFormData.ts | 1 + frontend/webapp/hooks/notification/useSSE.ts | 14 -------------- frontend/webapp/hooks/sources/useSourceCRUD.ts | 9 +++++---- frontend/webapp/types/common.ts | 1 + 14 files changed, 40 insertions(+), 38 deletions(-) diff --git a/frontend/webapp/app/page.tsx b/frontend/webapp/app/page.tsx index 9df649d3c..2dcaf764a 100644 --- a/frontend/webapp/app/page.tsx +++ b/frontend/webapp/app/page.tsx @@ -2,10 +2,10 @@ import { useEffect } from 'react'; import { useConfig } from '@/hooks'; import { CenterThis } from '@/styles'; -import { ROUTES, CONFIG } from '@/utils'; import { NOTIFICATION_TYPE } from '@/types'; import { useRouter } from 'next/navigation'; import { useNotificationStore } from '@/store'; +import { ROUTES, CONFIG, ACTION } from '@/utils'; import { FadeLoader } from '@/reuseable-components'; export default function App() { @@ -17,8 +17,8 @@ export default function App() { if (error) { addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: error.name, - message: error.message, + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, }); } else if (data) { const { installation } = data; diff --git a/frontend/webapp/components/notification/notification-manager.tsx b/frontend/webapp/components/notification/notification-manager.tsx index 0bc4791b1..d90188bdf 100644 --- a/frontend/webapp/components/notification/notification-manager.tsx +++ b/frontend/webapp/components/notification/notification-manager.tsx @@ -64,7 +64,8 @@ const NewCount = styled(Text)` `; export const NotificationManager = () => { - const { notifications, markAsSeen } = useNotificationStore(); + const { notifications: n, markAsSeen } = useNotificationStore(); + const notifications = n.filter(({ hideFromHistory }) => !hideFromHistory); const unseen = notifications.filter(({ seen }) => !seen); const unseenCount = unseen.length; diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx index cdcbfeda9..1a1b02573 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx @@ -72,7 +72,14 @@ export const RuleDrawer: React.FC = () => { const handleEdit = (bool?: boolean) => { if (item.type === InstrumentationRuleType.UNKNOWN_TYPE && (bool || bool === undefined)) { - addNotification({ type: NOTIFICATION_TYPE.WARNING, title: FORM_ALERTS.FORBIDDEN, message: FORM_ALERTS.CANNOT_EDIT_RULE, crdType: OVERVIEW_ENTITY_TYPES.RULE, target: id }); + addNotification({ + type: NOTIFICATION_TYPE.WARNING, + title: FORM_ALERTS.FORBIDDEN, + message: FORM_ALERTS.CANNOT_EDIT_RULE, + crdType: OVERVIEW_ENTITY_TYPES.RULE, + target: id, + hideFromHistory: true, + }); } else { setIsEditing(typeof bool === 'boolean' ? bool : true); } diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index 695b78a51..ae79d508f 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -15,13 +15,14 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.ACTION, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.ACTION) : undefined, + hideFromHistory, }); }; @@ -57,15 +58,15 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { actions: data?.computePlatform?.actions || [], createAction: (action: ActionInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating pipeline action...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating pipeline action...', undefined, true); createAction({ variables: { action } }); }, updateAction: (id: string, action: ActionInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating pipeline action...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating pipeline action...', undefined, true); updateAction({ variables: { id, action } }); }, deleteAction: (id: string, actionType: ActionsType) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting pipeline action...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting pipeline action...', undefined, true); deleteAction({ variables: { id, actionType } }); }, }; diff --git a/frontend/webapp/hooks/actions/useActionFormData.ts b/frontend/webapp/hooks/actions/useActionFormData.ts index 55b880056..5f6aed157 100644 --- a/frontend/webapp/hooks/actions/useActionFormData.ts +++ b/frontend/webapp/hooks/actions/useActionFormData.ts @@ -55,6 +55,7 @@ export function useActionFormData() { type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts index 8af6b93de..be53749ad 100644 --- a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts +++ b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts @@ -23,7 +23,7 @@ export const useComputePlatform = (): UseComputePlatformHook => { addNotification({ type: NOTIFICATION_TYPE.ERROR, title: error.name || ACTION.FETCH, - message: error.cause?.message, + message: error.cause?.message || error.message, }), }); diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index 3b7df92f8..b84fa8022 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -20,7 +20,7 @@ export const useNamespace = (namespaceName?: string, instrumentationLabeled = nu addNotification({ type: NOTIFICATION_TYPE.ERROR, title: error.name || ACTION.FETCH, - message: error.message, + message: error.cause?.message || error.message, }), }); diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 31eeb1c34..f571f65cd 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -15,13 +15,14 @@ export const useDestinationCRUD = (params?: Params) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.DESTINATION) : undefined, + hideFromHistory, }); }; @@ -57,15 +58,15 @@ export const useDestinationCRUD = (params?: Params) => { destinations: data?.computePlatform?.destinations || [], createDestination: (destination: DestinationInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating destination...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating destination...', undefined, true); createDestination({ variables: { destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, updateDestination: (id: string, destination: DestinationInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating destination...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating destination...', undefined, true); updateDestination({ variables: { id, destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, deleteDestination: (id: string) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting destination...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting destination...', undefined, true); deleteDestination({ variables: { id } }); }, }; diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 1e230085b..916bece0d 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -105,7 +105,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp addNotification({ type: NOTIFICATION_TYPE.ERROR, title: error.name || ACTION.FETCH, - message: error.message, + message: error.cause?.message || error.message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, }), }); @@ -171,6 +171,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 6c8718365..fbfbce747 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -15,13 +15,14 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.RULE, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.RULE) : undefined, + hideFromHistory, }); }; @@ -57,15 +58,15 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { instrumentationRules: data?.computePlatform?.instrumentationRules || [], createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating instrumentation rule...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating instrumentation rule...', undefined, true); createInstrumentationRule({ variables: { instrumentationRule } }); }, updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating instrumentation rule...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating instrumentation rule...', undefined, true); updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }); }, deleteInstrumentationRule: (ruleId: string) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting instrumentation rule...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting instrumentation rule...', undefined, true); deleteInstrumentationRule({ variables: { ruleId } }); }, }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts index 20d14e7df..e430d7ff7 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts @@ -45,6 +45,7 @@ export function useInstrumentationRuleFormData() { type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts index dc69a0e92..55e9e24be 100644 --- a/frontend/webapp/hooks/notification/useSSE.ts +++ b/frontend/webapp/hooks/notification/useSSE.ts @@ -4,18 +4,6 @@ import { NOTIFICATION_TYPE } from '@/types'; import { useComputePlatform } from '../compute-platform'; import { type NotifyPayload, useConnectionStore, useNotificationStore } from '@/store'; -const modifyType = (notification: NotifyPayload) => { - if (notification.title === 'Modified') { - if (notification.message?.indexOf('ProcessTerminated') === 0 || notification.message?.indexOf('NoHeartbeat') === 0 || notification.message?.indexOf('Failed') === 0) { - return NOTIFICATION_TYPE.ERROR; - } else { - return NOTIFICATION_TYPE.INFO; - } - } - - return notification.type; -}; - export const useSSE = () => { const { addNotification } = useNotificationStore(); const { setConnectionStore } = useConnectionStore(); @@ -40,8 +28,6 @@ export const useSSE = () => { target: data.target, }; - // notification.type = modifyType(notification); - // Dispatch the notification to the store addNotification(notification); refetchComputePlatform(); diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 48b79bd92..007924a16 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -31,13 +31,14 @@ export const useSourceCRUD = (params?: Params) => { } }, [refetch]); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.SOURCE, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE) : undefined, + hideFromHistory, }); }; @@ -106,16 +107,16 @@ export const useSourceCRUD = (params?: Params) => { sources: data?.computePlatform.k8sActualSources || [], createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating sources...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating sources...', undefined, true); await persistNamespaces(futureSelectAppsList); await persistSources(selectAppsList, true); }, updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating sources...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating sources...', undefined, true); await updateSource({ variables: { sourceId, patchSourceRequest } }); }, deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting sources...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting sources...', undefined, true); await persistSources(selectAppsList, false); }, }; diff --git a/frontend/webapp/types/common.ts b/frontend/webapp/types/common.ts index 5fa10761a..c0254c292 100644 --- a/frontend/webapp/types/common.ts +++ b/frontend/webapp/types/common.ts @@ -29,6 +29,7 @@ export interface Notification { target?: string; dismissed: boolean; seen: boolean; + hideFromHistory?: boolean; time: string; } From b8374a948800daadd001978e72f5570c290a67f7 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:44:28 +0200 Subject: [PATCH 096/259] refactor: update role permissions and resource names for clarity and consistency --- cli/cmd/resources/README.md | 77 ++++++++++++----------- cli/cmd/resources/ui.go | 16 ++--- helm/odigos/templates/ui/clusterrole.yaml | 14 ++++- 3 files changed, 59 insertions(+), 48 deletions(-) diff --git a/cli/cmd/resources/README.md b/cli/cmd/resources/README.md index 37beeaf66..b6bb7d7be 100644 --- a/cli/cmd/resources/README.md +++ b/cli/cmd/resources/README.md @@ -4,38 +4,40 @@ In this doc, we'll keep track of the permissions requested across different reso ## ClusterRole -| Component | APIGroups | Resources | Verbs | Comments | -| ------------ | --------- | -------------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------- | -| Odiglet | "" | pods | get, list, watch | Needed for language detection. | -| Odiglet | "" | pods/status | get | Needed for language detection. | -| Odiglet | "" | nodes | get, list, watch | Needed for virtual device registration. | -| Odiglet | apps | deployments, daemonsets, statefulsets | get, list, watch | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | apps | deployments/status, daemonsets/status, statefulsets/status | get | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | apps | replicasets | get | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | odigos.io | instrumentedapplications | get, list, watch, create, patch, update | Stores runtime details and language detection (temporary until migration to configs). | -| Odiglet | odigos.io | instrumentedapplications/status | get, patch, update | Updates status of instrumented applications. | -| Odiglet | odigos.io | instrumentationinstances | create, get, list, patch, update, watch, delete | Manages instrumentation instances for process state storage. | -| Odiglet | odigos.io | instrumentationinstances/status | get, patch, update | Updates status of instrumentation instances. | -| Odiglet | odigos.io | instrumentationconfigs | get, list, watch, patch, update | Manages instrumentation configurations (future update for runtime details storage). | -| Odiglet | odigos.io | instrumentationconfigs/status | get, patch, update | Updates status of instrumentation configurations. | -| Odiglet | policy | podsecuritypolicies | use | Allows using privileged PSP (if enabled). | -| Instrumentor | "" | nodes, namespaces | get, list, watch | Tracks runtime detection and resource labels for instrumentation. | -| Instrumentor | apps | daemonsets, deployments, statefulsets | get, list, watch, update, patch | Adjusts pod specifications for instrumentation. | -| Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Reacts to runtime detections in workloads. | -| Instrumentor | odigos.io | instrumentedapplications/status | get, patch, update | Updates application statuses post-injection. | -| Instrumentor | odigos.io | instrumentationconfigs | create, delete, get, list, patch, update, watch | Manages instrumentation configurations. | -| Scheduler | odigos.io | instrumentationconfigs | get, list, watch | Monitors changes in instrumentation configurations for scheduling updates. | -| Autoscaler | odigos.io | instrumentationconfigs | get, list, watch | Reads instrumentation configurations to populate the `data-collector` configmaps. | -| Collector | "" | nodes/stats, nodes/proxy | get, list | Retrieves metrics for telemetry purposes. | -| Collector | "" | pods | get, list | Accesses metadata for resource name processors. | -| Collector | apps | replicasets, deployments, daemonsets, statefulsets | get, list | Fetches application details for instrumentation. | -| Collector | "" | endpoints | get, list, watch | Needed for load balancer. | -| Collector | policy | podsecuritypolicies | use | Supports clients enabling pod security policies (optional). | -| UI | "" | namespaces | get, list, patch | Required to retrieve and modify namespace configurations during instrumentation. | -| UI | "" | services, pods | get, list | Required for discovering potential destinations and describing application workloads. | -| UI | apps | deployments, statefulsets, daemonsets | get, list, patch, update | Needed for application instrumentation. | -| UI | apps | replicasets | get, list | Used for describing source and application configurations. | -| UI | odigos.io | instrumentedapplications, instrumentationinstances, instrumentationconfigs | get, list, watch | Used to retrieve and monitor instrumented applications and configurations. | +| Component | APIGroups | Resources | Verbs | Comments | +| ------------ | ----------------- | ---------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------- | +| Odiglet | "" | pods | get, list, watch | Needed for language detection. | +| Odiglet | "" | pods/status | get | Needed for language detection. | +| Odiglet | "" | nodes | get, list, watch | Needed for virtual device registration. | +| Odiglet | apps | deployments, daemonsets, statefulsets | get, list, watch | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | apps | deployments/status, daemonsets/status, statefulsets/status | get | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | apps | replicasets | get | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | odigos.io | instrumentedapplications | get, list, watch, create, patch, update | Stores runtime details and language detection (temporary until migration to configs). | +| Odiglet | odigos.io | instrumentedapplications/status | get, patch, update | Updates status of instrumented applications. | +| Odiglet | odigos.io | instrumentationinstances | create, get, list, patch, update, watch, delete | Manages instrumentation instances for process state storage. | +| Odiglet | odigos.io | instrumentationinstances/status | get, patch, update | Updates status of instrumentation instances. | +| Odiglet | odigos.io | instrumentationconfigs | get, list, watch, patch, update | Manages instrumentation configurations (future update for runtime details storage). | +| Odiglet | odigos.io | instrumentationconfigs/status | get, patch, update | Updates status of instrumentation configurations. | +| Odiglet | policy | podsecuritypolicies | use | Allows using privileged PSP (if enabled). | +| Instrumentor | "" | nodes, namespaces | get, list, watch | Tracks runtime detection and resource labels for instrumentation. | +| Instrumentor | apps | daemonsets, deployments, statefulsets | get, list, watch, update, patch | Adjusts pod specifications for instrumentation. | +| Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Reacts to runtime detections in workloads. | +| Instrumentor | odigos.io | instrumentedapplications/status | get, patch, update | Updates application statuses post-injection. | +| Instrumentor | odigos.io | instrumentationconfigs | create, delete, get, list, patch, update, watch | Manages instrumentation configurations. | +| Scheduler | odigos.io | instrumentationconfigs | get, list, watch | Monitors changes in instrumentation configurations for scheduling updates. | +| Autoscaler | odigos.io | instrumentationconfigs | get, list, watch | Reads instrumentation configurations to populate the `data-collector` configmaps. | +| Collector | "" | nodes/stats, nodes/proxy | get, list | Retrieves metrics for telemetry purposes. | +| Collector | "" | pods | get, list | Accesses metadata for resource name processors. | +| Collector | apps | replicasets, deployments, daemonsets, statefulsets | get, list | Fetches application details for instrumentation. | +| Collector | "" | endpoints | get, list, watch | Needed for load balancer. | +| Collector | policy | podsecuritypolicies | use | Supports clients enabling pod security policies (optional). | +| UI | "" | namespaces | get, list, patch | Needed to retrieve and instrument namespaces. | +| UI | "" | services, pods | get, list | Required for identifying and describing sources and potential destinations. | +| UI | apps | deployments, statefulsets, daemonsets | get, list, patch, update | Required for instrumentation of application sources. | +| UI | apps | replicasets | get, list | Needed for describing application and resource relationships. | +| UI | odigos.io | instrumentationconfigs, instrumentationinstances | get, list, watch | Monitors and retrieves configurations for instrumentation. | +| UI | odigos.io | sources | get, list, create, delete | Manages sources for instrumentation and monitoring. | +| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Handles pipeline actions for custom logic. | --- @@ -56,8 +58,9 @@ In this doc, we'll keep track of the permissions requested across different reso | Autoscaler | autoscaling | horizontalpodautoscalers | create, patch, update, delete | Implements autoscaling for gateway collectors. | | Autoscaler | odigos.io | destinations | get, list, watch | Tracks and synchronizes destination configurations. | | Autoscaler | odigos.io | collectorsgroups, destinations/status | get, patch, update | Monitors and updates statuses of collectors groups and destinations. | -| UI | "" | configmaps | get, list | Accesses `odigos-config` for UI configuration settings. | -| UI | "" | secrets | get, list, create, patch, update | Manages destination secrets. | -| UI | odigos.io | instrumentationrules, destinations | get, list, create, patch, update, delete, watch | CRUD operations for destinations and instrumentation rules. | -| UI | odigos.io | collectorsgroups | get, list | Monitors instrumentation groups. | -| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Handles pipeline actions for custom logic. | +| UI | "" | configmaps | get, list | Reads `odigos-config` for UI configuration. | +| UI | "" | secrets | get, list, create, patch, update | Manages destination secrets for configurations. | +| UI | odigos.io | instrumentationrules, destinations | get, list, create, patch, update, delete | CRUD for destinations and instrumentation rules. | +| UI | odigos.io | collectorsgroups | get, list | Monitors groupings of collectors for UI updates. | +| UI | odigos.io | sources | get, list, create, delete | Manages sources for instrumentation and monitoring. | +| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Pipeline action management. | diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index 5d0fbdab6..f87d7ee84 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -150,12 +150,12 @@ func NewUIRole(ns string) *rbacv1.Role { Resources: []string{"secrets"}, Verbs: []string{"get", "list", "create", "patch", "update"}, }, - { // Needed for CRUD on Odigos entities + { // Needed for CRUD on instr. rule and destinations APIGroups: []string{"odigos.io"}, Resources: []string{"instrumentationrules", "destinations"}, Verbs: []string{"get", "list", "create", "patch", "update", "delete"}, }, - { // Needed to watch Odigos entities + { // Needed to notify UI about changes with destinations APIGroups: []string{"odigos.io"}, Resources: []string{"destinations"}, Verbs: []string{"watch"}, @@ -165,7 +165,7 @@ func NewUIRole(ns string) *rbacv1.Role { Resources: []string{"collectorsgroups"}, Verbs: []string{"get", "list"}, }, - { // Needed for CRUD on Pipeline Actions + { // Needed for CRUD on pipeline actions APIGroups: []string{"actions.odigos.io"}, Resources: []string{"*"}, Verbs: []string{"get", "list", "create", "patch", "update", "delete"}, @@ -214,7 +214,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Resources: []string{"namespaces"}, Verbs: []string{"get", "list", "patch"}, }, - { // Needed to instrument applications + { // Needed to get and instrument sources APIGroups: []string{"apps"}, Resources: []string{"deployments", "statefulsets", "daemonsets"}, Verbs: []string{"get", "list", "patch", "update"}, @@ -230,17 +230,17 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Resources: []string{"services", "pods"}, Verbs: []string{"get", "list"}, }, - { // Needed to read Odigos entities + { // Needed to get sources APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentationinstances", "instrumentationconfigs"}, + Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, Verbs: []string{"get", "list"}, }, - { // Needed to instrument / uninstrument applications + { // Needed to instrument / uninstrument sources APIGroups: []string{"odigos.io"}, Resources: []string{"sources"}, Verbs: []string{"get", "list", "create", "delete"}, }, - { // Needed to watch instrumented applications + { // Needed to notify UI about changes with sources APIGroups: []string{"odigos.io"}, Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, Verbs: []string{"watch"}, diff --git a/helm/odigos/templates/ui/clusterrole.yaml b/helm/odigos/templates/ui/clusterrole.yaml index db40122df..75eaf295a 100644 --- a/helm/odigos/templates/ui/clusterrole.yaml +++ b/helm/odigos/templates/ui/clusterrole.yaml @@ -40,16 +40,24 @@ rules: - apiGroups: - odigos.io resources: - - instrumentedapplications - - instrumentationinstances - instrumentationconfigs + - instrumentationinstances verbs: - get - list - apiGroups: - odigos.io resources: - - instrumentedapplications + - sources + verbs: + - get + - list + - create + - delete + - apiGroups: + - odigos.io + resources: + - instrumentationconfigs - instrumentationinstances verbs: - watch From 8cd2eea6c069ba51ff6efefd12f17d95b8dc1505 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:01:37 +0200 Subject: [PATCH 097/259] cleanup --- .../webapp/hooks/sources/useSourceCRUD.ts | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 007924a16..84a6b283e 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -1,4 +1,3 @@ -import { useCallback } from 'react'; import { useMutation } from '@apollo/client'; import { ACTION, getSseTargetFromId } from '@/utils'; import { useAppStore, useNotificationStore } from '@/store'; @@ -19,18 +18,6 @@ export const useSourceCRUD = (params?: Params) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const startPolling = useCallback(async () => { - let retries = 0; - const maxRetries = 5; - const retryInterval = 1 * 1000; // time in milliseconds - - while (retries < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, retryInterval)); - refetch(); - retries++; - } - }, [refetch]); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { addNotification({ type, @@ -42,14 +29,14 @@ export const useSourceCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string) => { + const handleComplete = (actionType: string) => { refetch(); - params?.onSuccess?.(title); + params?.onSuccess?.(actionType); }; const [createOrDeleteSources, cdState] = useMutation<{ persistK8sSources: boolean }>(PERSIST_SOURCE, { From dbaec1ff29ffd1fb6b1dcf9cf033486dad6460a3 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:01:44 +0200 Subject: [PATCH 098/259] feat: enhance notification messages for action and instrumentation rule CRUD operations --- .../webapp/hooks/actions/useActionCRUD.ts | 18 +++++++----- .../useInstrumentationRuleCRUD.ts | 28 +++++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index ae79d508f..f10b84b88 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -31,25 +31,32 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { params?.onError?.(actionType); }; - const handleComplete = (actionType: string) => { + const handleComplete = (actionType: string, message: string, id?: string) => { refetch(); + notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); params?.onSuccess?.(actionType); }; const [createAction, cState] = useMutation<{ createAction: { id: string } }>(CREATE_ACTION, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: () => handleComplete(ACTION.CREATE), + onCompleted: (res, req) => { + const id = res?.createAction?.id; + handleComplete(ACTION.CREATE, `Action "${id}" created`, id); + }, }); const [updateAction, uState] = useMutation<{ updateAction: { id: string } }>(UPDATE_ACTION, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: () => handleComplete(ACTION.UPDATE), + onCompleted: (res, req) => { + const id = res?.updateAction?.id; + handleComplete(ACTION.UPDATE, `Action "${id}" updated`, id); + }, }); const [deleteAction, dState] = useMutation<{ deleteAction: boolean }>(DELETE_ACTION, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.id; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.ACTION)); - handleComplete(ACTION.DELETE); + handleComplete(ACTION.DELETE, `Action "${id}" deleted`, id); }, }); @@ -58,15 +65,12 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { actions: data?.computePlatform?.actions || [], createAction: (action: ActionInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating pipeline action...', undefined, true); createAction({ variables: { action } }); }, updateAction: (id: string, action: ActionInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating pipeline action...', undefined, true); updateAction({ variables: { id, action } }); }, deleteAction: (id: string, actionType: ActionsType) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting pipeline action...', undefined, true); deleteAction({ variables: { id, actionType } }); }, }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index fbfbce747..230548285 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -1,6 +1,6 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; -import { ACTION, getSseTargetFromId } from '@/utils'; +import { ACTION, deriveTypeFromRule, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput } from '@/types'; import { CREATE_INSTRUMENTATION_RULE, UPDATE_INSTRUMENTATION_RULE, DELETE_INSTRUMENTATION_RULE } from '@/graphql/mutations'; @@ -26,30 +26,37 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string) => { + const handleComplete = (actionType: string, message: string, id?: string) => { refetch(); - params?.onSuccess?.(title); + notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); + params?.onSuccess?.(actionType); }; const [createInstrumentationRule, cState] = useMutation<{ createInstrumentationRule: { ruleId: string } }>(CREATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: () => handleComplete(ACTION.CREATE), + onCompleted: (res, req) => { + const id = res?.createInstrumentationRule?.ruleId; + handleComplete(ACTION.CREATE, `Rule "${id}" created`, id); + }, }); const [updateInstrumentationRule, uState] = useMutation<{ updateInstrumentationRule: { ruleId: string } }>(UPDATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: () => handleComplete(ACTION.UPDATE), + onCompleted: (res, req) => { + const id = res?.updateInstrumentationRule?.ruleId; + handleComplete(ACTION.UPDATE, `Rule "${id}" updated`, id); + }, }); const [deleteInstrumentationRule, dState] = useMutation<{ deleteInstrumentationRule: boolean }>(DELETE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.ruleId; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.RULE)); - handleComplete(ACTION.DELETE); + handleComplete(ACTION.DELETE, `Rule "${id}" deleted`, id); }, }); @@ -58,15 +65,12 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { instrumentationRules: data?.computePlatform?.instrumentationRules || [], createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating instrumentation rule...', undefined, true); createInstrumentationRule({ variables: { instrumentationRule } }); }, updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating instrumentation rule...', undefined, true); updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }); }, deleteInstrumentationRule: (ruleId: string) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting instrumentation rule...', undefined, true); deleteInstrumentationRule({ variables: { ruleId } }); }, }; From 44e36d3298274ec33c50fc50ab39c2c3db3097c0 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:02:09 +0200 Subject: [PATCH 099/259] cleanup --- .../hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 230548285..6a6b5aa03 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -1,6 +1,6 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; -import { ACTION, deriveTypeFromRule, getSseTargetFromId } from '@/utils'; +import { ACTION, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput } from '@/types'; import { CREATE_INSTRUMENTATION_RULE, UPDATE_INSTRUMENTATION_RULE, DELETE_INSTRUMENTATION_RULE } from '@/graphql/mutations'; From dfac48c90b0c0014c56004df9c32f91e9dfe692f Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:17:05 +0200 Subject: [PATCH 100/259] refactor: remove instr. app from describe source --- frontend/graph/generated.go | 445 +++++------------- frontend/graph/model/models_gen.go | 32 +- frontend/graph/schema.graphqls | 18 +- .../source_describe/source_describe.go | 8 +- frontend/webapp/graphql/queries/describe.ts | 16 +- frontend/webapp/types/describe.ts | 10 +- k8sutils/pkg/describe/source.go | 20 +- k8sutils/pkg/describe/source/analyze.go | 61 +-- 8 files changed, 151 insertions(+), 459 deletions(-) diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index 22cd3662b..a07f00471 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -217,6 +217,7 @@ type ComplexityRoot struct { } InstrumentationConfigAnalyze struct { + Containers func(childComplexity int) int CreateTime func(childComplexity int) int Created func(childComplexity int) int } @@ -255,12 +256,6 @@ type ComplexityRoot struct { Workloads func(childComplexity int) int } - InstrumentedApplicationAnalyze struct { - Containers func(childComplexity int) int - CreateTime func(childComplexity int) int - Created func(childComplexity int) int - } - K8sActualNamespace struct { InstrumentationLabelEnabled func(childComplexity int) int K8sActualSources func(childComplexity int, instrumentationLabeled *bool) int @@ -434,17 +429,16 @@ type ComplexityRoot struct { } SourceAnalyze struct { - InstrumentationConfig func(childComplexity int) int - InstrumentationDevice func(childComplexity int) int - InstrumentedApplication func(childComplexity int) int - Kind func(childComplexity int) int - Labels func(childComplexity int) int - Name func(childComplexity int) int - Namespace func(childComplexity int) int - Pods func(childComplexity int) int - PodsPhasesCount func(childComplexity int) int - RuntimeInfo func(childComplexity int) int - TotalPods func(childComplexity int) int + InstrumentationConfig func(childComplexity int) int + InstrumentationDevice func(childComplexity int) int + Kind func(childComplexity int) int + Labels func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + Pods func(childComplexity int) int + PodsPhasesCount func(childComplexity int) int + RuntimeInfo func(childComplexity int) int + TotalPods func(childComplexity int) int } SourceContainerRuntimeDetails struct { @@ -1214,6 +1208,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.HttpPayloadCollection.MimeTypes(childComplexity), true + case "InstrumentationConfigAnalyze.containers": + if e.complexity.InstrumentationConfigAnalyze.Containers == nil { + break + } + + return e.complexity.InstrumentationConfigAnalyze.Containers(childComplexity), true + case "InstrumentationConfigAnalyze.createTime": if e.complexity.InstrumentationConfigAnalyze.CreateTime == nil { break @@ -1361,27 +1362,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationRule.Workloads(childComplexity), true - case "InstrumentedApplicationAnalyze.containers": - if e.complexity.InstrumentedApplicationAnalyze.Containers == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.Containers(childComplexity), true - - case "InstrumentedApplicationAnalyze.createTime": - if e.complexity.InstrumentedApplicationAnalyze.CreateTime == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.CreateTime(childComplexity), true - - case "InstrumentedApplicationAnalyze.created": - if e.complexity.InstrumentedApplicationAnalyze.Created == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.Created(childComplexity), true - case "K8sActualNamespace.instrumentationLabelEnabled": if e.complexity.K8sActualNamespace.InstrumentationLabelEnabled == nil { break @@ -2239,13 +2219,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SourceAnalyze.InstrumentationDevice(childComplexity), true - case "SourceAnalyze.instrumentedApplication": - if e.complexity.SourceAnalyze.InstrumentedApplication == nil { - break - } - - return e.complexity.SourceAnalyze.InstrumentedApplication(childComplexity), true - case "SourceAnalyze.kind": if e.complexity.SourceAnalyze.Kind == nil { break @@ -7550,6 +7523,60 @@ func (ec *executionContext) fieldContext_InstrumentationConfigAnalyze_createTime return fc, nil } +func (ec *executionContext) _InstrumentationConfigAnalyze_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationConfigAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationConfigAnalyze_containers(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Containers, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ContainerRuntimeInfoAnalyze) + fc.Result = res + return ec.marshalNContainerRuntimeInfoAnalyze2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐContainerRuntimeInfoAnalyzeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_InstrumentationConfigAnalyze_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "InstrumentationConfigAnalyze", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "containerName": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_containerName(ctx, field) + case "language": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_language(ctx, field) + case "runtimeVersion": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_runtimeVersion(ctx, field) + case "envVars": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_envVars(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ContainerRuntimeInfoAnalyze", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _InstrumentationDeviceAnalyze_statusText(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationDeviceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_InstrumentationDeviceAnalyze_statusText(ctx, field) if err != nil { @@ -8464,165 +8491,6 @@ func (ec *executionContext) fieldContext_InstrumentationRule_payloadCollection(_ return fc, nil } -func (ec *executionContext) _InstrumentedApplicationAnalyze_created(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_created(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Created, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.EntityProperty) - fc.Result = res - return ec.marshalNEntityProperty2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐEntityProperty(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_created(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_EntityProperty_name(ctx, field) - case "value": - return ec.fieldContext_EntityProperty_value(ctx, field) - case "status": - return ec.fieldContext_EntityProperty_status(ctx, field) - case "explain": - return ec.fieldContext_EntityProperty_explain(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationAnalyze_createTime(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_createTime(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.CreateTime, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*model.EntityProperty) - fc.Result = res - return ec.marshalOEntityProperty2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐEntityProperty(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_createTime(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_EntityProperty_name(ctx, field) - case "value": - return ec.fieldContext_EntityProperty_value(ctx, field) - case "status": - return ec.fieldContext_EntityProperty_status(ctx, field) - case "explain": - return ec.fieldContext_EntityProperty_explain(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationAnalyze_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_containers(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Containers, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.ContainerRuntimeInfoAnalyze) - fc.Result = res - return ec.marshalNContainerRuntimeInfoAnalyze2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐContainerRuntimeInfoAnalyzeᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "containerName": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_containerName(ctx, field) - case "language": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_language(ctx, field) - case "runtimeVersion": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_runtimeVersion(ctx, field) - case "envVars": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_envVars(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ContainerRuntimeInfoAnalyze", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualNamespace_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualNamespace_name(ctx, field) if err != nil { @@ -13201,12 +13069,10 @@ func (ec *executionContext) fieldContext_Query_describeSource(ctx context.Contex return ec.fieldContext_SourceAnalyze_namespace(ctx, field) case "labels": return ec.fieldContext_SourceAnalyze_labels(ctx, field) - case "instrumentationConfig": - return ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) case "runtimeInfo": return ec.fieldContext_SourceAnalyze_runtimeInfo(ctx, field) - case "instrumentedApplication": - return ec.fieldContext_SourceAnalyze_instrumentedApplication(ctx, field) + case "instrumentationConfig": + return ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) case "instrumentationDevice": return ec.fieldContext_SourceAnalyze_instrumentationDevice(ctx, field) case "totalPods": @@ -14340,56 +14206,6 @@ func (ec *executionContext) fieldContext_SourceAnalyze_labels(_ context.Context, return fc, nil } -func (ec *executionContext) _SourceAnalyze_instrumentationConfig(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.InstrumentationConfig, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.InstrumentationConfigAnalyze) - fc.Result = res - return ec.marshalNInstrumentationConfigAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationConfigAnalyze(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_SourceAnalyze_instrumentationConfig(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "SourceAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "created": - return ec.fieldContext_InstrumentationConfigAnalyze_created(ctx, field) - case "createTime": - return ec.fieldContext_InstrumentationConfigAnalyze_createTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationConfigAnalyze", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _SourceAnalyze_runtimeInfo(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SourceAnalyze_runtimeInfo(ctx, field) if err != nil { @@ -14411,11 +14227,14 @@ func (ec *executionContext) _SourceAnalyze_runtimeInfo(ctx context.Context, fiel return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*model.RuntimeInfoAnalyze) fc.Result = res - return ec.marshalORuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx, field.Selections, res) + return ec.marshalNRuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_SourceAnalyze_runtimeInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -14437,8 +14256,8 @@ func (ec *executionContext) fieldContext_SourceAnalyze_runtimeInfo(_ context.Con return fc, nil } -func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceAnalyze_instrumentedApplication(ctx, field) +func (ec *executionContext) _SourceAnalyze_instrumentationConfig(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) if err != nil { return graphql.Null } @@ -14451,7 +14270,7 @@ func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.InstrumentedApplication, nil + return obj.InstrumentationConfig, nil }) if err != nil { ec.Error(ctx, err) @@ -14463,12 +14282,12 @@ func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.C } return graphql.Null } - res := resTmp.(*model.InstrumentedApplicationAnalyze) + res := resTmp.(*model.InstrumentationConfigAnalyze) fc.Result = res - return ec.marshalNInstrumentedApplicationAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationAnalyze(ctx, field.Selections, res) + return ec.marshalNInstrumentationConfigAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationConfigAnalyze(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SourceAnalyze_instrumentedApplication(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SourceAnalyze_instrumentationConfig(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "SourceAnalyze", Field: field, @@ -14477,13 +14296,13 @@ func (ec *executionContext) fieldContext_SourceAnalyze_instrumentedApplication(_ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "created": - return ec.fieldContext_InstrumentedApplicationAnalyze_created(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_created(ctx, field) case "createTime": - return ec.fieldContext_InstrumentedApplicationAnalyze_createTime(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_createTime(ctx, field) case "containers": - return ec.fieldContext_InstrumentedApplicationAnalyze_containers(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_containers(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type InstrumentedApplicationAnalyze", field.Name) + return nil, fmt.Errorf("no field named %q was found under type InstrumentationConfigAnalyze", field.Name) }, } return fc, nil @@ -19220,6 +19039,11 @@ func (ec *executionContext) _InstrumentationConfigAnalyze(ctx context.Context, s } case "createTime": out.Values[i] = ec._InstrumentationConfigAnalyze_createTime(ctx, field, obj) + case "containers": + out.Values[i] = ec._InstrumentationConfigAnalyze_containers(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -19472,52 +19296,6 @@ func (ec *executionContext) _InstrumentationRule(ctx context.Context, sel ast.Se return out } -var instrumentedApplicationAnalyzeImplementors = []string{"InstrumentedApplicationAnalyze"} - -func (ec *executionContext) _InstrumentedApplicationAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentedApplicationAnalyze) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentedApplicationAnalyzeImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentedApplicationAnalyze") - case "created": - out.Values[i] = ec._InstrumentedApplicationAnalyze_created(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "createTime": - out.Values[i] = ec._InstrumentedApplicationAnalyze_createTime(ctx, field, obj) - case "containers": - out.Values[i] = ec._InstrumentedApplicationAnalyze_containers(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var k8sActualNamespaceImplementors = []string{"K8sActualNamespace"} func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.SelectionSet, obj *model.K8sActualNamespace) graphql.Marshaler { @@ -20929,15 +20707,13 @@ func (ec *executionContext) _SourceAnalyze(ctx context.Context, sel ast.Selectio if out.Values[i] == graphql.Null { out.Invalids++ } - case "instrumentationConfig": - out.Values[i] = ec._SourceAnalyze_instrumentationConfig(ctx, field, obj) + case "runtimeInfo": + out.Values[i] = ec._SourceAnalyze_runtimeInfo(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "runtimeInfo": - out.Values[i] = ec._SourceAnalyze_runtimeInfo(ctx, field, obj) - case "instrumentedApplication": - out.Values[i] = ec._SourceAnalyze_instrumentedApplication(ctx, field, obj) + case "instrumentationConfig": + out.Values[i] = ec._SourceAnalyze_instrumentationConfig(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -22285,16 +22061,6 @@ func (ec *executionContext) unmarshalNInstrumentationRuleInput2githubᚗcomᚋod return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNInstrumentedApplicationAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentedApplicationAnalyze) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentedApplicationAnalyze(ctx, sel, v) -} - func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { res, err := graphql.UnmarshalInt(v) return res, graphql.ErrorOnPath(ctx, err) @@ -22652,6 +22418,16 @@ func (ec *executionContext) unmarshalNPodWorkloadInput2ᚖgithubᚗcomᚋodigos return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNRuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.RuntimeInfoAnalyze) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._RuntimeInfoAnalyze(ctx, sel, v) +} + func (ec *executionContext) unmarshalNSignalType2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSignalType(ctx context.Context, v interface{}) (model.SignalType, error) { var res model.SignalType err := res.UnmarshalGQL(v) @@ -23547,13 +23323,6 @@ func (ec *executionContext) marshalOProgrammingLanguage2ᚖgithubᚗcomᚋodigos return v } -func (ec *executionContext) marshalORuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.RuntimeInfoAnalyze) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._RuntimeInfoAnalyze(ctx, sel, v) -} - func (ec *executionContext) marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.SourceContainerRuntimeDetails) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 6e4b07780..9bef3d092 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -241,8 +241,9 @@ type HTTPPayloadCollectionInput struct { } type InstrumentationConfigAnalyze struct { - Created *EntityProperty `json:"created"` - CreateTime *EntityProperty `json:"createTime,omitempty"` + Created *EntityProperty `json:"created"` + CreateTime *EntityProperty `json:"createTime,omitempty"` + Containers []*ContainerRuntimeInfoAnalyze `json:"containers"` } type InstrumentationDeviceAnalyze struct { @@ -294,12 +295,6 @@ type InstrumentationRuleInput struct { PayloadCollection *PayloadCollectionInput `json:"payloadCollection,omitempty"` } -type InstrumentedApplicationAnalyze struct { - Created *EntityProperty `json:"created"` - CreateTime *EntityProperty `json:"createTime,omitempty"` - Containers []*ContainerRuntimeInfoAnalyze `json:"containers"` -} - type K8sActualNamespace struct { Name string `json:"name"` InstrumentationLabelEnabled *bool `json:"instrumentationLabelEnabled,omitempty"` @@ -568,17 +563,16 @@ type SingleSourceMetricsResponse struct { } type SourceAnalyze struct { - Name *EntityProperty `json:"name"` - Kind *EntityProperty `json:"kind"` - Namespace *EntityProperty `json:"namespace"` - Labels *InstrumentationLabelsAnalyze `json:"labels"` - InstrumentationConfig *InstrumentationConfigAnalyze `json:"instrumentationConfig"` - RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo,omitempty"` - InstrumentedApplication *InstrumentedApplicationAnalyze `json:"instrumentedApplication"` - InstrumentationDevice *InstrumentationDeviceAnalyze `json:"instrumentationDevice"` - TotalPods int `json:"totalPods"` - PodsPhasesCount string `json:"podsPhasesCount"` - Pods []*PodAnalyze `json:"pods"` + Name *EntityProperty `json:"name"` + Kind *EntityProperty `json:"kind"` + Namespace *EntityProperty `json:"namespace"` + Labels *InstrumentationLabelsAnalyze `json:"labels"` + RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` + InstrumentationConfig *InstrumentationConfigAnalyze `json:"instrumentationConfig"` + InstrumentationDevice *InstrumentationDeviceAnalyze `json:"instrumentationDevice"` + TotalPods int `json:"totalPods"` + PodsPhasesCount string `json:"podsPhasesCount"` + Pods []*PodAnalyze `json:"pods"` } type SourceContainerRuntimeDetails struct { diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index f2fb30fa6..76be28369 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -447,11 +447,6 @@ type InstrumentationLabelsAnalyze { instrumentedText: EntityProperty } -type InstrumentationConfigAnalyze { - created: EntityProperty! - createTime: EntityProperty -} - type ContainerRuntimeInfoAnalyze { containerName: EntityProperty! language: EntityProperty! @@ -459,14 +454,14 @@ type ContainerRuntimeInfoAnalyze { envVars: [EntityProperty!]! } -type RuntimeInfoAnalyze { - generation: EntityProperty! +type InstrumentationConfigAnalyze { + created: EntityProperty! + createTime: EntityProperty containers: [ContainerRuntimeInfoAnalyze!]! } -type InstrumentedApplicationAnalyze { - created: EntityProperty! - createTime: EntityProperty +type RuntimeInfoAnalyze { + generation: EntityProperty! containers: [ContainerRuntimeInfoAnalyze!]! } @@ -506,9 +501,8 @@ type SourceAnalyze { namespace: EntityProperty! labels: InstrumentationLabelsAnalyze! + runtimeInfo: RuntimeInfoAnalyze! instrumentationConfig: InstrumentationConfigAnalyze! - runtimeInfo: RuntimeInfoAnalyze - instrumentedApplication: InstrumentedApplicationAnalyze! instrumentationDevice: InstrumentationDeviceAnalyze! totalPods: Int! diff --git a/frontend/services/describe/source_describe/source_describe.go b/frontend/services/describe/source_describe/source_describe.go index b422e9836..329ffc76c 100644 --- a/frontend/services/describe/source_describe/source_describe.go +++ b/frontend/services/describe/source_describe/source_describe.go @@ -50,15 +50,11 @@ func ConvertSourceAnalyzeToGQL(analyze *source.SourceAnalyze) *model.SourceAnaly Namespace: describe_utils.ConvertEntityPropertyToGQL(analyze.Labels.Namespace), InstrumentedText: describe_utils.ConvertEntityPropertyToGQL(&analyze.Labels.InstrumentedText), }, + RuntimeInfo: convertRuntimeInfoToGQL(analyze.RuntimeInfo), InstrumentationConfig: &model.InstrumentationConfigAnalyze{ Created: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentationConfig.Created), CreateTime: describe_utils.ConvertEntityPropertyToGQL(analyze.InstrumentationConfig.CreateTime), - }, - RuntimeInfo: convertRuntimeInfoToGQL(analyze.RuntimeInfo), - InstrumentedApplication: &model.InstrumentedApplicationAnalyze{ - Created: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentedApplication.Created), - CreateTime: describe_utils.ConvertEntityPropertyToGQL(analyze.InstrumentedApplication.CreateTime), - Containers: convertRuntimeInfoContainersToGQL(analyze.InstrumentedApplication.Containers), + Containers: convertRuntimeInfoContainersToGQL(analyze.InstrumentationConfig.Containers), }, InstrumentationDevice: &model.InstrumentationDeviceAnalyze{ StatusText: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentationDevice.StatusText), diff --git a/frontend/webapp/graphql/queries/describe.ts b/frontend/webapp/graphql/queries/describe.ts index 1280a312e..cfd09670e 100644 --- a/frontend/webapp/graphql/queries/describe.ts +++ b/frontend/webapp/graphql/queries/describe.ts @@ -188,20 +188,6 @@ export const DESCRIBE_SOURCE = gql` explain } } - instrumentationConfig { - created { - name - value - status - explain - } - createTime { - name - value - status - explain - } - } runtimeInfo { generation { name @@ -236,7 +222,7 @@ export const DESCRIBE_SOURCE = gql` } } } - instrumentedApplication { + instrumentationConfig { created { name value diff --git a/frontend/webapp/types/describe.ts b/frontend/webapp/types/describe.ts index ead5c51e0..556bd5f54 100644 --- a/frontend/webapp/types/describe.ts +++ b/frontend/webapp/types/describe.ts @@ -12,11 +12,6 @@ interface InstrumentationLabelsAnalyze { instrumentedText?: EntityProperty; } -interface InstrumentationConfigAnalyze { - created: EntityProperty; - createTime?: EntityProperty; -} - interface ContainerRuntimeInfoAnalyze { containerName: EntityProperty; language: EntityProperty; @@ -29,7 +24,7 @@ interface RuntimeInfoAnalyze { containers: ContainerRuntimeInfoAnalyze[]; } -interface InstrumentedApplicationAnalyze { +interface InstrumentationConfigAnalyze { created: EntityProperty; createTime?: EntityProperty; containers: ContainerRuntimeInfoAnalyze[]; @@ -71,9 +66,8 @@ interface SourceAnalyze { namespace: EntityProperty; labels: InstrumentationLabelsAnalyze; - instrumentationConfig: InstrumentationConfigAnalyze; runtimeInfo?: RuntimeInfoAnalyze; - instrumentedApplication: InstrumentedApplicationAnalyze; + instrumentationConfig: InstrumentationConfigAnalyze; instrumentationDevice: InstrumentationDeviceAnalyze; totalPods: number; diff --git a/k8sutils/pkg/describe/source.go b/k8sutils/pkg/describe/source.go index d5bf6a75e..38bd055bd 100644 --- a/k8sutils/pkg/describe/source.go +++ b/k8sutils/pkg/describe/source.go @@ -23,12 +23,6 @@ func printWorkloadManifestInfo(analyze *source.SourceAnalyze, sb *strings.Builde printProperty(sb, 1, &analyze.Labels.InstrumentedText) } -func printInstrumentationConfigInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { - describeText(sb, 0, "\nInstrumentation Config:") - printProperty(sb, 1, &analyze.InstrumentationConfig.Created) - printProperty(sb, 1, analyze.InstrumentationConfig.CreateTime) -} - func printRuntimeDetails(analyze *source.SourceAnalyze, sb *strings.Builder) { describeText(sb, 0, "\nRuntime Inspection Details (new):") @@ -52,14 +46,13 @@ func printRuntimeDetails(analyze *source.SourceAnalyze, sb *strings.Builder) { } } -func printInstrumentedApplicationInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { - - describeText(sb, 0, "\nRuntime Inspection Details (old):") - printProperty(sb, 1, &analyze.InstrumentedApplication.Created) - printProperty(sb, 1, analyze.InstrumentedApplication.CreateTime) +func printInstrumentationConfigInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { + describeText(sb, 0, "\nInstrumentation Config:") + printProperty(sb, 1, &analyze.InstrumentationConfig.Created) + printProperty(sb, 1, analyze.InstrumentationConfig.CreateTime) describeText(sb, 1, "Detected Containers:") - for _, container := range analyze.InstrumentedApplication.Containers { + for _, container := range analyze.InstrumentationConfig.Containers { printProperty(sb, 2, &container.ContainerName) printProperty(sb, 3, &container.Language) printProperty(sb, 3, &container.RuntimeVersion) @@ -122,9 +115,8 @@ func DescribeSourceToText(analyze *source.SourceAnalyze) string { var sb strings.Builder printWorkloadManifestInfo(analyze, &sb) - printInstrumentationConfigInfo(analyze, &sb) printRuntimeDetails(analyze, &sb) - printInstrumentedApplicationInfo(analyze, &sb) + printInstrumentationConfigInfo(analyze, &sb) printAppliedInstrumentationDeviceInfo(analyze, &sb) printPodsInfo(analyze, &sb) diff --git a/k8sutils/pkg/describe/source/analyze.go b/k8sutils/pkg/describe/source/analyze.go index a825b3929..0b03fc5a1 100644 --- a/k8sutils/pkg/describe/source/analyze.go +++ b/k8sutils/pkg/describe/source/analyze.go @@ -21,11 +21,6 @@ type InstrumentationLabelsAnalyze struct { InstrumentedText properties.EntityProperty `json:"instrumentedText"` } -type InstrumentationConfigAnalyze struct { - Created properties.EntityProperty `json:"created"` - CreateTime *properties.EntityProperty `json:"createTime"` -} - type ContainerRuntimeInfoAnalyze struct { ContainerName properties.EntityProperty `json:"containerName"` Language properties.EntityProperty `json:"language"` @@ -38,7 +33,7 @@ type RuntimeInfoAnalyze struct { Containers []ContainerRuntimeInfoAnalyze `json:"containers"` } -type InstrumentedApplicationAnalyze struct { +type InstrumentationConfigAnalyze struct { Created properties.EntityProperty `json:"created"` CreateTime *properties.EntityProperty `json:"createTime"` Containers []ContainerRuntimeInfoAnalyze `json:"containers"` @@ -80,10 +75,9 @@ type SourceAnalyze struct { Namespace properties.EntityProperty `json:"namespace"` Labels InstrumentationLabelsAnalyze `json:"labels"` - InstrumentationConfig InstrumentationConfigAnalyze `json:"instrumentationConfig"` - RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` - InstrumentedApplication InstrumentedApplicationAnalyze `json:"instrumentedApplication"` - InstrumentationDevice InstrumentationDeviceAnalyze `json:"instrumentationDevice"` + RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` + InstrumentationConfig InstrumentationConfigAnalyze `json:"instrumentationConfig"` + InstrumentationDevice InstrumentationDeviceAnalyze `json:"instrumentationDevice"` TotalPods int `json:"totalPods"` PodsPhasesCount string `json:"podsPhasesCount"` @@ -167,9 +161,15 @@ func analyzeInstrumentationConfig(resources *OdigosSourceResources, instrumented } } + containers := make([]ContainerRuntimeInfoAnalyze, 0) + if resources.InstrumentedApplication != nil { + containers = analyzeRuntimeDetails(resources.InstrumentedApplication.Spec.RuntimeDetails) + } + return InstrumentationConfigAnalyze{ Created: created, CreateTime: createdTime, + Containers: containers, } } @@ -229,37 +229,6 @@ func analyzeRuntimeInfo(resources *OdigosSourceResources) *RuntimeInfoAnalyze { } } -func analyzeInstrumentedApplication(resources *OdigosSourceResources) InstrumentedApplicationAnalyze { - instrumentedApplicationCreated := resources.InstrumentedApplication != nil - - created := properties.EntityProperty{ - Name: "Created", - Value: properties.GetTextCreated(instrumentedApplicationCreated), - Status: properties.GetSuccessOrTransitioning(instrumentedApplicationCreated), - Explain: "whether the instrumented application object exists in the cluster. When a workload is labeled for instrumentation, an instrumented application object is created", - } - - var createdTime *properties.EntityProperty - if instrumentedApplicationCreated { - createdTime = &properties.EntityProperty{ - Name: "create time", - Value: resources.InstrumentedApplication.GetCreationTimestamp().String(), - Explain: "the time when the instrumented application object was created", - } - } - - containers := make([]ContainerRuntimeInfoAnalyze, 0) - if resources.InstrumentedApplication != nil { - containers = analyzeRuntimeDetails(resources.InstrumentedApplication.Spec.RuntimeDetails) - } - - return InstrumentedApplicationAnalyze{ - Created: created, - CreateTime: createdTime, - Containers: containers, - } -} - func analyzeInstrumentationDevice(resources *OdigosSourceResources, workloadObj *K8sSourceObject, instrumented bool) InstrumentationDeviceAnalyze { instrumentedApplication := resources.InstrumentedApplication @@ -520,9 +489,8 @@ func analyzePods(resources *OdigosSourceResources, expectedDevices Instrumentati func AnalyzeSource(resources *OdigosSourceResources, workloadObj *K8sSourceObject) *SourceAnalyze { labelsAnalysis, instrumented := analyzeInstrumentationLabels(resources, workloadObj) - icAnalysis := analyzeInstrumentationConfig(resources, instrumented) runtimeAnalysis := analyzeRuntimeInfo(resources) - instrumentedApplication := analyzeInstrumentedApplication(resources) + icAnalysis := analyzeInstrumentationConfig(resources, instrumented) device := analyzeInstrumentationDevice(resources, workloadObj, instrumented) pods, podsText := analyzePods(resources, device) @@ -532,10 +500,9 @@ func AnalyzeSource(resources *OdigosSourceResources, workloadObj *K8sSourceObjec Namespace: properties.EntityProperty{Name: "Namespace", Value: workloadObj.GetNamespace(), Explain: "the namespace of the k8s workload object that this source describes"}, Labels: labelsAnalysis, - InstrumentationConfig: icAnalysis, - RuntimeInfo: runtimeAnalysis, - InstrumentedApplication: instrumentedApplication, - InstrumentationDevice: device, + RuntimeInfo: runtimeAnalysis, + InstrumentationConfig: icAnalysis, + InstrumentationDevice: device, TotalPods: len(pods), PodsPhasesCount: podsText, From c3a964ea6fce2ac1ab015e809f9d9ba9ec3fdd23 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:24:45 +0200 Subject: [PATCH 101/259] cleanup --- frontend/services/sources.go | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index fc53b250f..394853e51 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -28,38 +28,6 @@ const ( WorkloadKindDaemonSet WorkloadKind = "DaemonSet" ) -type SourceLanguage struct { - ContainerName string `json:"container_name"` - Language string `json:"language"` -} - -type InstrumentedApplicationDetails struct { - Languages []SourceLanguage `json:"languages,omitempty"` - Conditions []metav1.Condition `json:"conditions,omitempty"` -} -type SourceID struct { - // combination of namespace, kind and name is unique - Name string `json:"name"` - Kind string `json:"kind"` - Namespace string `json:"namespace"` -} - -type Source struct { - ThinSource - ReportedName string `json:"reported_name,omitempty"` -} - -type PatchSourceRequest struct { - ReportedName *string `json:"reported_name"` -} - -// this object contains only part of the source fields. It is used to display the sources in the frontend -type ThinSource struct { - SourceID - NumberOfRunningInstances int `json:"number_of_running_instances"` - IaDetails *InstrumentedApplicationDetails `json:"instrumented_application_details"` -} - func GetWorkload(c context.Context, ns string, kind string, name string) (metav1.Object, int) { switch kind { case "Deployment": @@ -130,7 +98,6 @@ func AddHealthyInstrumentationInstancesCondition(ctx context.Context, instruConf } func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, nsName, metav1.GetOptions{}) if err != nil { return nil, err From 3c5904032bb0fd9da6341f299575cef720d2dfba Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:39:16 +0200 Subject: [PATCH 102/259] refactor: remove instrumented application retrieval from source resources --- k8sutils/pkg/describe/source/resources.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/k8sutils/pkg/describe/source/resources.go b/k8sutils/pkg/describe/source/resources.go index 3f585f51f..caaaa4d89 100644 --- a/k8sutils/pkg/describe/source/resources.go +++ b/k8sutils/pkg/describe/source/resources.go @@ -43,13 +43,6 @@ func GetRelevantSourceResources(ctx context.Context, kubeClient kubernetes.Inter return nil, err } - ia, err := odigosClient.InstrumentedApplications(workloadNs).Get(ctx, runtimeObjectName, metav1.GetOptions{}) - if err == nil { - sourceResources.InstrumentedApplication = ia - } else if !apierrors.IsNotFound(err) { - return nil, err - } - instrumentedAppSelector := labels.SelectorFromSet(labels.Set{ "instrumented-app": runtimeObjectName, }) From b62b840315bc50a04e51afd194f0afd8e665084f Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:43:32 +0200 Subject: [PATCH 103/259] refactor: remove unnecessary refetch calls in destination and source CRUD hooks --- frontend/webapp/hooks/actions/useActionCRUD.ts | 2 +- frontend/webapp/hooks/destinations/useDestinationCRUD.ts | 3 +-- .../hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts | 2 +- frontend/webapp/hooks/sources/useSourceCRUD.ts | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index f10b84b88..9cadebd79 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -32,8 +32,8 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { }; const handleComplete = (actionType: string, message: string, id?: string) => { - refetch(); notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); + refetch(); params?.onSuccess?.(actionType); }; diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index f571f65cd..9fc73cc76 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -12,7 +12,7 @@ interface Params { export const useDestinationCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); - const { data, refetch } = useComputePlatform(); + const { data } = useComputePlatform(); const { addNotification } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { @@ -32,7 +32,6 @@ export const useDestinationCRUD = (params?: Params) => { }; const handleComplete = (actionType: string) => { - refetch(); params?.onSuccess?.(actionType); }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 6a6b5aa03..99b61bf17 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -32,8 +32,8 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { }; const handleComplete = (actionType: string, message: string, id?: string) => { - refetch(); notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); + refetch(); params?.onSuccess?.(actionType); }; diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 84a6b283e..f62e6e366 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -15,7 +15,7 @@ export const useSourceCRUD = (params?: Params) => { const { configuredSources, setConfiguredSources } = useAppStore(); const { persistNamespace } = useNamespace(); - const { data, refetch } = useComputePlatform(); + const { data } = useComputePlatform(); const { addNotification } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { @@ -35,7 +35,6 @@ export const useSourceCRUD = (params?: Params) => { }; const handleComplete = (actionType: string) => { - refetch(); params?.onSuccess?.(actionType); }; From 2962545649b3f2efa80159a15b34342c77194bc5 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:53:41 +0200 Subject: [PATCH 104/259] refactor: add loading state for namespaces in sources list components --- .../choose-sources-body-fast/sources-list/index.tsx | 10 +++------- .../choose-sources-body-simple/sources-list/index.tsx | 9 +++------ frontend/webapp/hooks/sources/useSourceFormData.ts | 6 +++++- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx index 97231f57f..f38e35d4a 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import theme from '@/styles/theme'; import styled from 'styled-components'; import { UseSourceFormDataResponse } from '@/hooks'; -import { Checkbox, Divider, ExtendIcon, NoDataFound, Text, Toggle } from '@/reuseable-components'; +import { Checkbox, Divider, ExtendIcon, FadeLoader, NoDataFound, Text, Toggle } from '@/reuseable-components'; interface Props extends UseSourceFormDataResponse { isModal?: boolean; @@ -89,6 +89,7 @@ const NoDataFoundWrapper = styled.div` export const SourcesList: React.FC = ({ isModal = false, + namespacesLoading, selectedNamespace, onSelectNamespace, @@ -101,18 +102,13 @@ export const SourcesList: React.FC = ({ searchText, selectAllForNamespace, onSelectAll, - showSelectedOnly, filterSources, }) => { const namespaces = Object.entries(availableSources); if (!namespaces.length) { - return ( - - - - ); + return {namespacesLoading ? : }; } return ( diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx index 8bc147a45..76f4a163a 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { FolderIcon } from '@/assets'; import styled from 'styled-components'; import { type UseSourceFormDataResponse } from '@/hooks'; -import { Checkbox, NoDataFound, Text } from '@/reuseable-components'; +import { Checkbox, FadeLoader, NoDataFound, Text } from '@/reuseable-components'; interface Props extends UseSourceFormDataResponse { isModal?: boolean; @@ -74,6 +74,7 @@ const NoDataFoundWrapper = styled.div` export const SourcesList: React.FC = ({ isModal = false, + namespacesLoading, selectedNamespace, availableSources, @@ -85,11 +86,7 @@ export const SourcesList: React.FC = ({ const sources = availableSources[selectedNamespace] || []; if (!sources.length) { - return ( - - - - ); + return {namespacesLoading ? : }; } return ( diff --git a/frontend/webapp/hooks/sources/useSourceFormData.ts b/frontend/webapp/hooks/sources/useSourceFormData.ts index 8e16d1399..b877a1072 100644 --- a/frontend/webapp/hooks/sources/useSourceFormData.ts +++ b/frontend/webapp/hooks/sources/useSourceFormData.ts @@ -18,6 +18,8 @@ interface UseSourceFormDataParams { } export interface UseSourceFormDataResponse { + namespacesLoading: boolean; + selectedNamespace: SelectedNamespace; availableSources: SourcesByNamespace; selectedSources: SourcesByNamespace; @@ -50,7 +52,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo const [availableSources, setAvailableSources] = useState(appStore.availableSources); const [selectedSources, setSelectedSources] = useState(appStore.configuredSources); const [selectedFutureApps, setSelectedFutureApps] = useState(appStore.configuredFutureApps); - const { allNamespaces, data: namespacesData } = useNamespace(selectedNamespace, false); + const { allNamespaces, data: namespacesData, loading: namespacesLoading } = useNamespace(selectedNamespace, false); useEffect(() => { if (!!allNamespaces?.length) { @@ -197,6 +199,8 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo }; return { + namespacesLoading, + selectedNamespace, availableSources, selectedSources, From 6b9dabd603f9051ab3ac4f9ba8e2f44e60449bfa Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:55:34 +0200 Subject: [PATCH 105/259] refactor: handle potential undefined supportedSignals in destinations list --- .../choose-destination-body/destinations-list/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx index 5dd44ce68..ae906d20d 100644 --- a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx @@ -55,7 +55,7 @@ export const DestinationsList: React.FC = ({ items, setSe title={destinationItem.displayName} iconSrc={destinationItem.imageUrl} hoverText='Select' - monitors={Object.keys(destinationItem.supportedSignals).filter((signal) => destinationItem.supportedSignals[signal].supported)} + monitors={Object.keys(destinationItem.supportedSignals || {}).filter((signal) => destinationItem.supportedSignals[signal].supported)} monitorsWithLabels onClick={() => setSelectedItems(destinationItem)} /> From 69a314a1655966089939d28e85d118addde6d73e Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:57:52 +0200 Subject: [PATCH 106/259] refactor: remove unnecessary delay in redirect after destination creation --- .../containers/main/destinations/add-destination/index.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index a1bb1530e..288e45f6c 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -61,11 +61,8 @@ export function AddDestinationContainer() { await createSources(configuredSources, configuredFutureApps); await Promise.all(configuredDestinations.map(async ({ form }) => await createDestination(form))); - // Delay redirect by 1 seconds to allow the data to reach the backend 1st - setTimeout(() => { - resetState(); - router.push(ROUTES.OVERVIEW); - }, 1000); + resetState(); + router.push(ROUTES.OVERVIEW); }; const isSourcesListEmpty = () => !Object.values(configuredSources).some((sources) => !!sources.length); From 391f76f1780872f5e2b117972588643462339caf Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 14:03:24 +0200 Subject: [PATCH 107/259] fix: UI tests --- frontend/webapp/cypress/constants/index.ts | 4 ++-- frontend/webapp/cypress/e2e/03-sources.cy.ts | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/frontend/webapp/cypress/constants/index.ts b/frontend/webapp/cypress/constants/index.ts index fcc22c8b9..26cf3016f 100644 --- a/frontend/webapp/cypress/constants/index.ts +++ b/frontend/webapp/cypress/constants/index.ts @@ -100,6 +100,6 @@ export const TEXTS = { ACTION_WARN_MODAL_TITLE: `Delete action (${CYPRESS_TEST})`, INSTRUMENTATION_RULE_WARN_MODAL_TITLE: `Delete rule (${CYPRESS_TEST})`, - NOTIF_SOURCES_CREATED: 'successfully added 5 sources', - NOTIF_SOURCES_DELETED: 'successfully deleted 5 sources', + NOTIF_SOURCES_CREATED: 'Successfully created 5 sources', + NOTIF_SOURCES_DELETED: 'Successfully deleted 5 sources', }; diff --git a/frontend/webapp/cypress/e2e/03-sources.cy.ts b/frontend/webapp/cypress/e2e/03-sources.cy.ts index 84d3709e9..7ab0250e0 100644 --- a/frontend/webapp/cypress/e2e/03-sources.cy.ts +++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts @@ -26,8 +26,11 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { - cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); - cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { + cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); + cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); + }); }); }); }); @@ -70,8 +73,11 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { - cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); - cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { + cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); + cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); + }); }); }); }); From b87a13a3ca4807d2c91e0907218dfe6f58a38bff Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 14:18:32 +0200 Subject: [PATCH 108/259] fix: shorter wait times in cypress test --- frontend/webapp/cypress/e2e/03-sources.cy.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/webapp/cypress/e2e/03-sources.cy.ts b/frontend/webapp/cypress/e2e/03-sources.cy.ts index 7ab0250e0..7ec7f82a7 100644 --- a/frontend/webapp/cypress/e2e/03-sources.cy.ts +++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts @@ -20,14 +20,14 @@ describe('Sources CRUD', () => { cy.get(DATA_IDS.MODAL_ADD_SOURCE).should('exist'); cy.get(DATA_IDS.SELECT_NAMESPACE).find(DATA_IDS.CHECKBOX).click(); - // Wait for 3 seconds to allow the namespace & it's resources to be loaded into the UI - cy.wait(3000).then(() => { + // Wait for 2 seconds to allow the namespace & it's resources to be loaded into the UI + cy.wait(2000).then(() => { cy.contains('button', BUTTONS.DONE).click(); cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { - // Wait for 10 seconds to allow the backend to batch an SSE notification - cy.wait(10000).then(() => { + // Wait for 5 seconds to allow the backend to batch an SSE notification + cy.wait(5000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); }); @@ -73,8 +73,8 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { - // Wait for 10 seconds to allow the backend to batch an SSE notification - cy.wait(10000).then(() => { + // Wait for 5 seconds to allow the backend to batch an SSE notification + cy.wait(5000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); }); From 48c457034bfa6bc088682ccacb1e7ec0e1cbdf81 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 14:42:52 +0200 Subject: [PATCH 109/259] feat: add handling for modified instrumentation configs --- frontend/kube/watchers/destination_watcher.go | 8 +-- .../instrumentation_config_watcher.go | 56 +++++++++++++++---- .../instrumentation_instance_watcher.go | 8 +-- .../hooks/destinations/useDestinationCRUD.ts | 6 +- .../webapp/hooks/sources/useSourceCRUD.ts | 10 ++-- 5 files changed, 60 insertions(+), 28 deletions(-) diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index 706fd6928..b290e275d 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -14,7 +14,7 @@ import ( ) var destinationAddedEventBatcher *EventBatcher -var destinationModifiedBatcher *EventBatcher +var destinationModifiedEventBatcher *EventBatcher var destinationDeletedEventBatcher *EventBatcher func StartDestinationWatcher(ctx context.Context, namespace string) error { @@ -33,7 +33,7 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { }, ) - destinationModifiedBatcher = NewEventBatcher( + destinationModifiedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -75,7 +75,7 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { func handleDestinationWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() defer destinationAddedEventBatcher.Cancel() - defer destinationModifiedBatcher.Cancel() + defer destinationModifiedEventBatcher.Cancel() defer destinationDeletedEventBatcher.Cancel() for { select { @@ -126,7 +126,7 @@ func handleModifiedDestination(destination *v1alpha1.Destination) { conditionType = sse.MessageTypeError } - destinationModifiedBatcher.AddEvent(conditionType, data, target) + destinationModifiedEventBatcher.AddEvent(conditionType, data, target) } func handleDeletedDestination(destination *v1alpha1.Destination) { diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index c09107fe0..ddb67b5b8 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -14,11 +14,12 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var instruConfigAddedEventBatcher *EventBatcher -var instruConfigDeletedEventBatcher *EventBatcher +var instrumentationConfigAddedEventBatcher *EventBatcher +var instrumentationConfigModifiedEventBatcher *EventBatcher +var instrumentationConfigDeletedEventBatcher *EventBatcher func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { - instruConfigAddedEventBatcher = NewEventBatcher( + instrumentationConfigAddedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -33,7 +34,22 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er }, ) - instruConfigDeletedEventBatcher = NewEventBatcher( + instrumentationConfigModifiedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventModified, + CRDType: consts.InstrumentationConfig, + SuccessBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Successfully updated %d sources", batchSize) + }, + FailureBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Failed to update %d sources", batchSize) + }, + }, + ) + + instrumentationConfigDeletedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -59,8 +75,9 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer instruConfigAddedEventBatcher.Cancel() - defer instruConfigDeletedEventBatcher.Cancel() + defer instrumentationConfigAddedEventBatcher.Cancel() + defer instrumentationConfigModifiedEventBatcher.Cancel() + defer instrumentationConfigDeletedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -72,15 +89,17 @@ func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.I } switch event.Type { case watch.Added: - handleAddedEvent(event.Object.(*v1alpha1.InstrumentationConfig)) + handleAddedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) + case watch.Modified: + handleModifiedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) case watch.Deleted: - handleDeletedEvent(event.Object.(*v1alpha1.InstrumentationConfig)) + handleDeletedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) } } } } -func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { +func handleAddedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { @@ -90,10 +109,23 @@ func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) data := fmt.Sprintf(`Source "%s" created`, name) - instruConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) + instrumentationConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) +} + +func handleModifiedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) + if err != nil { + genericErrorMessage(sse.MessageEventAdded, consts.InstrumentationConfig, err.Error()) + return + } + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf(`Source "%s" updated`, name) + instrumentationConfigModifiedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } -func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { +func handleDeletedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { @@ -103,5 +135,5 @@ func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) data := fmt.Sprintf(`Source "%s" deleted`, name) - instruConfigDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) + instrumentationConfigDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 5f3bc3c03..0992faf63 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -14,10 +14,10 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var instruInstanceModifiedBatcher *EventBatcher +var instrumentationInstanceModifiedEventBatcher *EventBatcher func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) error { - instruInstanceModifiedBatcher = NewEventBatcher( + instrumentationInstanceModifiedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -41,7 +41,7 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer instruInstanceModifiedBatcher.Cancel() + defer instrumentationInstanceModifiedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -84,5 +84,5 @@ func handleModifiedInstrumentationInstance(instruInstance *v1alpha1.Instrumentat target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInstance.Status.Reason, instruInstance.Status.Message) - instruInstanceModifiedBatcher.AddEvent(sse.MessageTypeError, data, target) + instrumentationInstanceModifiedEventBatcher.AddEvent(sse.MessageTypeError, data, target) } diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 9fc73cc76..93ea5b701 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -57,15 +57,15 @@ export const useDestinationCRUD = (params?: Params) => { destinations: data?.computePlatform?.destinations || [], createDestination: (destination: DestinationInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating destination...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Creating destination...', undefined, true); createDestination({ variables: { destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, updateDestination: (id: string, destination: DestinationInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating destination...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating destination...', undefined, true); updateDestination({ variables: { id, destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, deleteDestination: (id: string) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting destination...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Deleting destination...', undefined, true); deleteDestination({ variables: { id } }); }, }; diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index f62e6e366..0d96d8d17 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -14,8 +14,8 @@ export const useSourceCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { configuredSources, setConfiguredSources } = useAppStore(); - const { persistNamespace } = useNamespace(); const { data } = useComputePlatform(); + const { persistNamespace } = useNamespace(); const { addNotification } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { @@ -64,7 +64,7 @@ export const useSourceCRUD = (params?: Params) => { const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: () => handleComplete(ACTION.UPDATE), + onCompleted: (res, req) => handleComplete(ACTION.UPDATE), }); const persistNamespaces = async (items: { [key: string]: boolean }) => { @@ -93,16 +93,16 @@ export const useSourceCRUD = (params?: Params) => { sources: data?.computePlatform.k8sActualSources || [], createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating sources...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Creating sources...', undefined, true); await persistNamespaces(futureSelectAppsList); await persistSources(selectAppsList, true); }, updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating sources...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating sources...', undefined, true); await updateSource({ variables: { sourceId, patchSourceRequest } }); }, deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting sources...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Deleting sources...', undefined, true); await persistSources(selectAppsList, false); }, }; From 0b1b0736633dc21608a85012fc466652ede265fa Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 15:52:41 +0200 Subject: [PATCH 110/259] fix: UI tests again --- frontend/webapp/cypress/e2e/03-sources.cy.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/webapp/cypress/e2e/03-sources.cy.ts b/frontend/webapp/cypress/e2e/03-sources.cy.ts index 7ec7f82a7..7ab0250e0 100644 --- a/frontend/webapp/cypress/e2e/03-sources.cy.ts +++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts @@ -20,14 +20,14 @@ describe('Sources CRUD', () => { cy.get(DATA_IDS.MODAL_ADD_SOURCE).should('exist'); cy.get(DATA_IDS.SELECT_NAMESPACE).find(DATA_IDS.CHECKBOX).click(); - // Wait for 2 seconds to allow the namespace & it's resources to be loaded into the UI - cy.wait(2000).then(() => { + // Wait for 3 seconds to allow the namespace & it's resources to be loaded into the UI + cy.wait(3000).then(() => { cy.contains('button', BUTTONS.DONE).click(); cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { - // Wait for 5 seconds to allow the backend to batch an SSE notification - cy.wait(5000).then(() => { + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); }); @@ -73,8 +73,8 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { - // Wait for 5 seconds to allow the backend to batch an SSE notification - cy.wait(5000).then(() => { + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); }); From 9f41850713b7ba67923cce663ff2db8b3ba180fe Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 15:58:53 +0200 Subject: [PATCH 111/259] feat: mark instrumented sources when gettgin namespace --- frontend/graph/generated.go | 153 +++++++----------- frontend/graph/model/models_gen.go | 6 +- frontend/graph/schema.graphqls | 4 +- frontend/graph/schema.resolvers.go | 43 +++-- frontend/services/sources.go | 20 +-- .../graphql/queries/compute-platform.ts | 7 +- .../hooks/compute-platform/useNamespace.ts | 19 ++- .../webapp/hooks/sources/useSourceFormData.ts | 4 +- frontend/webapp/types/sources.ts | 2 +- k8sutils/pkg/workload/workload.go | 9 -- 10 files changed, 114 insertions(+), 153 deletions(-) diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index a07f00471..b41a613fe 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -257,9 +257,8 @@ type ComplexityRoot struct { } K8sActualNamespace struct { - InstrumentationLabelEnabled func(childComplexity int) int - K8sActualSources func(childComplexity int, instrumentationLabeled *bool) int - Name func(childComplexity int) int + K8sActualSources func(childComplexity int) int + Name func(childComplexity int) int } K8sActualSource struct { @@ -270,6 +269,7 @@ type ComplexityRoot struct { Namespace func(childComplexity int) int NumberOfInstances func(childComplexity int) int ReportedName func(childComplexity int) int + Selected func(childComplexity int) int } LatencySamplerAction struct { @@ -477,7 +477,7 @@ type DestinationResolver interface { Conditions(ctx context.Context, obj *model.Destination) ([]*model.Condition, error) } type K8sActualNamespaceResolver interface { - K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace, instrumentationLabeled *bool) ([]*model.K8sActualSource, error) + K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace) ([]*model.K8sActualSource, error) } type MutationResolver interface { CreateNewDestination(ctx context.Context, destination model.DestinationInput) (*model.Destination, error) @@ -1362,24 +1362,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationRule.Workloads(childComplexity), true - case "K8sActualNamespace.instrumentationLabelEnabled": - if e.complexity.K8sActualNamespace.InstrumentationLabelEnabled == nil { - break - } - - return e.complexity.K8sActualNamespace.InstrumentationLabelEnabled(childComplexity), true - case "K8sActualNamespace.k8sActualSources": if e.complexity.K8sActualNamespace.K8sActualSources == nil { break } - args, err := ec.field_K8sActualNamespace_k8sActualSources_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.K8sActualNamespace.K8sActualSources(childComplexity, args["instrumentationLabeled"].(*bool)), true + return e.complexity.K8sActualNamespace.K8sActualSources(childComplexity), true case "K8sActualNamespace.name": if e.complexity.K8sActualNamespace.Name == nil { @@ -1437,6 +1425,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualSource.ReportedName(childComplexity), true + case "K8sActualSource.selected": + if e.complexity.K8sActualSource.Selected == nil { + break + } + + return e.complexity.K8sActualSource.Selected(childComplexity), true + case "LatencySamplerAction.details": if e.complexity.LatencySamplerAction.Details == nil { break @@ -2516,21 +2511,6 @@ func (ec *executionContext) field_ComputePlatform_k8sActualNamespace_args(ctx co return args, nil } -func (ec *executionContext) field_K8sActualNamespace_k8sActualSources_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 *bool - if tmp, ok := rawArgs["instrumentationLabeled"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("instrumentationLabeled")) - arg0, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) - if err != nil { - return nil, err - } - } - args["instrumentationLabeled"] = arg0 - return args, nil -} - func (ec *executionContext) field_Mutation_createAction_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -3878,8 +3858,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespaces(_ c switch field.Name { case "name": return ec.fieldContext_K8sActualNamespace_name(ctx, field) - case "instrumentationLabelEnabled": - return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) case "k8sActualSources": return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } @@ -3927,8 +3905,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx switch field.Name { case "name": return ec.fieldContext_K8sActualNamespace_name(ctx, field) - case "instrumentationLabelEnabled": - return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) case "k8sActualSources": return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } @@ -3996,6 +3972,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "selected": + return ec.fieldContext_K8sActualSource_selected(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) case "containers": @@ -8535,47 +8513,6 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_name(_ context.Conte return fc, nil } -func (ec *executionContext) _K8sActualNamespace_instrumentationLabelEnabled(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.InstrumentationLabelEnabled, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*bool) - fc.Result = res - return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_K8sActualNamespace_instrumentationLabelEnabled(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "K8sActualNamespace", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) if err != nil { @@ -8590,7 +8527,7 @@ func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Con }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.K8sActualNamespace().K8sActualSources(rctx, obj, fc.Args["instrumentationLabeled"].(*bool)) + return ec.resolvers.K8sActualNamespace().K8sActualSources(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8607,7 +8544,7 @@ func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Con return ec.marshalNK8sActualSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualSource(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualNamespace", Field: field, @@ -8623,6 +8560,8 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "selected": + return ec.fieldContext_K8sActualSource_selected(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) case "containers": @@ -8633,17 +8572,6 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_K8sActualNamespace_k8sActualSources_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } @@ -8820,6 +8748,47 @@ func (ec *executionContext) fieldContext_K8sActualSource_numberOfInstances(_ con return fc, nil } +func (ec *executionContext) _K8sActualSource_selected(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_selected(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Selected, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*bool) + fc.Result = res + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_K8sActualSource_selected(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "K8sActualSource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) if err != nil { @@ -19312,8 +19281,6 @@ func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.Sel if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "instrumentationLabelEnabled": - out.Values[i] = ec._K8sActualNamespace_instrumentationLabelEnabled(ctx, field, obj) case "k8sActualSources": field := field @@ -19401,6 +19368,8 @@ func (ec *executionContext) _K8sActualSource(ctx context.Context, sel ast.Select } case "numberOfInstances": out.Values[i] = ec._K8sActualSource_numberOfInstances(ctx, field, obj) + case "selected": + out.Values[i] = ec._K8sActualSource_selected(ctx, field, obj) case "reportedName": out.Values[i] = ec._K8sActualSource_reportedName(ctx, field, obj) case "containers": diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 9bef3d092..bf9bf142b 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -296,9 +296,8 @@ type InstrumentationRuleInput struct { } type K8sActualNamespace struct { - Name string `json:"name"` - InstrumentationLabelEnabled *bool `json:"instrumentationLabelEnabled,omitempty"` - K8sActualSources []*K8sActualSource `json:"k8sActualSources"` + Name string `json:"name"` + K8sActualSources []*K8sActualSource `json:"k8sActualSources"` } type K8sActualSource struct { @@ -306,6 +305,7 @@ type K8sActualSource struct { Name string `json:"name"` Kind K8sResourceKind `json:"kind"` NumberOfInstances *int `json:"numberOfInstances,omitempty"` + Selected *bool `json:"selected,omitempty"` ReportedName *string `json:"reportedName,omitempty"` Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` Conditions []*Condition `json:"conditions,omitempty"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 76be28369..602e4440f 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -61,8 +61,7 @@ type Condition { type K8sActualNamespace { name: String! - instrumentationLabelEnabled: Boolean - k8sActualSources(instrumentationLabeled: Boolean): [K8sActualSource]! + k8sActualSources: [K8sActualSource]! } input K8sDesiredNamespaceInput { @@ -78,6 +77,7 @@ type K8sActualSource { name: String! kind: K8sResourceKind! numberOfInstances: Int + selected: Boolean reportedName: String containers: [SourceContainerRuntimeDetails!] conditions: [Condition!] diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index c18266f24..5c341760e 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -19,7 +19,6 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/odigos_describe" "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" - "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) @@ -30,17 +29,8 @@ func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj * K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) for i, namespace := range namespacesResponse.Namespaces { - - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) - K8sActualNamespaces[i] = &model.K8sActualNamespace{ - Name: namespace.Name, - InstrumentationLabelEnabled: nsInstrumented, + Name: namespace.Name, } } @@ -49,7 +39,12 @@ func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj * // K8sActualNamespace is the resolver for the k8sActualNamespace field. func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) { - namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, name, nil) + namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, name) if err != nil { return nil, err } @@ -60,17 +55,9 @@ func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *m namespaceActualSourcesPointers[i] = &source } - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) - return &model.K8sActualNamespace{ - Name: name, - InstrumentationLabelEnabled: nsInstrumented, - K8sActualSources: namespaceActualSourcesPointers, + Name: namespace.Name, + K8sActualSources: namespaceActualSourcesPointers, }, nil } @@ -268,8 +255,8 @@ func (r *destinationResolver) Conditions(ctx context.Context, obj *model.Destina } // K8sActualSources is the resolver for the k8sActualSources field. -func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace, instrumentationLabeled *bool) ([]*model.K8sActualSource, error) { - namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, obj.Name, instrumentationLabeled) +func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace) ([]*model.K8sActualSource, error) { + namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, obj.Name) if err != nil { return nil, err } @@ -278,6 +265,14 @@ func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj * namespaceActualSourcesPointers := make([]*model.K8sActualSource, len(namespaceActualSources)) for i, source := range namespaceActualSources { namespaceActualSourcesPointers[i] = &source + + crd, err := services.GetSourceCRD(ctx, obj.Name, source.Name, services.WorkloadKind(source.Kind)) + instrumented := false + if crd != nil && err == nil { + instrumented = true + } + + namespaceActualSourcesPointers[i].Selected = &instrumented } return namespaceActualSourcesPointers, nil diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 394853e51..add2b7a46 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -97,7 +97,7 @@ func AddHealthyInstrumentationInstancesCondition(ctx context.Context, instruConf return nil } -func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func GetWorkloadsInNamespace(ctx context.Context, nsName string) ([]model.K8sActualSource, error) { namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, nsName, metav1.GetOptions{}) if err != nil { return nil, err @@ -112,19 +112,19 @@ func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentation g.Go(func() error { var err error - deps, err = getDeployments(ctx, *namespace, instrumentationLabeled) + deps, err = getDeployments(ctx, *namespace) return err }) g.Go(func() error { var err error - ss, err = getStatefulSets(ctx, *namespace, instrumentationLabeled) + ss, err = getStatefulSets(ctx, *namespace) return err }) g.Go(func() error { var err error - dss, err = getDaemonSets(ctx, *namespace, instrumentationLabeled) + dss, err = getDaemonSets(ctx, *namespace) return err }) @@ -140,7 +140,7 @@ func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentation return items, nil } -func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getDeployments(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().Deployments(namespace.Name).List, ctx, metav1.ListOptions{}, func(deps *appsv1.DeploymentList) error { for _, dep := range deps.Items { @@ -162,7 +162,7 @@ func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumenta return response, nil } -func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getDaemonSets(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().DaemonSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(dss *appsv1.DaemonSetList) error { for _, ds := range dss.Items { @@ -184,7 +184,7 @@ func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentat return response, nil } -func getStatefulSets(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getStatefulSets(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().StatefulSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(sss *appsv1.StatefulSetList) error { for _, ss := range sss.Items { @@ -264,7 +264,7 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } -func getSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { +func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { source, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ consts.OdigosNamespaceAnnotation: nsName, consts.OdigosWorkloadNameAnnotation: workloadName, @@ -293,7 +293,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } - source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { return fmt.Errorf(`source "%s" already exists`, workloadName) } @@ -321,7 +321,7 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } - source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if err != nil { return err } diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index afe9ca761..c02fd80dd 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -11,6 +11,7 @@ export const GET_COMPUTE_PLATFORM = gql` name kind numberOfInstances + selected reportedName containers { containerName @@ -102,15 +103,15 @@ export const GET_COMPUTE_PLATFORM = gql` `; export const GET_NAMESPACES = gql` - query GetK8sActualNamespace($namespaceName: String!, $instrumentationLabeled: Boolean) { + query GetK8sActualNamespace($namespaceName: String!) { computePlatform { k8sActualNamespace(name: $namespaceName) { name - instrumentationLabelEnabled - k8sActualSources(instrumentationLabeled: $instrumentationLabeled) { + k8sActualSources { kind name numberOfInstances + selected } } } diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index b84fa8022..1383550f0 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -5,30 +5,35 @@ import { useComputePlatform } from './useComputePlatform'; import { GET_NAMESPACES, PERSIST_NAMESPACE } from '@/graphql'; import { type ComputePlatform, NOTIFICATION_TYPE, type PersistNamespaceItemInput } from '@/types'; -export const useNamespace = (namespaceName?: string, instrumentationLabeled = null as boolean | null) => { +export const useNamespace = (namespaceName?: string) => { const { addNotification } = useNotificationStore(); const cp = useComputePlatform(); - const { data, loading, error } = useQuery(GET_NAMESPACES, { + const { data, loading } = useQuery(GET_NAMESPACES, { skip: !namespaceName, fetchPolicy: 'cache-first', - variables: { namespaceName, instrumentationLabeled }, + variables: { namespaceName }, + onError: (error) => + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, + }), }); const [persistNamespaceMutation] = useMutation(PERSIST_NAMESPACE, { onError: (error) => addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: error.name || ACTION.FETCH, + title: error.name || ACTION.UPDATE, message: error.cause?.message || error.message, }), }); return { + loading, + data: data?.computePlatform.k8sActualNamespace, allNamespaces: cp.data?.computePlatform.k8sActualNamespaces, persistNamespace: async (namespace: PersistNamespaceItemInput) => await persistNamespaceMutation({ variables: { namespace } }), - data: data?.computePlatform.k8sActualNamespace, - loading, - error, }; }; diff --git a/frontend/webapp/hooks/sources/useSourceFormData.ts b/frontend/webapp/hooks/sources/useSourceFormData.ts index b877a1072..7f31a3ff9 100644 --- a/frontend/webapp/hooks/sources/useSourceFormData.ts +++ b/frontend/webapp/hooks/sources/useSourceFormData.ts @@ -52,7 +52,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo const [availableSources, setAvailableSources] = useState(appStore.availableSources); const [selectedSources, setSelectedSources] = useState(appStore.configuredSources); const [selectedFutureApps, setSelectedFutureApps] = useState(appStore.configuredFutureApps); - const { allNamespaces, data: namespacesData, loading: namespacesLoading } = useNamespace(selectedNamespace, false); + const { allNamespaces, data: namespacesData, loading: namespacesLoading } = useNamespace(selectedNamespace); useEffect(() => { if (!!allNamespaces?.length) { @@ -78,7 +78,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo // set available sources for current selected namespace const { name, k8sActualSources = [] } = namespacesData; setAvailableSources((prev) => ({ ...prev, [name]: k8sActualSources })); - setSelectedSources((prev) => ({ ...prev, [name]: prev[name] || [] })); + setSelectedSources((prev) => ({ ...prev, [name]: prev[name] || k8sActualSources.filter(({ selected }) => selected) })); } }, [namespacesData]); diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index 62c207116..945b8d88a 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -13,10 +13,10 @@ export type K8sActualSource = { name: string; kind: string; numberOfInstances: number; + selected: boolean; reportedName: string; containers: Array; conditions: Array; - selected?: boolean; // not from backend }; export type WorkloadId = { diff --git a/k8sutils/pkg/workload/workload.go b/k8sutils/pkg/workload/workload.go index 581540cd0..16fd5e699 100644 --- a/k8sutils/pkg/workload/workload.go +++ b/k8sutils/pkg/workload/workload.go @@ -111,15 +111,6 @@ func IsInstrumentationDisabledExplicitly(obj client.Object) bool { return false } -func GetInstrumentationLabelValue(labels map[string]string) *bool { - if val, exists := labels[consts.OdigosInstrumentationLabel]; exists { - enabled := val == consts.InstrumentationEnabled - return &enabled - } - - return nil -} - func GetWorkloadObject(ctx context.Context, objectKey client.ObjectKey, kind WorkloadKind, kubeClient client.Client) (metav1.Object, error) { switch kind { case WorkloadKindDeployment: From bb3472f921a38b543406f53d345dc43320a9ae3b Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 16:47:59 +0200 Subject: [PATCH 112/259] feat: refactor source handling to allow UI to persist sources without errors --- frontend/services/sources.go | 6 ++-- .../destinations/add-destination/index.tsx | 13 +++++-- .../overview/multi-source-control/index.tsx | 10 ++++-- .../choose-source-modal/index.tsx | 14 ++++++-- .../sources/source-drawer-container/index.tsx | 5 +-- .../hooks/compute-platform/useNamespace.ts | 1 - .../webapp/hooks/sources/useSourceCRUD.ts | 35 +++++++------------ 7 files changed, 50 insertions(+), 34 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index add2b7a46..4167548d2 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -295,7 +295,8 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return fmt.Errorf(`source "%s" already exists`, workloadName) + // we don't want to throw error, because we want to allow the UI to send an array of all sources even if instrumented + return nil } newSource := &v1alpha1.Source{ @@ -323,7 +324,8 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if err != nil { - return err + // we don't want to throw error, because we want to allow the UI to send an array of all sources even if not instrumented + return nil } err = kube.DefaultClient.OdigosClient.Sources(nsName).Delete(ctx, source.Name, metav1.DeleteOptions{}) diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index 288e45f6c..99b01179a 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -41,7 +41,7 @@ const StyledAddDestinationButton = styled(Button)` export function AddDestinationContainer() { const router = useRouter(); - const { createSources } = useSourceCRUD(); + const { persistSources } = useSourceCRUD(); const { createDestination } = useDestinationCRUD(); const { configuredSources, configuredFutureApps, configuredDestinations, resetState } = useAppStore((state) => state); @@ -58,7 +58,16 @@ export function AddDestinationContainer() { const clickDone = async () => { setIsLoading(true); - await createSources(configuredSources, configuredFutureApps); + const payload: typeof configuredSources = {}; + + Object.entries(configuredSources).forEach(([namespace, sources]) => { + payload[namespace] = sources.map((source) => ({ + ...source, + selected: true, + })); + }); + + await persistSources(payload, configuredFutureApps); await Promise.all(configuredDestinations.map(async ({ form }) => await createDestination(form))); resetState(); diff --git a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx index 4181dda72..ebe13b893 100644 --- a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx +++ b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx @@ -31,7 +31,7 @@ export const MultiSourceControl = () => { animateOut: slide.out['center'], }); - const { sources, deleteSources } = useSourceCRUD(); + const { sources, persistSources } = useSourceCRUD(); const { configuredSources, setConfiguredSources } = useAppStore(); const [isWarnModalOpen, setIsWarnModalOpen] = useState(false); @@ -50,7 +50,13 @@ export const MultiSourceControl = () => { }; const onDelete = () => { - deleteSources(configuredSources); + const payload: typeof configuredSources = {}; + + Object.entries(configuredSources).forEach(([namespace, selectedSources]) => { + payload[namespace] = selectedSources.map(({ selected, ...rest }) => ({ ...rest, selected: false })); + }); + + persistSources(payload, {}); onDeselect(); setIsWarnModalOpen(false); }; diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx index 9fe3201ab..d3c02e349 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx @@ -12,12 +12,20 @@ export const AddSourceModal: React.FC = ({ isOpen, onClose }) => { useKeyDown({ key: 'Enter', active: isOpen }, () => handleSubmit()); const menuState = useSourceFormData(); - const { createSources } = useSourceCRUD({ onSuccess: onClose }); + const { persistSources } = useSourceCRUD({ onSuccess: onClose }); const handleSubmit = async () => { - const { selectedSources, selectedFutureApps } = menuState; + const { availableSources, selectedSources, selectedFutureApps } = menuState; + const payload: typeof availableSources = {}; - await createSources(selectedSources, selectedFutureApps); + Object.entries(availableSources).forEach(([namespace, sources]) => { + payload[namespace] = sources.map((source) => ({ + ...source, + selected: !!selectedSources[namespace].find(({ kind, name }) => kind === source.kind && name === source.name), + })); + }); + + await persistSources(payload, selectedFutureApps); }; return ( diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx index 4ea0eb2ac..5e23efabb 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -33,7 +33,7 @@ const DataContainer = styled.div` export const SourceDrawer: React.FC = () => { const { selectedItem, setSelectedItem } = useDrawerStore(); - const { deleteSources, updateSource } = useSourceCRUD({ + const { persistSources, updateSource } = useSourceCRUD({ onSuccess: (type) => { setIsEditing(false); setIsFormDirty(false); @@ -112,7 +112,8 @@ export const SourceDrawer: React.FC = () => { const handleDelete = async () => { const { namespace } = item; - await deleteSources({ [namespace]: [item] }); + + await persistSources({ [namespace]: [{ ...item, selected: false }] }, {}); }; const handleSave = async () => { diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index 1383550f0..33d5a5c1d 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -11,7 +11,6 @@ export const useNamespace = (namespaceName?: string) => { const { data, loading } = useQuery(GET_NAMESPACES, { skip: !namespaceName, - fetchPolicy: 'cache-first', variables: { namespaceName }, onError: (error) => addNotification({ diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 0d96d8d17..5744ea9db 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -39,25 +39,20 @@ export const useSourceCRUD = (params?: Params) => { }; const [createOrDeleteSources, cdState] = useMutation<{ persistK8sSources: boolean }>(PERSIST_SOURCE, { - onError: (error, req) => { - const { selected } = req?.variables?.sources?.[0] || {}; - const action = selected ? ACTION.CREATE : ACTION.DELETE; - - handleError(action, error.message); - }, + onError: (error, req) => handleError('', error.message), onCompleted: (res, req) => { const count = req?.variables?.sources.length; - const namespace = req?.variables?.namespace; - const { name, kind, selected } = req?.variables?.sources?.[0] || {}; - const action = selected ? ACTION.CREATE : ACTION.DELETE; - if (count > 1) { - handleComplete(action); - } else { + if (count === 1) { + const namespace = req?.variables?.namespace; + const { name, kind, selected } = req?.variables?.sources?.[0] || {}; const id = { namespace, name, kind }; + if (!selected) removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE)); if (!selected) setConfiguredSources({ ...configuredSources, [namespace]: configuredSources[namespace]?.filter((source) => source.name !== name) || [] }); - handleComplete(action); + handleComplete(selected ? ACTION.CREATE : ACTION.DELETE); + } else { + handleComplete(''); } }, }); @@ -73,7 +68,7 @@ export const useSourceCRUD = (params?: Params) => { } }; - const persistSources = async (items: { [key: string]: K8sActualSource[] }, selected: boolean) => { + const persistSources = async (items: { [key: string]: K8sActualSource[] }) => { for (const [namespace, sources] of Object.entries(items)) { await createOrDeleteSources({ variables: { @@ -81,7 +76,7 @@ export const useSourceCRUD = (params?: Params) => { sources: sources.map((source) => ({ kind: source.kind, name: source.name, - selected, + selected: source.selected, })), }, }); @@ -92,18 +87,14 @@ export const useSourceCRUD = (params?: Params) => { loading: cdState.loading || uState.loading, sources: data?.computePlatform.k8sActualSources || [], - createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Creating sources...', undefined, true); + persistSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Persisting sources...', undefined, true); await persistNamespaces(futureSelectAppsList); - await persistSources(selectAppsList, true); + await persistSources(selectAppsList); }, updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating sources...', undefined, true); await updateSource({ variables: { sourceId, patchSourceRequest } }); }, - deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Deleting sources...', undefined, true); - await persistSources(selectAppsList, false); - }, }; }; From 01ac3bb1951db572b7c5c9bdba04b39011609d50 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 11:50:24 +0200 Subject: [PATCH 113/259] feat: add sources field with spec and status to compute platform --- frontend/gqlgen.yml | 6 +- frontend/graph/generated.go | 572 +++++++++++++++++- frontend/graph/model/models_gen.go | 16 +- frontend/graph/schema.graphqls | 16 +- frontend/graph/schema.resolvers.go | 26 + .../graphql/queries/compute-platform.ts | 18 + frontend/webapp/types/compute-platform.ts | 5 +- frontend/webapp/types/sources.ts | 9 + 8 files changed, 642 insertions(+), 26 deletions(-) diff --git a/frontend/gqlgen.yml b/frontend/gqlgen.yml index 562c27730..fda4093fe 100644 --- a/frontend/gqlgen.yml +++ b/frontend/gqlgen.yml @@ -50,12 +50,14 @@ models: resolver: true k8sActualSource: resolver: true + k8sActualSources: + resolver: true + sources: + resolver: true destinations: resolver: true actions: resolver: true - k8sActualSources: - resolver: true instrumentationRules: resolver: true K8sActualNamespace: diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index caba2e253..5dc7c723f 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -87,6 +87,7 @@ type ComplexityRoot struct { K8sActualNamespaces func(childComplexity int) int K8sActualSource func(childComplexity int, name *string, namespace *string, kind *string) int K8sActualSources func(childComplexity int) int + Sources func(childComplexity int) int } Condition struct { @@ -452,6 +453,11 @@ type ComplexityRoot struct { TotalDataSent func(childComplexity int) int } + Source struct { + Spec func(childComplexity int) int + Status func(childComplexity int) int + } + SourceAnalyze struct { InstrumentationConfig func(childComplexity int) int InstrumentationDevice func(childComplexity int) int @@ -473,6 +479,14 @@ type ComplexityRoot struct { RuntimeVersion func(childComplexity int) int } + SourceSpec struct { + Workload func(childComplexity int) int + } + + SourceStatus struct { + Conditions func(childComplexity int) int + } + SupportedSignals struct { Logs func(childComplexity int) int Metrics func(childComplexity int) int @@ -493,6 +507,7 @@ type ComputePlatformResolver interface { K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) + Sources(ctx context.Context, obj *model.ComputePlatform) ([]*model.Source, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) InstrumentationRules(ctx context.Context, obj *model.ComputePlatform) ([]*model.InstrumentationRule, error) @@ -749,6 +764,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ComputePlatform.K8sActualSources(childComplexity), true + case "ComputePlatform.sources": + if e.complexity.ComputePlatform.Sources == nil { + break + } + + return e.complexity.ComputePlatform.Sources(childComplexity), true + case "Condition.lastTransitionTime": if e.complexity.Condition.LastTransitionTime == nil { break @@ -2320,6 +2342,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SingleSourceMetricsResponse.TotalDataSent(childComplexity), true + case "Source.spec": + if e.complexity.Source.Spec == nil { + break + } + + return e.complexity.Source.Spec(childComplexity), true + + case "Source.status": + if e.complexity.Source.Status == nil { + break + } + + return e.complexity.Source.Status(childComplexity), true + case "SourceAnalyze.instrumentationConfig": if e.complexity.SourceAnalyze.InstrumentationConfig == nil { break @@ -2425,6 +2461,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SourceContainerRuntimeDetails.RuntimeVersion(childComplexity), true + case "SourceSpec.workload": + if e.complexity.SourceSpec.Workload == nil { + break + } + + return e.complexity.SourceSpec.Workload(childComplexity), true + + case "SourceStatus.conditions": + if e.complexity.SourceStatus.Conditions == nil { + break + } + + return e.complexity.SourceStatus.Conditions(childComplexity), true + case "SupportedSignals.logs": if e.complexity.SupportedSignals.Logs == nil { break @@ -4240,6 +4290,56 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont return fc, nil } +func (ec *executionContext) _ComputePlatform_sources(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ComputePlatform_sources(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.ComputePlatform().Sources(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Source) + fc.Result = res + return ec.marshalNSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ComputePlatform_sources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ComputePlatform", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "spec": + return ec.fieldContext_Source_spec(ctx, field) + case "status": + return ec.fieldContext_Source_status(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Source", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _ComputePlatform_destinations(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ComputePlatform_destinations(ctx, field) if err != nil { @@ -8739,10 +8839,10 @@ func (ec *executionContext) fieldContext_InstrumentationRule_workloads(_ context switch field.Name { case "namespace": return ec.fieldContext_PodWorkload_namespace(ctx, field) - case "kind": - return ec.fieldContext_PodWorkload_kind(ctx, field) case "name": return ec.fieldContext_PodWorkload_name(ctx, field) + case "kind": + return ec.fieldContext_PodWorkload_kind(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type PodWorkload", field.Name) }, @@ -13017,8 +13117,8 @@ func (ec *executionContext) fieldContext_PodWorkload_namespace(_ context.Context return fc, nil } -func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PodWorkload_kind(ctx, field) +func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PodWorkload_name(ctx, field) if err != nil { return graphql.Null } @@ -13031,7 +13131,7 @@ func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -13043,26 +13143,26 @@ func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(model.K8sResourceKind) + res := resTmp.(string) fc.Result = res - return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PodWorkload_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PodWorkload_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PodWorkload", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type K8sResourceKind does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PodWorkload_name(ctx, field) +func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PodWorkload_kind(ctx, field) if err != nil { return graphql.Null } @@ -13075,7 +13175,7 @@ func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -13087,19 +13187,19 @@ func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(string) + res := resTmp.(model.K8sResourceKind) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PodWorkload_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PodWorkload_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PodWorkload", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type K8sResourceKind does not have child fields") }, } return fc, nil @@ -13453,6 +13553,8 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context return ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) case "k8sActualSources": return ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) + case "sources": + return ec.fieldContext_ComputePlatform_sources(ctx, field) case "destinations": return ec.fieldContext_ComputePlatform_destinations(ctx, field) case "actions": @@ -14744,6 +14846,102 @@ func (ec *executionContext) fieldContext_SingleSourceMetricsResponse_throughput( return fc, nil } +func (ec *executionContext) _Source_spec(ctx context.Context, field graphql.CollectedField, obj *model.Source) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Source_spec(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Spec, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.SourceSpec) + fc.Result = res + return ec.marshalNSourceSpec2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceSpec(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Source_spec(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Source", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "workload": + return ec.fieldContext_SourceSpec_workload(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceSpec", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Source_status(ctx context.Context, field graphql.CollectedField, obj *model.Source) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Source_status(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Status, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.SourceStatus) + fc.Result = res + return ec.marshalNSourceStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceStatus(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Source_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Source", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "conditions": + return ec.fieldContext_SourceStatus_conditions(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceStatus", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _SourceAnalyze_name(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SourceAnalyze_name(ctx, field) if err != nil { @@ -15474,6 +15672,111 @@ func (ec *executionContext) fieldContext_SourceContainerRuntimeDetails_otherAgen return fc, nil } +func (ec *executionContext) _SourceSpec_workload(ctx context.Context, field graphql.CollectedField, obj *model.SourceSpec) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceSpec_workload(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Workload, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.PodWorkload) + fc.Result = res + return ec.marshalNPodWorkload2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPodWorkload(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SourceSpec_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SourceSpec", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "namespace": + return ec.fieldContext_PodWorkload_namespace(ctx, field) + case "name": + return ec.fieldContext_PodWorkload_name(ctx, field) + case "kind": + return ec.fieldContext_PodWorkload_kind(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PodWorkload", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _SourceStatus_conditions(ctx context.Context, field graphql.CollectedField, obj *model.SourceStatus) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceStatus_conditions(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Conditions, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*model.Condition) + fc.Result = res + return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SourceStatus_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SourceStatus", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "status": + return ec.fieldContext_Condition_status(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) + case "reason": + return ec.fieldContext_Condition_reason(ctx, field) + case "message": + return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _SupportedSignals_traces(ctx context.Context, field graphql.CollectedField, obj *model.SupportedSignals) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SupportedSignals_traces(ctx, field) if err != nil { @@ -18705,6 +19008,42 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "sources": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ComputePlatform_sources(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "destinations": field := field @@ -21167,13 +21506,13 @@ func (ec *executionContext) _PodWorkload(ctx context.Context, sel ast.SelectionS if out.Values[i] == graphql.Null { out.Invalids++ } - case "kind": - out.Values[i] = ec._PodWorkload_kind(ctx, field, obj) + case "name": + out.Values[i] = ec._PodWorkload_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "name": - out.Values[i] = ec._PodWorkload_name(ctx, field, obj) + case "kind": + out.Values[i] = ec._PodWorkload_kind(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -21692,6 +22031,50 @@ func (ec *executionContext) _SingleSourceMetricsResponse(ctx context.Context, se return out } +var sourceImplementors = []string{"Source"} + +func (ec *executionContext) _Source(ctx context.Context, sel ast.SelectionSet, obj *model.Source) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sourceImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Source") + case "spec": + out.Values[i] = ec._Source_spec(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "status": + out.Values[i] = ec._Source_status(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var sourceAnalyzeImplementors = []string{"SourceAnalyze"} func (ec *executionContext) _SourceAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.SourceAnalyze) graphql.Marshaler { @@ -21829,6 +22212,81 @@ func (ec *executionContext) _SourceContainerRuntimeDetails(ctx context.Context, return out } +var sourceSpecImplementors = []string{"SourceSpec"} + +func (ec *executionContext) _SourceSpec(ctx context.Context, sel ast.SelectionSet, obj *model.SourceSpec) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sourceSpecImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SourceSpec") + case "workload": + out.Values[i] = ec._SourceSpec_workload(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var sourceStatusImplementors = []string{"SourceStatus"} + +func (ec *executionContext) _SourceStatus(ctx context.Context, sel ast.SelectionSet, obj *model.SourceStatus) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sourceStatusImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SourceStatus") + case "conditions": + out.Values[i] = ec._SourceStatus_conditions(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var supportedSignalsImplementors = []string{"SupportedSignals"} func (ec *executionContext) _SupportedSignals(ctx context.Context, sel ast.SelectionSet, obj *model.SupportedSignals) graphql.Marshaler { @@ -23733,6 +24191,60 @@ func (ec *executionContext) marshalNSingleSourceMetricsResponse2ᚖgithubᚗcom return ec._SingleSourceMetricsResponse(ctx, sel, v) } +func (ec *executionContext) marshalNSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Source) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSource(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSource(ctx context.Context, sel ast.SelectionSet, v *model.Source) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Source(ctx, sel, v) +} + func (ec *executionContext) marshalNSourceAnalyze2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceAnalyze(ctx context.Context, sel ast.SelectionSet, v model.SourceAnalyze) graphql.Marshaler { return ec._SourceAnalyze(ctx, sel, &v) } @@ -23757,6 +24269,26 @@ func (ec *executionContext) marshalNSourceContainerRuntimeDetails2ᚖgithubᚗco return ec._SourceContainerRuntimeDetails(ctx, sel, v) } +func (ec *executionContext) marshalNSourceSpec2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceSpec(ctx context.Context, sel ast.SelectionSet, v *model.SourceSpec) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._SourceSpec(ctx, sel, v) +} + +func (ec *executionContext) marshalNSourceStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceStatus(ctx context.Context, sel ast.SelectionSet, v *model.SourceStatus) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._SourceStatus(ctx, sel, v) +} + func (ec *executionContext) unmarshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, v interface{}) (model.SpanKind, error) { var res model.SpanKind err := res.UnmarshalGQL(v) diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index d5407efa3..cc9928307 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -78,6 +78,7 @@ type ComputePlatform struct { K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` K8sActualSource *K8sActualSource `json:"k8sActualSource,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` + Sources []*Source `json:"sources"` Destinations []*Destination `json:"destinations"` Actions []*PipelineAction `json:"actions"` InstrumentationRules []*InstrumentationRule `json:"instrumentationRules"` @@ -500,8 +501,8 @@ type PodContainerAnalyze struct { type PodWorkload struct { Namespace string `json:"namespace"` - Kind K8sResourceKind `json:"kind"` Name string `json:"name"` + Kind K8sResourceKind `json:"kind"` } type PodWorkloadInput struct { @@ -586,6 +587,11 @@ type SingleSourceMetricsResponse struct { Throughput int `json:"throughput"` } +type Source struct { + Spec *SourceSpec `json:"spec"` + Status *SourceStatus `json:"status"` +} + type SourceAnalyze struct { Name *EntityProperty `json:"name"` Kind *EntityProperty `json:"kind"` @@ -607,6 +613,14 @@ type SourceContainerRuntimeDetails struct { OtherAgent *string `json:"otherAgent,omitempty"` } +type SourceSpec struct { + Workload *PodWorkload `json:"workload"` +} + +type SourceStatus struct { + Conditions []*Condition `json:"conditions,omitempty"` +} + type TestConnectionResponse struct { Succeeded bool `json:"succeeded"` StatusCode int `json:"statusCode"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 3fb7b0f4d..7f1d95c22 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -106,10 +106,23 @@ input K8sDesiredSourceInput { autoInstrument: Boolean } +type SourceSpec { + workload: PodWorkload! +} + +type SourceStatus { + conditions: [Condition!] +} + +type Source { + spec: SourceSpec! + status: SourceStatus! +} + type PodWorkload { namespace: String! - kind: K8sResourceKind! name: String! + kind: K8sResourceKind! } input PodWorkloadInput { @@ -207,6 +220,7 @@ type ComputePlatform { k8sActualNamespaces: [K8sActualNamespace]! k8sActualSource(name: String, namespace: String, kind: String): K8sActualSource k8sActualSources: [K8sActualSource]! + sources: [Source!]! destinations: [Destination!]! actions: [PipelineAction!]! instrumentationRules: [InstrumentationRule!]! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index ffddced75..585a30baf 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -110,6 +110,32 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod return actualSources, nil } +// Sources is the resolver for the sources field. +func (r *computePlatformResolver) Sources(ctx context.Context, obj *model.ComputePlatform) ([]*model.Source, error) { + sources, err := kube.DefaultClient.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + + var result []*model.Source + for _, source := range sources.Items { + result = append(result, &model.Source{ + Spec: &model.SourceSpec{ + Workload: &model.PodWorkload{ + Namespace: source.Spec.Workload.Namespace, + Name: source.Spec.Workload.Name, + Kind: model.K8sResourceKind(source.Spec.Workload.Kind), + }, + }, + Status: &model.SourceStatus{ + Conditions: convertConditions(source.Status.Conditions), + }, + }) + } + + return result, nil +} + // Destinations is the resolver for the destinations field. func (r *computePlatformResolver) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) { odigosns := consts.DefaultOdigosNamespace diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index 674c61246..b914d7e09 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -25,6 +25,24 @@ export const GET_COMPUTE_PLATFORM = gql` } } } + sources { + spec { + workload { + kind + name + namespace + } + } + status { + conditions { + status + type + reason + message + lastTransitionTime + } + } + } destinations { id name diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index 16fea4d76..94fb47a97 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -1,4 +1,4 @@ -import type { K8sActualSource } from './sources'; +import type { K8sActualSource, Source } from './sources'; import type { ActualDestination } from './destinations'; import type { ActionData, ActionDataParsed } from './actions'; import type { InstrumentationRuleSpec, InstrumentationRuleSpecMapped } from './instrumentation-rules'; @@ -14,9 +14,10 @@ interface ComputePlatformData { computePlatformType: string; k8sActualNamespace?: K8sActualNamespace; k8sActualNamespaces: K8sActualNamespace[]; - actions: ActionData[]; k8sActualSources: K8sActualSource[]; + sources: Source[]; destinations: ActualDestination[]; + actions: ActionData[]; instrumentationRules: InstrumentationRuleSpec[]; } diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index f2ce90015..6a8a25334 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -36,3 +36,12 @@ export type PersistSourcesArray = { name: string; selected: boolean; }; + +export interface Source { + spec: { + workload: WorkloadId; + }; + status: { + conditions: Condition[]; + }; +} From 4eeda2c2e5101956f28bcc403390acf8a2f8ebe7 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 14:35:31 +0200 Subject: [PATCH 114/259] feat: add source management for workloads and update GraphQL queries --- cli/cmd/resources/ui.go | 9 ++- frontend/services/namespaces.go | 2 +- frontend/services/utils.go | 79 ++++++++++++++----- .../graphql/queries/compute-platform.ts | 24 +----- frontend/webapp/types/compute-platform.ts | 3 +- frontend/webapp/types/sources.ts | 9 --- 6 files changed, 72 insertions(+), 54 deletions(-) diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index de9a2ca14..6c50853f7 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -235,9 +235,14 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Resources: []string{"instrumentedapplications", "instrumentationinstances", "instrumentationconfigs"}, Verbs: []string{"get", "list"}, }, - { // Needed to watch Odigos entities + { // Needed to instrument / uninstrument applications + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources"}, + Verbs: []string{"get", "list", "create", "delete"}, + }, + { // Needed to watch instrumented applications APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances"}, + Resources: []string{"instrumentedapplications", "instrumentationinstances", "sources"}, Verbs: []string{"watch"}, }, }, diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 534249c90..f06e3355b 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -181,7 +181,7 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo g.Go(func() error { // Only label selected sources, ignore the rest if currWorkload.Selected != nil { - return setWorkloadInstrumentationLabel(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + return ToggleWorkloadSource(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } return nil }) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index 71218b665..57a8a94d6 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -8,11 +8,12 @@ import ( "strings" "time" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" ) const cdnUrl = "https://d15jtxgb40qetw.cloudfront.net" @@ -21,24 +22,6 @@ func GetImageURL(image string) string { return path.Join(cdnUrl, image) } -func setWorkloadInstrumentationLabel(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { - jsonMergePatchData := GetJsonMergePatchForInstrumentationLabel(enabled) - - switch workloadKind { - case WorkloadKindDeployment: - _, err := kube.DefaultClient.AppsV1().Deployments(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindStatefulSet: - _, err := kube.DefaultClient.AppsV1().StatefulSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindDaemonSet: - _, err := kube.DefaultClient.AppsV1().DaemonSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - default: - return errors.New("unsupported workload kind " + string(workloadKind)) - } -} - func ConvertFieldsToString(fields map[string]string) string { if len(fields) == 0 { return "" @@ -86,3 +69,61 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } return latestStatusTime.Time.Format(time.RFC3339) } + +func CreateWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + newSource := &v1alpha1.Source{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "source-", + }, + Spec: v1alpha1.SourceSpec{ + Workload: workload.PodWorkload{ + Namespace: nsName, + Name: workloadName, + Kind: workload.WorkloadKind(workloadKind), + }, + }, + Status: v1alpha1.SourceStatus{ + Conditions: []metav1.Condition{}, + }, + } + + switch workloadKind { + case WorkloadKindDeployment: + case WorkloadKindStatefulSet: + case WorkloadKindDaemonSet: + _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) + return err + default: + return errors.New("unsupported workload kind " + string(workloadKind)) + } + + return nil +} + +func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + switch workloadKind { + case WorkloadKindDeployment: + case WorkloadKindStatefulSet: + case WorkloadKindDaemonSet: + err := kube.DefaultClient.OdigosClient.Sources("").Delete(ctx, workloadName, metav1.DeleteOptions{}) + return err + default: + return errors.New("unsupported workload kind " + string(workloadKind)) + } + + return nil +} + +func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { + if enabled == nil { + return errors.New("enabled must be provided") + } + + if *enabled { + CreateWorkloadSource(ctx, nsName, workloadName, workloadKind) + } else { + DeleteWorkloadSource(ctx, nsName, workloadName, workloadKind) + } + + return nil +} diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index b914d7e09..b75b562c7 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -3,6 +3,9 @@ import { gql } from '@apollo/client'; export const GET_COMPUTE_PLATFORM = gql` query GetComputePlatform { computePlatform { + k8sActualNamespaces { + name + } k8sActualSources { name kind @@ -25,24 +28,6 @@ export const GET_COMPUTE_PLATFORM = gql` } } } - sources { - spec { - workload { - kind - name - namespace - } - } - status { - conditions { - status - type - reason - message - lastTransitionTime - } - } - } destinations { id name @@ -114,9 +99,6 @@ export const GET_COMPUTE_PLATFORM = gql` } } } - k8sActualNamespaces { - name - } } } `; diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index 94fb47a97..e4c65f2d4 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -1,4 +1,4 @@ -import type { K8sActualSource, Source } from './sources'; +import type { K8sActualSource } from './sources'; import type { ActualDestination } from './destinations'; import type { ActionData, ActionDataParsed } from './actions'; import type { InstrumentationRuleSpec, InstrumentationRuleSpecMapped } from './instrumentation-rules'; @@ -15,7 +15,6 @@ interface ComputePlatformData { k8sActualNamespace?: K8sActualNamespace; k8sActualNamespaces: K8sActualNamespace[]; k8sActualSources: K8sActualSource[]; - sources: Source[]; destinations: ActualDestination[]; actions: ActionData[]; instrumentationRules: InstrumentationRuleSpec[]; diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index 6a8a25334..f2ce90015 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -36,12 +36,3 @@ export type PersistSourcesArray = { name: string; selected: boolean; }; - -export interface Source { - spec: { - workload: WorkloadId; - }; - status: { - conditions: Condition[]; - }; -} From 3408c1881118bf115250d63e11bb3e76c08017bf Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 14:45:02 +0200 Subject: [PATCH 115/259] refactor: remove sources field and related types from GraphQL schema --- frontend/gqlgen.yml | 2 - frontend/graph/generated.go | 532 ----------------------------- frontend/graph/model/models_gen.go | 14 - frontend/graph/schema.graphqls | 14 - frontend/graph/schema.resolvers.go | 26 -- 5 files changed, 588 deletions(-) diff --git a/frontend/gqlgen.yml b/frontend/gqlgen.yml index fda4093fe..f5dedfb41 100644 --- a/frontend/gqlgen.yml +++ b/frontend/gqlgen.yml @@ -52,8 +52,6 @@ models: resolver: true k8sActualSources: resolver: true - sources: - resolver: true destinations: resolver: true actions: diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index 5dc7c723f..a0642bc58 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -87,7 +87,6 @@ type ComplexityRoot struct { K8sActualNamespaces func(childComplexity int) int K8sActualSource func(childComplexity int, name *string, namespace *string, kind *string) int K8sActualSources func(childComplexity int) int - Sources func(childComplexity int) int } Condition struct { @@ -453,11 +452,6 @@ type ComplexityRoot struct { TotalDataSent func(childComplexity int) int } - Source struct { - Spec func(childComplexity int) int - Status func(childComplexity int) int - } - SourceAnalyze struct { InstrumentationConfig func(childComplexity int) int InstrumentationDevice func(childComplexity int) int @@ -479,14 +473,6 @@ type ComplexityRoot struct { RuntimeVersion func(childComplexity int) int } - SourceSpec struct { - Workload func(childComplexity int) int - } - - SourceStatus struct { - Conditions func(childComplexity int) int - } - SupportedSignals struct { Logs func(childComplexity int) int Metrics func(childComplexity int) int @@ -507,7 +493,6 @@ type ComputePlatformResolver interface { K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) - Sources(ctx context.Context, obj *model.ComputePlatform) ([]*model.Source, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) InstrumentationRules(ctx context.Context, obj *model.ComputePlatform) ([]*model.InstrumentationRule, error) @@ -764,13 +749,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ComputePlatform.K8sActualSources(childComplexity), true - case "ComputePlatform.sources": - if e.complexity.ComputePlatform.Sources == nil { - break - } - - return e.complexity.ComputePlatform.Sources(childComplexity), true - case "Condition.lastTransitionTime": if e.complexity.Condition.LastTransitionTime == nil { break @@ -2342,20 +2320,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SingleSourceMetricsResponse.TotalDataSent(childComplexity), true - case "Source.spec": - if e.complexity.Source.Spec == nil { - break - } - - return e.complexity.Source.Spec(childComplexity), true - - case "Source.status": - if e.complexity.Source.Status == nil { - break - } - - return e.complexity.Source.Status(childComplexity), true - case "SourceAnalyze.instrumentationConfig": if e.complexity.SourceAnalyze.InstrumentationConfig == nil { break @@ -2461,20 +2425,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SourceContainerRuntimeDetails.RuntimeVersion(childComplexity), true - case "SourceSpec.workload": - if e.complexity.SourceSpec.Workload == nil { - break - } - - return e.complexity.SourceSpec.Workload(childComplexity), true - - case "SourceStatus.conditions": - if e.complexity.SourceStatus.Conditions == nil { - break - } - - return e.complexity.SourceStatus.Conditions(childComplexity), true - case "SupportedSignals.logs": if e.complexity.SupportedSignals.Logs == nil { break @@ -4290,56 +4240,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont return fc, nil } -func (ec *executionContext) _ComputePlatform_sources(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_sources(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().Sources(rctx, obj) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.Source) - fc.Result = res - return ec.marshalNSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_ComputePlatform_sources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ComputePlatform", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "spec": - return ec.fieldContext_Source_spec(ctx, field) - case "status": - return ec.fieldContext_Source_status(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Source", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _ComputePlatform_destinations(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ComputePlatform_destinations(ctx, field) if err != nil { @@ -13553,8 +13453,6 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context return ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) case "k8sActualSources": return ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) - case "sources": - return ec.fieldContext_ComputePlatform_sources(ctx, field) case "destinations": return ec.fieldContext_ComputePlatform_destinations(ctx, field) case "actions": @@ -14846,102 +14744,6 @@ func (ec *executionContext) fieldContext_SingleSourceMetricsResponse_throughput( return fc, nil } -func (ec *executionContext) _Source_spec(ctx context.Context, field graphql.CollectedField, obj *model.Source) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Source_spec(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Spec, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.SourceSpec) - fc.Result = res - return ec.marshalNSourceSpec2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceSpec(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Source_spec(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Source", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "workload": - return ec.fieldContext_SourceSpec_workload(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type SourceSpec", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _Source_status(ctx context.Context, field graphql.CollectedField, obj *model.Source) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Source_status(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Status, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.SourceStatus) - fc.Result = res - return ec.marshalNSourceStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceStatus(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Source_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Source", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "conditions": - return ec.fieldContext_SourceStatus_conditions(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type SourceStatus", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _SourceAnalyze_name(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SourceAnalyze_name(ctx, field) if err != nil { @@ -15672,111 +15474,6 @@ func (ec *executionContext) fieldContext_SourceContainerRuntimeDetails_otherAgen return fc, nil } -func (ec *executionContext) _SourceSpec_workload(ctx context.Context, field graphql.CollectedField, obj *model.SourceSpec) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceSpec_workload(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Workload, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.PodWorkload) - fc.Result = res - return ec.marshalNPodWorkload2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPodWorkload(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_SourceSpec_workload(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "SourceSpec", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "namespace": - return ec.fieldContext_PodWorkload_namespace(ctx, field) - case "name": - return ec.fieldContext_PodWorkload_name(ctx, field) - case "kind": - return ec.fieldContext_PodWorkload_kind(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type PodWorkload", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _SourceStatus_conditions(ctx context.Context, field graphql.CollectedField, obj *model.SourceStatus) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceStatus_conditions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Conditions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.Condition) - fc.Result = res - return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_SourceStatus_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "SourceStatus", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "status": - return ec.fieldContext_Condition_status(ctx, field) - case "type": - return ec.fieldContext_Condition_type(ctx, field) - case "reason": - return ec.fieldContext_Condition_reason(ctx, field) - case "message": - return ec.fieldContext_Condition_message(ctx, field) - case "lastTransitionTime": - return ec.fieldContext_Condition_lastTransitionTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _SupportedSignals_traces(ctx context.Context, field graphql.CollectedField, obj *model.SupportedSignals) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SupportedSignals_traces(ctx, field) if err != nil { @@ -19008,42 +18705,6 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select continue } - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "sources": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._ComputePlatform_sources(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "destinations": field := field @@ -22031,50 +21692,6 @@ func (ec *executionContext) _SingleSourceMetricsResponse(ctx context.Context, se return out } -var sourceImplementors = []string{"Source"} - -func (ec *executionContext) _Source(ctx context.Context, sel ast.SelectionSet, obj *model.Source) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, sourceImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Source") - case "spec": - out.Values[i] = ec._Source_spec(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "status": - out.Values[i] = ec._Source_status(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var sourceAnalyzeImplementors = []string{"SourceAnalyze"} func (ec *executionContext) _SourceAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.SourceAnalyze) graphql.Marshaler { @@ -22212,81 +21829,6 @@ func (ec *executionContext) _SourceContainerRuntimeDetails(ctx context.Context, return out } -var sourceSpecImplementors = []string{"SourceSpec"} - -func (ec *executionContext) _SourceSpec(ctx context.Context, sel ast.SelectionSet, obj *model.SourceSpec) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, sourceSpecImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("SourceSpec") - case "workload": - out.Values[i] = ec._SourceSpec_workload(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var sourceStatusImplementors = []string{"SourceStatus"} - -func (ec *executionContext) _SourceStatus(ctx context.Context, sel ast.SelectionSet, obj *model.SourceStatus) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, sourceStatusImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("SourceStatus") - case "conditions": - out.Values[i] = ec._SourceStatus_conditions(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var supportedSignalsImplementors = []string{"SupportedSignals"} func (ec *executionContext) _SupportedSignals(ctx context.Context, sel ast.SelectionSet, obj *model.SupportedSignals) graphql.Marshaler { @@ -24191,60 +23733,6 @@ func (ec *executionContext) marshalNSingleSourceMetricsResponse2ᚖgithubᚗcom return ec._SingleSourceMetricsResponse(ctx, sel, v) } -func (ec *executionContext) marshalNSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Source) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSource(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSource(ctx context.Context, sel ast.SelectionSet, v *model.Source) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._Source(ctx, sel, v) -} - func (ec *executionContext) marshalNSourceAnalyze2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceAnalyze(ctx context.Context, sel ast.SelectionSet, v model.SourceAnalyze) graphql.Marshaler { return ec._SourceAnalyze(ctx, sel, &v) } @@ -24269,26 +23757,6 @@ func (ec *executionContext) marshalNSourceContainerRuntimeDetails2ᚖgithubᚗco return ec._SourceContainerRuntimeDetails(ctx, sel, v) } -func (ec *executionContext) marshalNSourceSpec2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceSpec(ctx context.Context, sel ast.SelectionSet, v *model.SourceSpec) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._SourceSpec(ctx, sel, v) -} - -func (ec *executionContext) marshalNSourceStatus2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceStatus(ctx context.Context, sel ast.SelectionSet, v *model.SourceStatus) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._SourceStatus(ctx, sel, v) -} - func (ec *executionContext) unmarshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, v interface{}) (model.SpanKind, error) { var res model.SpanKind err := res.UnmarshalGQL(v) diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index cc9928307..880c47690 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -78,7 +78,6 @@ type ComputePlatform struct { K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` K8sActualSource *K8sActualSource `json:"k8sActualSource,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` - Sources []*Source `json:"sources"` Destinations []*Destination `json:"destinations"` Actions []*PipelineAction `json:"actions"` InstrumentationRules []*InstrumentationRule `json:"instrumentationRules"` @@ -587,11 +586,6 @@ type SingleSourceMetricsResponse struct { Throughput int `json:"throughput"` } -type Source struct { - Spec *SourceSpec `json:"spec"` - Status *SourceStatus `json:"status"` -} - type SourceAnalyze struct { Name *EntityProperty `json:"name"` Kind *EntityProperty `json:"kind"` @@ -613,14 +607,6 @@ type SourceContainerRuntimeDetails struct { OtherAgent *string `json:"otherAgent,omitempty"` } -type SourceSpec struct { - Workload *PodWorkload `json:"workload"` -} - -type SourceStatus struct { - Conditions []*Condition `json:"conditions,omitempty"` -} - type TestConnectionResponse struct { Succeeded bool `json:"succeeded"` StatusCode int `json:"statusCode"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 7f1d95c22..808c9ca2c 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -106,19 +106,6 @@ input K8sDesiredSourceInput { autoInstrument: Boolean } -type SourceSpec { - workload: PodWorkload! -} - -type SourceStatus { - conditions: [Condition!] -} - -type Source { - spec: SourceSpec! - status: SourceStatus! -} - type PodWorkload { namespace: String! name: String! @@ -220,7 +207,6 @@ type ComputePlatform { k8sActualNamespaces: [K8sActualNamespace]! k8sActualSource(name: String, namespace: String, kind: String): K8sActualSource k8sActualSources: [K8sActualSource]! - sources: [Source!]! destinations: [Destination!]! actions: [PipelineAction!]! instrumentationRules: [InstrumentationRule!]! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 585a30baf..ffddced75 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -110,32 +110,6 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod return actualSources, nil } -// Sources is the resolver for the sources field. -func (r *computePlatformResolver) Sources(ctx context.Context, obj *model.ComputePlatform) ([]*model.Source, error) { - sources, err := kube.DefaultClient.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, err - } - - var result []*model.Source - for _, source := range sources.Items { - result = append(result, &model.Source{ - Spec: &model.SourceSpec{ - Workload: &model.PodWorkload{ - Namespace: source.Spec.Workload.Namespace, - Name: source.Spec.Workload.Name, - Kind: model.K8sResourceKind(source.Spec.Workload.Kind), - }, - }, - Status: &model.SourceStatus{ - Conditions: convertConditions(source.Status.Conditions), - }, - }) - } - - return result, nil -} - // Destinations is the resolver for the destinations field. func (r *computePlatformResolver) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) { odigosns := consts.DefaultOdigosNamespace From 9ac23748ffa814ef01a611e3090761c6ab21eff3 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 14:45:34 +0200 Subject: [PATCH 116/259] refactor: simplify workload source creation and deletion logic --- frontend/services/utils.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index 57a8a94d6..d8ccf9fbc 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -87,31 +87,23 @@ func CreateWorkloadSource(ctx context.Context, nsName string, workloadName strin }, } - switch workloadKind { - case WorkloadKindDeployment: - case WorkloadKindStatefulSet: - case WorkloadKindDaemonSet: - _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) - return err - default: + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) } - return nil + _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) + return err + } func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - switch workloadKind { - case WorkloadKindDeployment: - case WorkloadKindStatefulSet: - case WorkloadKindDaemonSet: - err := kube.DefaultClient.OdigosClient.Sources("").Delete(ctx, workloadName, metav1.DeleteOptions{}) - return err - default: + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) } - return nil + err := kube.DefaultClient.OdigosClient.Sources("").Delete(ctx, workloadName, metav1.DeleteOptions{}) + return err + } func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { From cb4695b70592ff863e58a9ceddd586eec64aa807 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 14:55:04 +0200 Subject: [PATCH 117/259] fix: validate workload kind in CreateWorkloadSource function --- frontend/services/utils.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index d8ccf9fbc..c2a7c85b4 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -71,6 +71,10 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } func CreateWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return errors.New("unsupported workload kind " + string(workloadKind)) + } + newSource := &v1alpha1.Source{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "source-", @@ -87,10 +91,6 @@ func CreateWorkloadSource(ctx context.Context, nsName string, workloadName strin }, } - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) - } - _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) return err From 12fbcf790a0694bf14c71d014a2724b4df153686 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 13:37:02 -0500 Subject: [PATCH 118/259] Add finalizer for uninstrumentation --- .../controllers/startlangdetection/source_controller.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 6e4269981..0d560a56f 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -16,6 +16,8 @@ import ( "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) +var sourceFinalizer = "odigos.io/source-finalizer" + type SourceReconciler struct { client.Client Scheme *runtime.Scheme From 056ebae27bcb0596fff153011f9d7419d5b69d66 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 20 Dec 2024 14:11:24 -0500 Subject: [PATCH 119/259] Add workload labels to Source --- .../controllers/startlangdetection/source_controller.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 0d560a56f..c8eb80ac6 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -16,7 +16,13 @@ import ( "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -var sourceFinalizer = "odigos.io/source-finalizer" +var ( + sourceFinalizer = "odigos.io/source-finalizer" + + workloadNameLabel = "odigos.io/workload-name" + workloadNamespaceLabel = "odigos.io/workload-namespace" + workloadKindLabel = "odigos.io/workload-kind" +) type SourceReconciler struct { client.Client From 3f1b7efa237ecb87d9cf2f5c5615539893270917 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 13:09:24 -0500 Subject: [PATCH 120/259] Add SourceReconciler to deleteinstrumentedapplication controller --- .../deleteinstrumentedapplication/manager.go | 22 +++++++++---------- .../source_controller.go | 2 +- .../startlangdetection/source_controller.go | 2 ++ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/instrumentor/controllers/deleteinstrumentedapplication/manager.go b/instrumentor/controllers/deleteinstrumentedapplication/manager.go index da00e9827..e401c2354 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/manager.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/manager.go @@ -77,17 +77,17 @@ func SetupWithManager(mgr ctrl.Manager) error { } err = builder. - ControllerManagedBy(mgr). - Named("deleteinstrumentedapplication-source"). - WithEventFilter(&k8sutils.OnlyUpdatesPredicate{}). - For(&odigosv1.Source{}). - Complete(&SourceReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - }) -if err != nil { - return err -} + ControllerManagedBy(mgr). + Named("deleteinstrumentedapplication-source"). + WithEventFilter(&k8sutils.OnlyUpdatesPredicate{}). + For(&odigosv1.Source{}). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) + if err != nil { + return err + } return nil diff --git a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go index a60656046..acd8aba31 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go @@ -20,8 +20,8 @@ import ( "context" "github.com/odigos-io/odigos/api/odigos/v1alpha1" - "github.com/odigos-io/odigos/k8sutils/pkg/workload" "github.com/odigos-io/odigos/k8sutils/pkg/consts" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index c8eb80ac6..28c83bc37 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -18,6 +18,8 @@ import ( var ( sourceFinalizer = "odigos.io/source-finalizer" + // TODO: Needed until InstrumentedApplication is removed + instrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" workloadNameLabel = "odigos.io/workload-name" workloadNamespaceLabel = "odigos.io/workload-namespace" From 656914d9d9c18fd1361bbb61c12a1566a43f7e7c Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 23 Dec 2024 13:43:11 -0500 Subject: [PATCH 121/259] Enable build and e2e workflows for feature branch --- .github/workflows/build.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 55dce9449..1e6cd1f17 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -44,7 +44,7 @@ jobs: uses: docker/setup-buildx-action@v3 - uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Build Instrumentor Image uses: docker/build-push-action@v6 with: @@ -75,7 +75,7 @@ jobs: run: | make test build-and-test-odiglet: - runs-on: ubuntu-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx @@ -119,7 +119,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Set up Goreleaser uses: goreleaser/goreleaser-action@v5 with: @@ -132,7 +132,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Test k8sutils module working-directory: ./k8sutils run: | @@ -144,7 +144,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Test common module working-directory: ./common run: | @@ -156,7 +156,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Test procdiscovery module working-directory: ./procdiscovery run: | From 2645702f2100cb57613b30e88137924681f74202 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 15:44:50 +0200 Subject: [PATCH 122/259] fix: use default namespace for creating and deleting workload sources --- frontend/services/utils.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index c2a7c85b4..abfcdf358 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -10,6 +10,7 @@ import ( "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -91,9 +92,8 @@ func CreateWorkloadSource(ctx context.Context, nsName string, workloadName strin }, } - _, err := kube.DefaultClient.OdigosClient.Sources("").Create(ctx, newSource, metav1.CreateOptions{}) + _, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) return err - } func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { @@ -101,9 +101,8 @@ func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName strin return errors.New("unsupported workload kind " + string(workloadKind)) } - err := kube.DefaultClient.OdigosClient.Sources("").Delete(ctx, workloadName, metav1.DeleteOptions{}) + err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) return err - } func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { @@ -112,10 +111,8 @@ func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName strin } if *enabled { - CreateWorkloadSource(ctx, nsName, workloadName, workloadKind) + return CreateWorkloadSource(ctx, nsName, workloadName, workloadKind) } else { - DeleteWorkloadSource(ctx, nsName, workloadName, workloadKind) + return DeleteWorkloadSource(ctx, nsName, workloadName, workloadKind) } - - return nil } From 1ff33b0e9ad045a96d85ba43a43690240850eb3a Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 16:34:10 +0200 Subject: [PATCH 123/259] refactor: replace workload source functions with source CRD implementations --- frontend/services/namespaces.go | 2 +- frontend/services/sources.go | 55 +++++++++++++++++++++++---- frontend/services/utils.go | 66 --------------------------------- 3 files changed, 49 insertions(+), 74 deletions(-) diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index f06e3355b..699ed8cde 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -181,7 +181,7 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo g.Go(func() error { // Only label selected sources, ignore the rest if currWorkload.Selected != nil { - return ToggleWorkloadSource(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } return nil }) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 9549fba7a..33e993361 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -2,24 +2,22 @@ package services import ( "context" + "errors" "fmt" "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" + "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" - + "github.com/odigos-io/odigos/k8sutils/pkg/client" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" appsv1 "k8s.io/api/apps/v1" - - "github.com/odigos-io/odigos/frontend/graph/model" - - "github.com/odigos-io/odigos/k8sutils/pkg/client" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "golang.org/x/sync/errgroup" - corev1 "k8s.io/api/core/v1" ) type WorkloadKind string @@ -319,3 +317,46 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s } return annotations } + +func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return errors.New("unsupported workload kind " + string(workloadKind)) + } + + newSource := &v1alpha1.Source{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "source-", + }, + Spec: v1alpha1.SourceSpec{ + Workload: workload.PodWorkload{ + Namespace: nsName, + Name: workloadName, + Kind: workload.WorkloadKind(workloadKind), + }, + }, + } + + _, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) + return err +} + +func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return errors.New("unsupported workload kind " + string(workloadKind)) + } + + err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) + return err +} + +func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { + if enabled == nil { + return errors.New("enabled must be provided") + } + + if *enabled { + return CreateSourceCRD(ctx, nsName, workloadName, workloadKind) + } else { + return DeleteSourceCRD(ctx, nsName, workloadName, workloadKind) + } +} diff --git a/frontend/services/utils.go b/frontend/services/utils.go index abfcdf358..6d8f1603e 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -1,19 +1,12 @@ package services import ( - "context" - "errors" "fmt" "path" - "strings" "time" - "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" - "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/graph/model" - "github.com/odigos-io/odigos/frontend/kube" - "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -23,19 +16,6 @@ func GetImageURL(image string) string { return path.Join(cdnUrl, image) } -func ConvertFieldsToString(fields map[string]string) string { - if len(fields) == 0 { - return "" - } - - var parts []string - for key, value := range fields { - parts = append(parts, fmt.Sprintf("%s: %s", key, value)) - } - - return strings.Join(parts, ", ") -} - func ConvertSignals(signals []model.SignalType) ([]common.ObservabilitySignal, error) { var result []common.ObservabilitySignal for _, s := range signals { @@ -70,49 +50,3 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } return latestStatusTime.Time.Format(time.RFC3339) } - -func CreateWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) - } - - newSource := &v1alpha1.Source{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "source-", - }, - Spec: v1alpha1.SourceSpec{ - Workload: workload.PodWorkload{ - Namespace: nsName, - Name: workloadName, - Kind: workload.WorkloadKind(workloadKind), - }, - }, - Status: v1alpha1.SourceStatus{ - Conditions: []metav1.Condition{}, - }, - } - - _, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) - return err -} - -func DeleteWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) - } - - err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) - return err -} - -func ToggleWorkloadSource(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { - if enabled == nil { - return errors.New("enabled must be provided") - } - - if *enabled { - return CreateWorkloadSource(ctx, nsName, workloadName, workloadKind) - } else { - return DeleteWorkloadSource(ctx, nsName, workloadName, workloadKind) - } -} From aab3fe6e75600d7522733e266f1e23061663cc2f Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 18:36:46 +0200 Subject: [PATCH 124/259] feat: add GetSourceCRD function to retrieve source CRD by workload details --- frontend/services/sources.go | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 33e993361..ca5e50859 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -16,6 +16,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" "golang.org/x/sync/errgroup" ) @@ -318,6 +319,31 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } +func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return nil, errors.New("unsupported workload kind " + string(workloadKind)) + } + + selector := labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-name": workloadName, + "odigos.io/workload-namespace": nsName, + "odigos.io/workload-kind": string(workloadKind), + }) + + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return nil, err + } + + crdName := sourceList.Items[0].Name + source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + return source, nil +} + func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) @@ -345,7 +371,15 @@ func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return errors.New("unsupported workload kind " + string(workloadKind)) } - err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, workloadName, metav1.DeleteOptions{}) + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) + if err != nil { + return err + } + if source == nil { + return errors.New("source not found" + workloadName) + } + + err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err } From 942da6b5fadfbb3fba1f616b859c07eb06200fa5 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 25 Dec 2024 18:42:53 +0200 Subject: [PATCH 125/259] fix: handle case when source is not found in GetSourceCRD and remove redundant check in DeleteSourceCRD --- frontend/services/sources.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index ca5e50859..5eef17fdd 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -334,6 +334,9 @@ func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workl if err != nil { return nil, err } + if len(sourceList.Items) == 0 { + return nil, errors.New("source not found" + workloadName) + } crdName := sourceList.Items[0].Name source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) @@ -375,9 +378,6 @@ func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo if err != nil { return err } - if source == nil { - return errors.New("source not found" + workloadName) - } err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err From 30e8fa53b3bc4a4a6ee5905c20b0af25d6d6417a Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Thu, 26 Dec 2024 09:55:51 +0200 Subject: [PATCH 126/259] feat: update GetSourceCRD to GetSourceCRDs for retrieving multiple sources by workload details --- frontend/services/sources.go | 55 +++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 5eef17fdd..7e49196a9 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -319,32 +319,53 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } -func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return nil, errors.New("unsupported workload kind " + string(workloadKind)) +func GetSourceCRDs(ctx context.Context, args ...interface{}) ([]*v1alpha1.Source, error) { + var nsName, workloadName string + var workloadKind WorkloadKind + if len(args) > 0 { + nsName, _ = args[0].(string) + } + if len(args) > 1 { + workloadName, _ = args[1].(string) + } + if len(args) > 2 { + workloadKind, _ = args[2].(WorkloadKind) + if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { + return nil, errors.New("unsupported workload kind " + string(workloadKind)) + } } - selector := labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-name": workloadName, - "odigos.io/workload-namespace": nsName, - "odigos.io/workload-kind": string(workloadKind), - }) + labelsSet := labels.Set{} + if nsName != "" { + labelsSet["odigos.io/workload-namespace"] = nsName + } + if workloadName != "" { + labelsSet["odigos.io/workload-name"] = workloadName + } + if string(workloadKind) != "" { + labelsSet["odigos.io/workload-kind"] = string(workloadKind) + } - sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: selector.String()}) + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labelsSet).String()}) if err != nil { return nil, err } - if len(sourceList.Items) == 0 { + if workloadName != "" && len(sourceList.Items) == 0 { return nil, errors.New("source not found" + workloadName) } - crdName := sourceList.Items[0].Name - source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) - if err != nil { - return nil, err + var sources []*v1alpha1.Source + + for _, crd := range sourceList.Items { + crdName := crd.Name + source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + sources = append(sources, source) } - return source, nil + return sources, nil } func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { @@ -374,12 +395,12 @@ func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return errors.New("unsupported workload kind " + string(workloadKind)) } - source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) + sources, err := GetSourceCRDs(ctx, nsName, workloadName, workloadKind) if err != nil { return err } - err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) + err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, sources[0].Name, metav1.DeleteOptions{}) return err } From de9dfe1d2c7e03ae11b0355a5f70ecbe7d891dff Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Thu, 26 Dec 2024 10:34:04 +0200 Subject: [PATCH 127/259] feat: implement SetWorkloadInstrumentationLabel function and update SyncWorkloadsInNamespace to handle source labeling --- frontend/graph/schema.resolvers.go | 15 ++++++++++++--- frontend/services/namespaces.go | 10 +++++++++- frontend/services/sources.go | 20 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index ffddced75..aab9c70dd 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -20,6 +20,7 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" "github.com/odigos-io/odigos/k8sutils/pkg/workload" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) @@ -81,14 +82,21 @@ func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *mode // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { + // sourceList, err := services.GetSourceCRDs(ctx) + // if err != nil { + // return nil, err + // } + + // Initialize an empty list of K8sActualSource + var actualSources []*model.K8sActualSource + + // for _, source := range sourceList { + // TODO: remove "InstrumentedApplications" once we're ready to move over to "InstrumentationConfigs" combined with "Source CRDs" instrumentedApplications, err := kube.DefaultClient.OdigosClient.InstrumentedApplications("").List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } - // Initialize an empty list of K8sActualSource - var actualSources []*model.K8sActualSource - // Convert each instrumented application to the K8sActualSource type for _, app := range instrumentedApplications.Items { actualSource := instrumentedApplicationToActualSource(app) @@ -106,6 +114,7 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod actualSource.ReportedName = &reportedName actualSources = append(actualSources, actualSource) } + // } return actualSources, nil } diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 699ed8cde..602120596 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -181,7 +181,15 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo g.Go(func() error { // Only label selected sources, ignore the rest if currWorkload.Selected != nil { - return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + err := ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + if err != nil { + return err + } + // TODO: remove this after a fix was made in the backend to correctly handle the InstrumentedApplication on-create Source CRD + err = SetWorkloadInstrumentationLabel(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + if err != nil { + return err + } } return nil }) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 7e49196a9..c44a6ceda 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -17,6 +17,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" labels "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" "golang.org/x/sync/errgroup" ) @@ -415,3 +416,22 @@ func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, wo return DeleteSourceCRD(ctx, nsName, workloadName, workloadKind) } } + +// TODO: remove this after a fix was made in the backend to correctly handle the InstrumentedApplication on-create Source CRD +func SetWorkloadInstrumentationLabel(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { + jsonMergePatchData := GetJsonMergePatchForInstrumentationLabel(enabled) + + switch workloadKind { + case WorkloadKindDeployment: + _, err := kube.DefaultClient.AppsV1().Deployments(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) + return err + case WorkloadKindStatefulSet: + _, err := kube.DefaultClient.AppsV1().StatefulSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) + return err + case WorkloadKindDaemonSet: + _, err := kube.DefaultClient.AppsV1().DaemonSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) + return err + default: + return errors.New("unsupported workload kind " + string(workloadKind)) + } +} From 30c2ddcda8d7746f43a10708f168d3035fd5ceff Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Thu, 26 Dec 2024 17:37:32 +0200 Subject: [PATCH 128/259] refactor: rename GetSourceCRDs to GetAllSourceCRDs and update related functions for clarity --- frontend/graph/schema.resolvers.go | 2 +- frontend/services/sources.go | 68 ++++++++++++++---------------- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index aab9c70dd..feb57ef33 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -82,7 +82,7 @@ func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *mode // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { - // sourceList, err := services.GetSourceCRDs(ctx) + // sourceList, err := services.GetAllSourceCRDs(ctx) // if err != nil { // return nil, err // } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index c44a6ceda..a690a3183 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -320,40 +320,11 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } -func GetSourceCRDs(ctx context.Context, args ...interface{}) ([]*v1alpha1.Source, error) { - var nsName, workloadName string - var workloadKind WorkloadKind - if len(args) > 0 { - nsName, _ = args[0].(string) - } - if len(args) > 1 { - workloadName, _ = args[1].(string) - } - if len(args) > 2 { - workloadKind, _ = args[2].(WorkloadKind) - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return nil, errors.New("unsupported workload kind " + string(workloadKind)) - } - } - - labelsSet := labels.Set{} - if nsName != "" { - labelsSet["odigos.io/workload-namespace"] = nsName - } - if workloadName != "" { - labelsSet["odigos.io/workload-name"] = workloadName - } - if string(workloadKind) != "" { - labelsSet["odigos.io/workload-kind"] = string(workloadKind) - } - - sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labelsSet).String()}) +func GetAllSourceCRDs(ctx context.Context) ([]*v1alpha1.Source, error) { + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } - if workloadName != "" && len(sourceList.Items) == 0 { - return nil, errors.New("source not found" + workloadName) - } var sources []*v1alpha1.Source @@ -369,7 +340,30 @@ func GetSourceCRDs(ctx context.Context, args ...interface{}) ([]*v1alpha1.Source return sources, nil } -func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { +func getSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { + sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ + "odigos.io/workload-namespace": nsName, + "odigos.io/workload-name": workloadName, + "odigos.io/workload-kind": string(workloadKind), + }).String()}) + + if err != nil { + return nil, err + } + if len(sourceList.Items) == 0 { + return nil, errors.New("source not found" + workloadName) + } + if len(sourceList.Items) > 1 { + return nil, errors.New("too many sources" + workloadName) + } + + crdName := sourceList.Items[0].Name + source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + + return source, err +} + +func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) } @@ -391,17 +385,17 @@ func CreateSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } -func DeleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { +func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { return errors.New("unsupported workload kind " + string(workloadKind)) } - sources, err := GetSourceCRDs(ctx, nsName, workloadName, workloadKind) + source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if err != nil { return err } - err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, sources[0].Name, metav1.DeleteOptions{}) + err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err } @@ -411,9 +405,9 @@ func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, wo } if *enabled { - return CreateSourceCRD(ctx, nsName, workloadName, workloadKind) + return createSourceCRD(ctx, nsName, workloadName, workloadKind) } else { - return DeleteSourceCRD(ctx, nsName, workloadName, workloadKind) + return deleteSourceCRD(ctx, nsName, workloadName, workloadKind) } } From 40729dd7d1123b102f4a06ffd8a10d6cd3376398 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 27 Dec 2024 09:42:32 -0500 Subject: [PATCH 129/259] Feedback --- .../deleteinstrumentedapplication/source_controller.go | 1 + .../startlangdetection/source_controller.go | 10 ---------- k8sutils/pkg/predicate/creation.go | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go index acd8aba31..db3225884 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go @@ -22,6 +22,7 @@ import ( "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 28c83bc37..6e4269981 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -16,16 +16,6 @@ import ( "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) -var ( - sourceFinalizer = "odigos.io/source-finalizer" - // TODO: Needed until InstrumentedApplication is removed - instrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" - - workloadNameLabel = "odigos.io/workload-name" - workloadNamespaceLabel = "odigos.io/workload-namespace" - workloadKindLabel = "odigos.io/workload-kind" -) - type SourceReconciler struct { client.Client Scheme *runtime.Scheme diff --git a/k8sutils/pkg/predicate/creation.go b/k8sutils/pkg/predicate/creation.go index 921a62795..481418370 100644 --- a/k8sutils/pkg/predicate/creation.go +++ b/k8sutils/pkg/predicate/creation.go @@ -24,4 +24,4 @@ func (i CreationPredicate) Generic(e event.GenericEvent) bool { return false } -var _ predicate.Predicate = &DeletionPredicate{} \ No newline at end of file +var _ predicate.Predicate = &DeletionPredicate{} From 013a08e770b0cd534892bcc95c35e8da1edf9be4 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 11:49:23 +0200 Subject: [PATCH 130/259] Add check for existing Source CRD before creation --- frontend/services/sources.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index a690a3183..a965cf485 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -368,6 +368,11 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo return errors.New("unsupported workload kind " + string(workloadKind)) } + source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) + if source != nil && err == nil { + return errors.New("source already exists") + } + newSource := &v1alpha1.Source{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "source-", @@ -381,7 +386,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo }, } - _, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) + _, err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) return err } From beee82df5052d1e024feded5d0b7e980a1a37ade Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 11:51:44 +0200 Subject: [PATCH 131/259] Enhance error message for existing Source CRD to include workload name --- frontend/services/sources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index a965cf485..30c6a2616 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -370,7 +370,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return errors.New("source already exists") + return errors.New("source already exists" + workloadName) } newSource := &v1alpha1.Source{ From 40afd7fbdc672a431adf810b9d0d0210b40a97d8 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 16:09:25 +0200 Subject: [PATCH 132/259] Refactor K8sActualSources resolver and clean up unused code; improve error messages for source retrieval --- frontend/graph/schema.resolvers.go | 8 ----- frontend/services/namespaces.go | 11 ++---- frontend/services/sources.go | 54 ++++-------------------------- 3 files changed, 9 insertions(+), 64 deletions(-) diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index feb57ef33..72ecf8166 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -82,15 +82,9 @@ func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *mode // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { - // sourceList, err := services.GetAllSourceCRDs(ctx) - // if err != nil { - // return nil, err - // } - // Initialize an empty list of K8sActualSource var actualSources []*model.K8sActualSource - // for _, source := range sourceList { // TODO: remove "InstrumentedApplications" once we're ready to move over to "InstrumentationConfigs" combined with "Source CRDs" instrumentedApplications, err := kube.DefaultClient.OdigosClient.InstrumentedApplications("").List(ctx, metav1.ListOptions{}) if err != nil { @@ -103,7 +97,6 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod services.AddHealthyInstrumentationInstancesCondition(ctx, &app, actualSource) owner, _ := services.GetWorkload(ctx, actualSource.Namespace, string(actualSource.Kind), actualSource.Name) if owner == nil { - continue } ownerAnnotations := owner.GetAnnotations() @@ -114,7 +107,6 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod actualSource.ReportedName = &reportedName actualSources = append(actualSources, actualSource) } - // } return actualSources, nil } diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 602120596..a4fb57780 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -181,18 +181,11 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo g.Go(func() error { // Only label selected sources, ignore the rest if currWorkload.Selected != nil { - err := ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) - if err != nil { - return err - } - // TODO: remove this after a fix was made in the backend to correctly handle the InstrumentedApplication on-create Source CRD - err = SetWorkloadInstrumentationLabel(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) - if err != nil { - return err - } + return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } return nil }) } + return g.Wait() } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 30c6a2616..7c1ca5647 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -17,7 +17,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" labels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" "golang.org/x/sync/errgroup" ) @@ -320,28 +319,8 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } -func GetAllSourceCRDs(ctx context.Context) ([]*v1alpha1.Source, error) { - sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, err - } - - var sources []*v1alpha1.Source - - for _, crd := range sourceList.Items { - crdName := crd.Name - source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) - if err != nil { - return nil, err - } - sources = append(sources, source) - } - - return sources, nil -} - func getSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { - sourceList, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ + sourceList, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ "odigos.io/workload-namespace": nsName, "odigos.io/workload-name": workloadName, "odigos.io/workload-kind": string(workloadKind), @@ -351,14 +330,14 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, err } if len(sourceList.Items) == 0 { - return nil, errors.New("source not found" + workloadName) + return nil, errors.New("source not found " + "\"" + workloadName + "\"") } if len(sourceList.Items) > 1 { - return nil, errors.New("too many sources" + workloadName) + return nil, errors.New("too many sources " + "\"" + workloadName + "\"") } crdName := sourceList.Items[0].Name - source, err := kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Get(ctx, crdName, metav1.GetOptions{}) + source, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) return source, err } @@ -370,7 +349,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return errors.New("source already exists" + workloadName) + return errors.New("source already exists " + "\"" + workloadName + "\"") } newSource := &v1alpha1.Source{ @@ -386,7 +365,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo }, } - _, err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Create(ctx, newSource, metav1.CreateOptions{}) + _, err = kube.DefaultClient.OdigosClient.Sources(nsName).Create(ctx, newSource, metav1.CreateOptions{}) return err } @@ -400,7 +379,7 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } - err = kube.DefaultClient.OdigosClient.Sources(consts.DefaultOdigosNamespace).Delete(ctx, source.Name, metav1.DeleteOptions{}) + err = kube.DefaultClient.OdigosClient.Sources(nsName).Delete(ctx, source.Name, metav1.DeleteOptions{}) return err } @@ -415,22 +394,3 @@ func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, wo return deleteSourceCRD(ctx, nsName, workloadName, workloadKind) } } - -// TODO: remove this after a fix was made in the backend to correctly handle the InstrumentedApplication on-create Source CRD -func SetWorkloadInstrumentationLabel(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { - jsonMergePatchData := GetJsonMergePatchForInstrumentationLabel(enabled) - - switch workloadKind { - case WorkloadKindDeployment: - _, err := kube.DefaultClient.AppsV1().Deployments(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindStatefulSet: - _, err := kube.DefaultClient.AppsV1().StatefulSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindDaemonSet: - _, err := kube.DefaultClient.AppsV1().DaemonSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - default: - return errors.New("unsupported workload kind " + string(workloadKind)) - } -} From 030534f3598eb4ba9fb9b780e30bdab6f6c3aca5 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 27 Dec 2024 18:03:10 +0200 Subject: [PATCH 133/259] Create common interface for instrumenteationConfig and instrumentedApplication for migration --- .../odigos.io_instrumentationconfigs.yaml | 56 +++++++++++++++++++ .../v1alpha1/instrumentationconfigstatus.go | 18 ++++++ .../v1alpha1/instrumentationconfig_types.go | 37 ++++++++++++ .../v1alpha1/instrumentedapplication_types.go | 27 ++++----- api/odigos/v1alpha1/zz_generated.deepcopy.go | 7 +++ .../odigos.io_instrumentationconfigs.yaml | 56 +++++++++++++++++++ .../instrumentationdevice/common.go | 46 +++++++-------- .../instrumentation/instrumentation.go | 20 +++---- 8 files changed, 217 insertions(+), 50 deletions(-) diff --git a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml index 8fb450866..5a49486ff 100644 --- a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml +++ b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml @@ -423,6 +423,62 @@ spec: type: object status: properties: + conditions: + 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 runtimeDetailsByContainer: description: Capture Runtime Details for the workloads that this CR applies to. diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go index efab8f15b..f6796dea5 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go @@ -17,10 +17,15 @@ limitations under the License. package v1alpha1 +import ( + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + // InstrumentationConfigStatusApplyConfiguration represents a declarative configuration of the InstrumentationConfigStatus type for use // with apply. type InstrumentationConfigStatusApplyConfiguration struct { RuntimeDetailsByContainer []RuntimeDetailsByContainerApplyConfiguration `json:"runtimeDetailsByContainer,omitempty"` + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` } // InstrumentationConfigStatusApplyConfiguration constructs a declarative configuration of the InstrumentationConfigStatus type for use with @@ -41,3 +46,16 @@ func (b *InstrumentationConfigStatusApplyConfiguration) WithRuntimeDetailsByCont } return b } + +// 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 *InstrumentationConfigStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationConfigStatusApplyConfiguration { + 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/odigos/v1alpha1/instrumentationconfig_types.go b/api/odigos/v1alpha1/instrumentationconfig_types.go index 0c157f3a3..6d4b93882 100644 --- a/api/odigos/v1alpha1/instrumentationconfig_types.go +++ b/api/odigos/v1alpha1/instrumentationconfig_types.go @@ -5,8 +5,16 @@ import ( "github.com/odigos-io/odigos/common" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) +// +kubebuilder:object:generate=false +type WorkloadDetails interface { + client.Object + RuntimeDetailsByContainer() []RuntimeDetailsByContainer + Conditions() *[]metav1.Condition +} + // +genclient // +kubebuilder:object:root=true // +kubebuilder:subresource:status @@ -20,9 +28,38 @@ type InstrumentationConfig struct { Status InstrumentationConfigStatus `json:"status,omitempty"` } +var _ WorkloadDetails = &InstrumentationConfig{} + +func (ic *InstrumentationConfig) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { + return ic.Status.RuntimeDetailsByContainer +} + +func (ic *InstrumentationConfig) Conditions() *[]metav1.Condition { + return &ic.Status.Conditions +} + +// +kubebuilder:object:generate=true +type RuntimeDetailsByContainer struct { + ContainerName string `json:"containerName"` + Language common.ProgrammingLanguage `json:"language"` + RuntimeVersion string `json:"runtimeVersion,omitempty"` + EnvVars []EnvVar `json:"envVars,omitempty"` + OtherAgent *OtherAgent `json:"otherAgent,omitempty"` + LibCType *common.LibCType `json:"libCType,omitempty"` + + // Stores the error message from the CRI runtime if returned to prevent instrumenting the container if an error exists. + CriErrorMessage *string `json:"criErrorMessage,omitempty"` + // Holds the environment variables retrieved from the container runtime. + EnvFromContainerRuntime []EnvVar `json:"envFromContainerRuntime,omitempty"` + // A temporary variable used during migration to track whether the new runtime detection process has been executed. If empty, it indicates the process has not yet been run. This field may be removed later. + RuntimeUpdateState *ProcessingState `json:"runtimeUpdateState,omitempty"` +} + type InstrumentationConfigStatus struct { // Capture Runtime Details for the workloads that this CR applies to. RuntimeDetailsByContainer []RuntimeDetailsByContainer `json:"runtimeDetailsByContainer,omitempty"` + + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" protobuf:"bytes,1,rep,name=conditions"` } // Config for the OpenTelemeetry SDKs that should be applied to a workload. diff --git a/api/odigos/v1alpha1/instrumentedapplication_types.go b/api/odigos/v1alpha1/instrumentedapplication_types.go index cbedca502..85be3223c 100644 --- a/api/odigos/v1alpha1/instrumentedapplication_types.go +++ b/api/odigos/v1alpha1/instrumentedapplication_types.go @@ -51,23 +51,6 @@ const ( ProcessingStateSkipped ProcessingState = "Skipped" ) -// +kubebuilder:object:generate=true -type RuntimeDetailsByContainer struct { - ContainerName string `json:"containerName"` - Language common.ProgrammingLanguage `json:"language"` - RuntimeVersion string `json:"runtimeVersion,omitempty"` - EnvVars []EnvVar `json:"envVars,omitempty"` - OtherAgent *OtherAgent `json:"otherAgent,omitempty"` - LibCType *common.LibCType `json:"libCType,omitempty"` - - // Stores the error message from the CRI runtime if returned to prevent instrumenting the container if an error exists. - CriErrorMessage *string `json:"criErrorMessage,omitempty"` - // Holds the environment variables retrieved from the container runtime. - EnvFromContainerRuntime []EnvVar `json:"envFromContainerRuntime,omitempty"` - // A temporary variable used during migration to track whether the new runtime detection process has been executed. If empty, it indicates the process has not yet been run. This field may be removed later. - RuntimeUpdateState *ProcessingState `json:"runtimeUpdateState,omitempty"` -} - // +kubebuilder:object:generate=true type OptionByContainer struct { ContainerName string `json:"containerName"` @@ -110,6 +93,16 @@ type InstrumentedApplicationList struct { Items []InstrumentedApplication `json:"items"` } +var _ WorkloadDetails = &InstrumentedApplication{} + +func (ia *InstrumentedApplication) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { + return ia.Spec.RuntimeDetails +} + +func (ia *InstrumentedApplication) Conditions() *[]metav1.Condition { + return &ia.Status.Conditions +} + func init() { SchemeBuilder.Register(&InstrumentedApplication{}, &InstrumentedApplicationList{}) } diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 5c7edb7ad..4d703eb33 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -484,6 +484,13 @@ func (in *InstrumentationConfigStatus) DeepCopyInto(out *InstrumentationConfigSt (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationConfigStatus. diff --git a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml index 8fb450866..5a49486ff 100644 --- a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml +++ b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml @@ -423,6 +423,62 @@ spec: type: object status: properties: + conditions: + 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 runtimeDetailsByContainer: description: Capture Runtime Details for the workloads that this CR applies to. diff --git a/instrumentor/controllers/instrumentationdevice/common.go b/instrumentor/controllers/instrumentationdevice/common.go index a0de5727a..e93919dae 100644 --- a/instrumentor/controllers/instrumentationdevice/common.go +++ b/instrumentor/controllers/instrumentationdevice/common.go @@ -66,13 +66,13 @@ func isDataCollectionReady(ctx context.Context, c client.Client) bool { return nodeCollectorsGroup.Status.Ready } -func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, runtimeDetails *odigosv1.InstrumentedApplication) (error, bool) { +func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetails) (error, bool) { // devicePartiallyApplied is used to indicate that the instrumentation device was partially applied for some of the containers. devicePartiallyApplied := false deviceNotAppliedDueToPresenceOfAnotherAgent := false logger := log.FromContext(ctx) - obj, err := getWorkloadObject(ctx, kubeClient, runtimeDetails) + obj, err := getWorkloadObject(ctx, kubeClient, workloadDetails) if err != nil { return err, false } @@ -133,7 +133,7 @@ func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.C agentsCanRunConcurrently = *odigosConfiguration.AllowConcurrentAgents } - err, deviceApplied, deviceSkippedDueToOtherAgent := instrumentation.ApplyInstrumentationDevicesToPodTemplate(podSpec, runtimeDetails, otelSdkToUse, obj, logger, agentsCanRunConcurrently) + err, deviceApplied, deviceSkippedDueToOtherAgent := instrumentation.ApplyInstrumentationDevicesToPodTemplate(podSpec, workloadDetails.RuntimeDetailsByContainer(), otelSdkToUse, obj, logger, agentsCanRunConcurrently) if err != nil { return err } @@ -213,8 +213,8 @@ func removeInstrumentationDeviceFromWorkload(ctx context.Context, kubeClient cli return nil } -func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDetails *odigosv1.InstrumentedApplication) (client.Object, error) { - name, kind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(runtimeDetails.Name) +func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDetailsObject client.Object) (client.Object, error) { + name, kind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(runtimeDetailsObject.GetName()) if err != nil { return nil, err } @@ -225,7 +225,7 @@ func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDet } err = kubeClient.Get(ctx, client.ObjectKey{ - Namespace: runtimeDetails.Namespace, + Namespace: runtimeDetailsObject.GetNamespace(), Name: name, }, workloadObject) if err != nil { @@ -251,45 +251,45 @@ func getPodSpecFromObject(obj client.Object) (*corev1.PodTemplateSpec, error) { // reconciles a single workload, which might be triggered by a change in multiple resources. // each time a relevant resource changes, this function is called to reconcile the workload // and always writes the status into the InstrumentedApplication CR -func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, instrumentedApplication *odigosv1.InstrumentedApplication, isNodeCollectorReady bool) error { +func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetails, isNodeCollectorReady bool) error { - workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedApplication.Name) + workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(workloadDetails.GetName()) if err != nil { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) return err } if !isNodeCollectorReady { - err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonDataCollectionNotReady) + err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonDataCollectionNotReady) if err == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonDataCollectionNotReady), "OpenTelemetry pipeline not yet ready to receive data") + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonDataCollectionNotReady), "OpenTelemetry pipeline not yet ready to receive data") } else { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) } return err } - if len(instrumentedApplication.Spec.RuntimeDetails) == 0 { - err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonNoRuntimeDetails) + if len(workloadDetails.RuntimeDetailsByContainer()) == 0 { + err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonNoRuntimeDetails) if err == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonNoRuntimeDetails), "No runtime details found") + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonNoRuntimeDetails), "No runtime details found") } else { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) } return err } - runtimeVersionSupport, err := versionsupport.IsRuntimeVersionSupported(ctx, instrumentedApplication.Spec.RuntimeDetails) + runtimeVersionSupport, err := versionsupport.IsRuntimeVersionSupported(ctx, workloadDetails.RuntimeDetailsByContainer()) if !runtimeVersionSupport { - errRemove := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported) + errRemove := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported) if errRemove == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported), err.Error()) } else { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), errRemove.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), errRemove.Error()) } return nil } - err, devicePartiallyApplied := addInstrumentationDeviceToWorkload(ctx, kubeClient, instrumentedApplication) + err, devicePartiallyApplied := addInstrumentationDeviceToWorkload(ctx, kubeClient, workloadDetails) if err == nil { var successMessage string if devicePartiallyApplied { @@ -297,9 +297,9 @@ func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, inst } else { successMessage = "Instrumentation device applied successfully" } - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionTrue, appliedInstrumentationDeviceType, "InstrumentationDeviceApplied", successMessage) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionTrue, appliedInstrumentationDeviceType, "InstrumentationDeviceApplied", successMessage) } else { - conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrApplying), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrApplying), err.Error()) } return err } diff --git a/instrumentor/instrumentation/instrumentation.go b/instrumentor/instrumentation/instrumentation.go index f5afd09b4..5451356e3 100644 --- a/instrumentor/instrumentation/instrumentation.go +++ b/instrumentor/instrumentation/instrumentation.go @@ -22,7 +22,7 @@ var ( ErrPatchEnvVars = errors.New("failed to patch env vars") ) -func ApplyInstrumentationDevicesToPodTemplate(original *corev1.PodTemplateSpec, runtimeDetails *odigosv1.InstrumentedApplication, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object, +func ApplyInstrumentationDevicesToPodTemplate(original *corev1.PodTemplateSpec, runtimeDetails []odigosv1.RuntimeDetailsByContainer, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object, logger logr.Logger, agentsCanRunConcurrently bool) (error, bool, bool) { // delete any existing instrumentation devices. // this is necessary for example when migrating from community to enterprise, @@ -154,8 +154,8 @@ func RevertInstrumentationDevices(original *corev1.PodTemplateSpec) bool { return changed } -func getLanguageOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) common.ProgrammingLanguage { - for _, l := range instrumentation.Spec.RuntimeDetails { +func getLanguageOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) common.ProgrammingLanguage { + for _, l := range runtimeDetails { if l.ContainerName == containerName { return l.Language } @@ -164,8 +164,8 @@ func getLanguageOfContainer(instrumentation *odigosv1.InstrumentedApplication, c return common.UnknownProgrammingLanguage } -func getContainerOtherAgents(instrumentation *odigosv1.InstrumentedApplication, containerName string) *odigosv1.OtherAgent { - for _, l := range instrumentation.Spec.RuntimeDetails { +func getContainerOtherAgents(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) *odigosv1.OtherAgent { + for _, l := range runtimeDetails { if l.ContainerName == containerName { if l.OtherAgent != nil && *l.OtherAgent != (odigosv1.OtherAgent{}) { return l.OtherAgent @@ -175,8 +175,8 @@ func getContainerOtherAgents(instrumentation *odigosv1.InstrumentedApplication, return nil } -func getLibCTypeOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) *common.LibCType { - for _, l := range instrumentation.Spec.RuntimeDetails { +func getLibCTypeOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) *common.LibCType { + for _, l := range runtimeDetails { if l.ContainerName == containerName { return l.LibCType } @@ -187,10 +187,10 @@ func getLibCTypeOfContainer(instrumentation *odigosv1.InstrumentedApplication, c // getEnvVarsOfContainer returns the env vars which are defined for the given container and are used for instrumentation purposes. // This function also returns env vars which are declared in the container build. -func getEnvVarsOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) map[string]string { +func getEnvVarsOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) map[string]string { envVars := make(map[string]string) - for _, l := range instrumentation.Spec.RuntimeDetails { + for _, l := range runtimeDetails { if l.ContainerName == containerName { for _, env := range l.EnvVars { envVars[env.Name] = env.Value @@ -204,7 +204,7 @@ func getEnvVarsOfContainer(instrumentation *odigosv1.InstrumentedApplication, co // when otelsdk is nil, it means that the container is not instrumented. // this will trigger reverting of any existing env vars which were set by odigos before. -func patchEnvVarsForContainer(runtimeDetails *odigosv1.InstrumentedApplication, container *corev1.Container, sdk *common.OtelSdk, programmingLanguage common.ProgrammingLanguage, manifestEnvOriginal *envoverwrite.OrigWorkloadEnvValues) error { +func patchEnvVarsForContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, container *corev1.Container, sdk *common.OtelSdk, programmingLanguage common.ProgrammingLanguage, manifestEnvOriginal *envoverwrite.OrigWorkloadEnvValues) error { observedEnvs := getEnvVarsOfContainer(runtimeDetails, container.Name) From 2dddc118a0eb87510fc5ad897b23f9065961bdb9 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 27 Dec 2024 18:32:31 +0200 Subject: [PATCH 134/259] rename interface to WorkloadDetailsObject --- api/odigos/v1alpha1/instrumentationconfig_types.go | 4 ++-- api/odigos/v1alpha1/instrumentedapplication_types.go | 2 +- instrumentor/controllers/instrumentationdevice/common.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/odigos/v1alpha1/instrumentationconfig_types.go b/api/odigos/v1alpha1/instrumentationconfig_types.go index 6d4b93882..a8158d713 100644 --- a/api/odigos/v1alpha1/instrumentationconfig_types.go +++ b/api/odigos/v1alpha1/instrumentationconfig_types.go @@ -9,7 +9,7 @@ import ( ) // +kubebuilder:object:generate=false -type WorkloadDetails interface { +type WorkloadDetailsObject interface { client.Object RuntimeDetailsByContainer() []RuntimeDetailsByContainer Conditions() *[]metav1.Condition @@ -28,7 +28,7 @@ type InstrumentationConfig struct { Status InstrumentationConfigStatus `json:"status,omitempty"` } -var _ WorkloadDetails = &InstrumentationConfig{} +var _ WorkloadDetailsObject = &InstrumentationConfig{} func (ic *InstrumentationConfig) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { return ic.Status.RuntimeDetailsByContainer diff --git a/api/odigos/v1alpha1/instrumentedapplication_types.go b/api/odigos/v1alpha1/instrumentedapplication_types.go index 85be3223c..b66a6c6a2 100644 --- a/api/odigos/v1alpha1/instrumentedapplication_types.go +++ b/api/odigos/v1alpha1/instrumentedapplication_types.go @@ -93,7 +93,7 @@ type InstrumentedApplicationList struct { Items []InstrumentedApplication `json:"items"` } -var _ WorkloadDetails = &InstrumentedApplication{} +var _ WorkloadDetailsObject = &InstrumentedApplication{} func (ia *InstrumentedApplication) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { return ia.Spec.RuntimeDetails diff --git a/instrumentor/controllers/instrumentationdevice/common.go b/instrumentor/controllers/instrumentationdevice/common.go index e93919dae..7adaecab1 100644 --- a/instrumentor/controllers/instrumentationdevice/common.go +++ b/instrumentor/controllers/instrumentationdevice/common.go @@ -66,7 +66,7 @@ func isDataCollectionReady(ctx context.Context, c client.Client) bool { return nodeCollectorsGroup.Status.Ready } -func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetails) (error, bool) { +func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetailsObject) (error, bool) { // devicePartiallyApplied is used to indicate that the instrumentation device was partially applied for some of the containers. devicePartiallyApplied := false deviceNotAppliedDueToPresenceOfAnotherAgent := false @@ -251,7 +251,7 @@ func getPodSpecFromObject(obj client.Object) (*corev1.PodTemplateSpec, error) { // reconciles a single workload, which might be triggered by a change in multiple resources. // each time a relevant resource changes, this function is called to reconcile the workload // and always writes the status into the InstrumentedApplication CR -func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetails, isNodeCollectorReady bool) error { +func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetailsObject, isNodeCollectorReady bool) error { workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(workloadDetails.GetName()) if err != nil { From 1d1bcbafe34b7acd68da8f73590523cec31577d2 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 17:03:41 +0200 Subject: [PATCH 135/259] Improve error messages in source retrieval and add workload kind validation --- frontend/services/sources.go | 16 +++++++++------- frontend/services/utils.go | 10 ++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 7c1ca5647..9c548b473 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -330,10 +330,10 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, err } if len(sourceList.Items) == 0 { - return nil, errors.New("source not found " + "\"" + workloadName + "\"") + return nil, errors.New("\"" + workloadName + "\"" + " source not found") } if len(sourceList.Items) > 1 { - return nil, errors.New("too many sources " + "\"" + workloadName + "\"") + return nil, errors.New("\"" + workloadName + "\"" + " expected to get 1 source got " + string(len(sourceList.Items))) } crdName := sourceList.Items[0].Name @@ -343,13 +343,14 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl } func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) + err := CheckWorkloadKind(workloadKind) + if err != nil { + return err } source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return errors.New("source already exists " + "\"" + workloadName + "\"") + return errors.New("\"" + workloadName + "\"" + " source already exists") } newSource := &v1alpha1.Source{ @@ -370,8 +371,9 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo } func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - if workloadKind != WorkloadKindDeployment && workloadKind != WorkloadKindStatefulSet && workloadKind != WorkloadKindDaemonSet { - return errors.New("unsupported workload kind " + string(workloadKind)) + err := CheckWorkloadKind(workloadKind) + if err != nil { + return err } source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index 6d8f1603e..c092e532e 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -1,6 +1,7 @@ package services import ( + "errors" "fmt" "path" "time" @@ -50,3 +51,12 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } return latestStatusTime.Time.Format(time.RFC3339) } + +func CheckWorkloadKind(kind WorkloadKind) error { + switch kind { + case WorkloadKindDeployment, WorkloadKindStatefulSet, WorkloadKindDaemonSet: + return nil + default: + return errors.New("unsupported workload kind: " + string(kind)) + } +} From fbd60072644821363264bb1ce2cdacaf223603e1 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 18:03:52 +0200 Subject: [PATCH 136/259] Add --nowait option to cli-install command in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 61f6679c7..96005d933 100644 --- a/Makefile +++ b/Makefile @@ -199,7 +199,7 @@ check-clean-work-tree: .PHONY: cli-install cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" - cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) + cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) --nowait .PHONY: cli-uninstall cli-uninstall: From d5fbab8e8afa71619bb1b4dd9ad5e80d95b876ca Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 18:55:07 +0200 Subject: [PATCH 137/259] Refactor K8sActualSource and related components to streamline data handling; update conditions and containers access patterns --- frontend/graph/conversions.go | 30 +- frontend/graph/generated.go | 1214 +++-------------- frontend/graph/model/models_gen.go | 36 +- frontend/graph/schema.graphqls | 28 +- frontend/graph/schema.resolvers.go | 71 +- frontend/services/sources.go | 74 +- .../common/dropdowns/error-dropdown/index.tsx | 4 +- .../dropdowns/language-dropdown/index.tsx | 2 +- .../build-drawer-item.ts | 8 +- .../sources/source-drawer-container/index.tsx | 9 +- .../graphql/queries/compute-platform.ts | 29 +- .../compute-platform/useComputePlatform.ts | 8 +- frontend/webapp/types/compute-platform.ts | 2 +- frontend/webapp/types/sources.ts | 13 +- .../utils/constants/programming-languages.ts | 27 +- .../strings/get-entity-label/index.ts | 2 +- .../strings/get-health-status/index.ts | 2 +- 17 files changed, 315 insertions(+), 1244 deletions(-) diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index 67746ccc1..d4f093047 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -42,10 +42,10 @@ func k8sLastTransitionTimeToGql(t v1.Time) *string { return &str } -func instrumentedApplicationToActualSource(instrumentedApp v1alpha1.InstrumentedApplication) *model.K8sActualSource { - // Map the container runtime details +func instrumentationConfigToActualSource(instruConfig v1alpha1.InstrumentationConfig) *model.K8sActualSource { + // Map the containers runtime details var containers []*model.SourceContainerRuntimeDetails - for _, container := range instrumentedApp.Spec.RuntimeDetails { + for _, container := range instruConfig.Status.RuntimeDetailsByContainer { var otherAgentName *string if container.OtherAgent != nil { otherAgentName = &container.OtherAgent.Name @@ -59,30 +59,28 @@ func instrumentedApplicationToActualSource(instrumentedApp v1alpha1.Instrumented }) } - // Map the conditions of the application + // Map the conditions var conditions []*model.Condition - for _, condition := range instrumentedApp.Status.Conditions { + for _, condition := range instruConfig.Status.Conditions { conditions = append(conditions, &model.Condition{ - Type: condition.Type, Status: k8sConditionStatusToGql(condition.Status), + Type: condition.Type, Reason: &condition.Reason, - LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), Message: &condition.Message, + LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), }) } // Return the converted K8sActualSource object return &model.K8sActualSource{ - Namespace: instrumentedApp.Namespace, - Kind: k8sKindToGql(instrumentedApp.OwnerReferences[0].Kind), - Name: instrumentedApp.OwnerReferences[0].Name, - ServiceName: &instrumentedApp.Name, + Namespace: instruConfig.Namespace, + Kind: k8sKindToGql(instruConfig.OwnerReferences[0].Kind), + Name: instruConfig.OwnerReferences[0].Name, NumberOfInstances: nil, - AutoInstrumented: instrumentedApp.Spec.Options != nil, - InstrumentedApplicationDetails: &model.InstrumentedApplicationDetails{ - Containers: containers, - Conditions: conditions, - }, + ServiceName: &instruConfig.Name, + ReportedName: &instruConfig.Spec.ServiceName, + Containers: containers, + Conditions: conditions, } } diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index a0642bc58..5da74e178 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -85,7 +85,6 @@ type ComplexityRoot struct { InstrumentationRules func(childComplexity int) int K8sActualNamespace func(childComplexity int, name string) int K8sActualNamespaces func(childComplexity int) int - K8sActualSource func(childComplexity int, name *string, namespace *string, kind *string) int K8sActualSources func(childComplexity int) int } @@ -240,22 +239,12 @@ type ComplexityRoot struct { Workload func(childComplexity int) int } - InstrumentationLibrary struct { - LibraryName func(childComplexity int) int - Options func(childComplexity int) int - } - InstrumentationLibraryGlobalId struct { Language func(childComplexity int) int Name func(childComplexity int) int SpanKind func(childComplexity int) int } - InstrumentationOption struct { - OptionKey func(childComplexity int) int - SpanKind func(childComplexity int) int - } - InstrumentationRule struct { Disabled func(childComplexity int) int InstrumentationLibraries func(childComplexity int) int @@ -272,12 +261,6 @@ type ComplexityRoot struct { Created func(childComplexity int) int } - InstrumentedApplicationDetails struct { - Conditions func(childComplexity int) int - Containers func(childComplexity int) int - InstrumentationOptions func(childComplexity int) int - } - K8sActualNamespace struct { InstrumentationLabelEnabled func(childComplexity int) int K8sActualSources func(childComplexity int, instrumentationLabeled *bool) int @@ -285,15 +268,14 @@ type ComplexityRoot struct { } K8sActualSource struct { - AutoInstrumented func(childComplexity int) int - AutoInstrumentedDecision func(childComplexity int) int - InstrumentedApplicationDetails func(childComplexity int) int - Kind func(childComplexity int) int - Name func(childComplexity int) int - Namespace func(childComplexity int) int - NumberOfInstances func(childComplexity int) int - ReportedName func(childComplexity int) int - ServiceName func(childComplexity int) int + Conditions func(childComplexity int) int + Containers func(childComplexity int) int + Kind func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + NumberOfInstances func(childComplexity int) int + ReportedName func(childComplexity int) int + ServiceName func(childComplexity int) int } LatencySamplerAction struct { @@ -489,9 +471,8 @@ type ComplexityRoot struct { } type ComputePlatformResolver interface { - K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) - K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) + K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) @@ -730,18 +711,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ComputePlatform.K8sActualNamespaces(childComplexity), true - case "ComputePlatform.k8sActualSource": - if e.complexity.ComputePlatform.K8sActualSource == nil { - break - } - - args, err := ec.field_ComputePlatform_k8sActualSource_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.ComputePlatform.K8sActualSource(childComplexity, args["name"].(*string), args["namespace"].(*string), args["kind"].(*string)), true - case "ComputePlatform.k8sActualSources": if e.complexity.ComputePlatform.K8sActualSources == nil { break @@ -1323,20 +1292,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationLabelsAnalyze.Workload(childComplexity), true - case "InstrumentationLibrary.libraryName": - if e.complexity.InstrumentationLibrary.LibraryName == nil { - break - } - - return e.complexity.InstrumentationLibrary.LibraryName(childComplexity), true - - case "InstrumentationLibrary.options": - if e.complexity.InstrumentationLibrary.Options == nil { - break - } - - return e.complexity.InstrumentationLibrary.Options(childComplexity), true - case "InstrumentationLibraryGlobalId.language": if e.complexity.InstrumentationLibraryGlobalId.Language == nil { break @@ -1358,20 +1313,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationLibraryGlobalId.SpanKind(childComplexity), true - case "InstrumentationOption.optionKey": - if e.complexity.InstrumentationOption.OptionKey == nil { - break - } - - return e.complexity.InstrumentationOption.OptionKey(childComplexity), true - - case "InstrumentationOption.spanKind": - if e.complexity.InstrumentationOption.SpanKind == nil { - break - } - - return e.complexity.InstrumentationOption.SpanKind(childComplexity), true - case "InstrumentationRule.disabled": if e.complexity.InstrumentationRule.Disabled == nil { break @@ -1442,27 +1383,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentedApplicationAnalyze.Created(childComplexity), true - case "InstrumentedApplicationDetails.conditions": - if e.complexity.InstrumentedApplicationDetails.Conditions == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.Conditions(childComplexity), true - - case "InstrumentedApplicationDetails.containers": - if e.complexity.InstrumentedApplicationDetails.Containers == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.Containers(childComplexity), true - - case "InstrumentedApplicationDetails.instrumentationOptions": - if e.complexity.InstrumentedApplicationDetails.InstrumentationOptions == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.InstrumentationOptions(childComplexity), true - case "K8sActualNamespace.instrumentationLabelEnabled": if e.complexity.K8sActualNamespace.InstrumentationLabelEnabled == nil { break @@ -1489,26 +1409,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualNamespace.Name(childComplexity), true - case "K8sActualSource.autoInstrumented": - if e.complexity.K8sActualSource.AutoInstrumented == nil { - break - } - - return e.complexity.K8sActualSource.AutoInstrumented(childComplexity), true - - case "K8sActualSource.autoInstrumentedDecision": - if e.complexity.K8sActualSource.AutoInstrumentedDecision == nil { + case "K8sActualSource.conditions": + if e.complexity.K8sActualSource.Conditions == nil { break } - return e.complexity.K8sActualSource.AutoInstrumentedDecision(childComplexity), true + return e.complexity.K8sActualSource.Conditions(childComplexity), true - case "K8sActualSource.instrumentedApplicationDetails": - if e.complexity.K8sActualSource.InstrumentedApplicationDetails == nil { + case "K8sActualSource.containers": + if e.complexity.K8sActualSource.Containers == nil { break } - return e.complexity.K8sActualSource.InstrumentedApplicationDetails(childComplexity), true + return e.complexity.K8sActualSource.Containers(childComplexity), true case "K8sActualSource.kind": if e.complexity.K8sActualSource.Kind == nil { @@ -2638,39 +2551,6 @@ func (ec *executionContext) field_ComputePlatform_k8sActualNamespace_args(ctx co return args, nil } -func (ec *executionContext) field_ComputePlatform_k8sActualSource_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 *string - if tmp, ok := rawArgs["name"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["name"] = arg0 - var arg1 *string - if tmp, ok := rawArgs["namespace"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) - arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["namespace"] = arg1 - var arg2 *string - if tmp, ok := rawArgs["kind"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("kind")) - arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["kind"] = arg2 - return args, nil -} - func (ec *executionContext) field_K8sActualNamespace_k8sActualSources_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -3992,66 +3872,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_computePlatformType(_ c return fc, nil } -func (ec *executionContext) _ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().K8sActualNamespace(rctx, obj, fc.Args["name"].(string)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*model.K8sActualNamespace) - fc.Result = res - return ec.marshalOK8sActualNamespace2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualNamespace(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ComputePlatform", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_K8sActualNamespace_name(ctx, field) - case "instrumentationLabelEnabled": - return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) - case "k8sActualSources": - return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type K8sActualNamespace", field.Name) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_ComputePlatform_k8sActualNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - func (ec *executionContext) _ComputePlatform_k8sActualNamespaces(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) if err != nil { @@ -4104,8 +3924,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespaces(_ c return fc, nil } -func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) +func (ec *executionContext) _ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) if err != nil { return graphql.Null } @@ -4118,7 +3938,7 @@ func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().K8sActualSource(rctx, obj, fc.Args["name"].(*string), fc.Args["namespace"].(*string), fc.Args["kind"].(*string)) + return ec.resolvers.ComputePlatform().K8sActualNamespace(rctx, obj, fc.Args["name"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -4127,12 +3947,12 @@ func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context if resTmp == nil { return graphql.Null } - res := resTmp.(*model.K8sActualSource) + res := resTmp.(*model.K8sActualNamespace) fc.Result = res - return ec.marshalOK8sActualSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualSource(ctx, field.Selections, res) + return ec.marshalOK8sActualNamespace2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualNamespace(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ComputePlatform", Field: field, @@ -4140,26 +3960,14 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx con IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "namespace": - return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": - return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) - case "numberOfInstances": - return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) - case "reportedName": - return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + return ec.fieldContext_K8sActualNamespace_name(ctx, field) + case "instrumentationLabelEnabled": + return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) + case "k8sActualSources": + return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) + return nil, fmt.Errorf("no field named %q was found under type K8sActualNamespace", field.Name) }, } defer func() { @@ -4169,7 +3977,7 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx con } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_ComputePlatform_k8sActualSource_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_ComputePlatform_k8sActualNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -4217,22 +4025,20 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont switch field.Name { case "namespace": return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) + case "kind": + return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "serviceName": + return ec.fieldContext_K8sActualSource_serviceName(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + case "containers": + return ec.fieldContext_K8sActualSource_containers(ctx, field) + case "conditions": + return ec.fieldContext_K8sActualSource_conditions(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) }, @@ -8226,8 +8032,8 @@ func (ec *executionContext) fieldContext_InstrumentationLabelsAnalyze_instrument return fc, nil } -func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibrary) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibrary_libraryName(ctx, field) +func (ec *executionContext) _InstrumentationLibraryGlobalId_name(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_name(ctx, field) if err != nil { return graphql.Null } @@ -8240,7 +8046,7 @@ func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Cont }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.LibraryName, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -8257,9 +8063,9 @@ func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Cont return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibrary_libraryName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibrary", + Object: "InstrumentationLibraryGlobalId", Field: field, IsMethod: false, IsResolver: false, @@ -8270,8 +8076,8 @@ func (ec *executionContext) fieldContext_InstrumentationLibrary_libraryName(_ co return fc, nil } -func (ec *executionContext) _InstrumentationLibrary_options(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibrary) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibrary_options(ctx, field) +func (ec *executionContext) _InstrumentationLibraryGlobalId_spanKind(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_spanKind(ctx, field) if err != nil { return graphql.Null } @@ -8284,44 +8090,35 @@ func (ec *executionContext) _InstrumentationLibrary_options(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Options, nil + return obj.SpanKind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.InstrumentationOption) + res := resTmp.(*model.SpanKind) fc.Result = res - return ec.marshalNInstrumentationOption2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOptionᚄ(ctx, field.Selections, res) + return ec.marshalOSpanKind2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibrary_options(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_spanKind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibrary", + Object: "InstrumentationLibraryGlobalId", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "optionKey": - return ec.fieldContext_InstrumentationOption_optionKey(ctx, field) - case "spanKind": - return ec.fieldContext_InstrumentationOption_spanKind(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationOption", field.Name) + return nil, errors.New("field of type SpanKind does not have child fields") }, } return fc, nil } -func (ec *executionContext) _InstrumentationLibraryGlobalId_name(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_name(ctx, field) +func (ec *executionContext) _InstrumentationLibraryGlobalId_language(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_language(ctx, field) if err != nil { return graphql.Null } @@ -8334,38 +8131,35 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId_name(ctx context.Con }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Language, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*model.ProgrammingLanguage) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOProgrammingLanguage2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐProgrammingLanguage(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_language(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "InstrumentationLibraryGlobalId", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ProgrammingLanguage does not have child fields") }, } return fc, nil } -func (ec *executionContext) _InstrumentationLibraryGlobalId_spanKind(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_spanKind(ctx, field) +func (ec *executionContext) _InstrumentationRule_ruleId(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationRule_ruleId(ctx, field) if err != nil { return graphql.Null } @@ -8378,35 +8172,38 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId_spanKind(ctx context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SpanKind, nil + return obj.RuleID, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*model.SpanKind) + res := resTmp.(string) fc.Result = res - return ec.marshalOSpanKind2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_spanKind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationRule_ruleId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibraryGlobalId", + Object: "InstrumentationRule", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type SpanKind does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _InstrumentationLibraryGlobalId_language(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_language(ctx, field) +func (ec *executionContext) _InstrumentationRule_ruleName(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationRule_ruleName(ctx, field) if err != nil { return graphql.Null } @@ -8419,7 +8216,7 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId_language(ctx context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Language, nil + return obj.RuleName, nil }) if err != nil { ec.Error(ctx, err) @@ -8428,26 +8225,26 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId_language(ctx context if resTmp == nil { return graphql.Null } - res := resTmp.(*model.ProgrammingLanguage) + res := resTmp.(*string) fc.Result = res - return ec.marshalOProgrammingLanguage2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐProgrammingLanguage(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_language(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationRule_ruleName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibraryGlobalId", + Object: "InstrumentationRule", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ProgrammingLanguage does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _InstrumentationOption_optionKey(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationOption) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationOption_optionKey(ctx, field) +func (ec *executionContext) _InstrumentationRule_notes(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationRule_notes(ctx, field) if err != nil { return graphql.Null } @@ -8460,26 +8257,23 @@ func (ec *executionContext) _InstrumentationOption_optionKey(ctx context.Context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.OptionKey, nil + return obj.Notes, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationOption_optionKey(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationRule_notes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationOption", + Object: "InstrumentationRule", Field: field, IsMethod: false, IsResolver: false, @@ -8490,8 +8284,8 @@ func (ec *executionContext) fieldContext_InstrumentationOption_optionKey(_ conte return fc, nil } -func (ec *executionContext) _InstrumentationOption_spanKind(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationOption) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationOption_spanKind(ctx, field) +func (ec *executionContext) _InstrumentationRule_disabled(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationRule_disabled(ctx, field) if err != nil { return graphql.Null } @@ -8504,177 +8298,7 @@ func (ec *executionContext) _InstrumentationOption_spanKind(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SpanKind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(model.SpanKind) - fc.Result = res - return ec.marshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationOption_spanKind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationOption", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type SpanKind does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationRule_ruleId(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationRule_ruleId(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.RuleID, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationRule_ruleId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationRule", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationRule_ruleName(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationRule_ruleName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.RuleName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*string) - fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationRule_ruleName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationRule", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationRule_notes(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationRule_notes(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Notes, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*string) - fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationRule_notes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationRule", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationRule_disabled(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationRule_disabled(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Disabled, nil + return obj.Disabled, nil }) if err != nil { ec.Error(ctx, err) @@ -9009,160 +8633,6 @@ func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_containe return fc, nil } -func (ec *executionContext) _InstrumentedApplicationDetails_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_containers(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Containers, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.SourceContainerRuntimeDetails) - fc.Result = res - return ec.marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "containerName": - return ec.fieldContext_SourceContainerRuntimeDetails_containerName(ctx, field) - case "language": - return ec.fieldContext_SourceContainerRuntimeDetails_language(ctx, field) - case "runtimeVersion": - return ec.fieldContext_SourceContainerRuntimeDetails_runtimeVersion(ctx, field) - case "otherAgent": - return ec.fieldContext_SourceContainerRuntimeDetails_otherAgent(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type SourceContainerRuntimeDetails", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationDetails_conditions(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_conditions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Conditions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.Condition) - fc.Result = res - return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "status": - return ec.fieldContext_Condition_status(ctx, field) - case "type": - return ec.fieldContext_Condition_type(ctx, field) - case "reason": - return ec.fieldContext_Condition_reason(ctx, field) - case "message": - return ec.fieldContext_Condition_message(ctx, field) - case "lastTransitionTime": - return ec.fieldContext_Condition_lastTransitionTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationDetails_instrumentationOptions(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_instrumentationOptions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.InstrumentationOptions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.InstrumentationLibrary) - fc.Result = res - return ec.marshalNInstrumentationLibrary2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_instrumentationOptions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "libraryName": - return ec.fieldContext_InstrumentationLibrary_libraryName(ctx, field) - case "options": - return ec.fieldContext_InstrumentationLibrary_options(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationLibrary", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualNamespace_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualNamespace_name(ctx, field) if err != nil { @@ -9289,22 +8759,20 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx switch field.Name { case "namespace": return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) + case "kind": + return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "serviceName": + return ec.fieldContext_K8sActualSource_serviceName(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + case "containers": + return ec.fieldContext_K8sActualSource_containers(ctx, field) + case "conditions": + return ec.fieldContext_K8sActualSource_conditions(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) }, @@ -9367,50 +8835,6 @@ func (ec *executionContext) fieldContext_K8sActualSource_namespace(_ context.Con return fc, nil } -func (ec *executionContext) _K8sActualSource_kind(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_kind(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Kind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(model.K8sResourceKind) - fc.Result = res - return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_K8sActualSource_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "K8sActualSource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type K8sResourceKind does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualSource_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualSource_name(ctx, field) if err != nil { @@ -9455,8 +8879,8 @@ func (ec *executionContext) fieldContext_K8sActualSource_name(_ context.Context, return fc, nil } -func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_serviceName(ctx, field) +func (ec *executionContext) _K8sActualSource_kind(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_kind(ctx, field) if err != nil { return graphql.Null } @@ -9469,28 +8893,31 @@ func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ServiceName, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(model.K8sResourceKind) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_serviceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type K8sResourceKind does not have child fields") }, } return fc, nil @@ -9537,8 +8964,8 @@ func (ec *executionContext) fieldContext_K8sActualSource_numberOfInstances(_ con return fc, nil } -func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) +func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_serviceName(ctx, field) if err != nil { return graphql.Null } @@ -9551,7 +8978,7 @@ func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ReportedName, nil + return obj.ServiceName, nil }) if err != nil { ec.Error(ctx, err) @@ -9565,7 +8992,7 @@ func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, f return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_reportedName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_serviceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, @@ -9578,8 +9005,8 @@ func (ec *executionContext) fieldContext_K8sActualSource_reportedName(_ context. return fc, nil } -func (ec *executionContext) _K8sActualSource_autoInstrumented(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) +func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) if err != nil { return graphql.Null } @@ -9592,38 +9019,35 @@ func (ec *executionContext) _K8sActualSource_autoInstrumented(ctx context.Contex }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.AutoInstrumented, nil + return obj.ReportedName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(bool) + res := resTmp.(*string) fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_autoInstrumented(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_reportedName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _K8sActualSource_autoInstrumentedDecision(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) +func (ec *executionContext) _K8sActualSource_containers(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_containers(ctx, field) if err != nil { return graphql.Null } @@ -9636,38 +9060,45 @@ func (ec *executionContext) _K8sActualSource_autoInstrumentedDecision(ctx contex }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.AutoInstrumentedDecision, nil + return obj.Containers, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.([]*model.SourceContainerRuntimeDetails) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_autoInstrumentedDecision(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "containerName": + return ec.fieldContext_SourceContainerRuntimeDetails_containerName(ctx, field) + case "language": + return ec.fieldContext_SourceContainerRuntimeDetails_language(ctx, field) + case "runtimeVersion": + return ec.fieldContext_SourceContainerRuntimeDetails_runtimeVersion(ctx, field) + case "otherAgent": + return ec.fieldContext_SourceContainerRuntimeDetails_otherAgent(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceContainerRuntimeDetails", field.Name) }, } return fc, nil } -func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) +func (ec *executionContext) _K8sActualSource_conditions(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_conditions(ctx, field) if err != nil { return graphql.Null } @@ -9680,7 +9111,7 @@ func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.InstrumentedApplicationDetails, nil + return obj.Conditions, nil }) if err != nil { ec.Error(ctx, err) @@ -9689,12 +9120,12 @@ func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx if resTmp == nil { return graphql.Null } - res := resTmp.(*model.InstrumentedApplicationDetails) + res := resTmp.([]*model.Condition) fc.Result = res - return ec.marshalOInstrumentedApplicationDetails2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationDetails(ctx, field.Selections, res) + return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_instrumentedApplicationDetails(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, @@ -9702,14 +9133,18 @@ func (ec *executionContext) fieldContext_K8sActualSource_instrumentedApplication IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "containers": - return ec.fieldContext_InstrumentedApplicationDetails_containers(ctx, field) - case "conditions": - return ec.fieldContext_InstrumentedApplicationDetails_conditions(ctx, field) - case "instrumentationOptions": - return ec.fieldContext_InstrumentedApplicationDetails_instrumentationOptions(ctx, field) + case "status": + return ec.fieldContext_Condition_status(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) + case "reason": + return ec.fieldContext_Condition_reason(ctx, field) + case "message": + return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type InstrumentedApplicationDetails", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) }, } return fc, nil @@ -13445,12 +12880,10 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context switch field.Name { case "computePlatformType": return ec.fieldContext_ComputePlatform_computePlatformType(ctx, field) - case "k8sActualNamespace": - return ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) case "k8sActualNamespaces": return ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) - case "k8sActualSource": - return ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) + case "k8sActualNamespace": + return ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) case "k8sActualSources": return ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) case "destinations": @@ -18554,53 +17987,20 @@ func (ec *executionContext) _ClusterInfo(ctx context.Context, sel ast.SelectionS var computePlatformImplementors = []string{"ComputePlatform"} -func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.SelectionSet, obj *model.ComputePlatform) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, computePlatformImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("ComputePlatform") - case "computePlatformType": - out.Values[i] = ec._ComputePlatform_computePlatformType(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "k8sActualNamespace": - field := field - - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._ComputePlatform_k8sActualNamespace(ctx, field, obj) - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } +func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.SelectionSet, obj *model.ComputePlatform) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, computePlatformImplementors) - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ComputePlatform") + case "computePlatformType": + out.Values[i] = ec._ComputePlatform_computePlatformType(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } case "k8sActualNamespaces": field := field @@ -18637,7 +18037,7 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "k8sActualSource": + case "k8sActualNamespace": field := field innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { @@ -18646,7 +18046,7 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ComputePlatform_k8sActualSource(ctx, field, obj) + res = ec._ComputePlatform_k8sActualNamespace(ctx, field, obj) return res } @@ -20031,50 +19431,6 @@ func (ec *executionContext) _InstrumentationLabelsAnalyze(ctx context.Context, s return out } -var instrumentationLibraryImplementors = []string{"InstrumentationLibrary"} - -func (ec *executionContext) _InstrumentationLibrary(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLibrary) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationLibraryImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationLibrary") - case "libraryName": - out.Values[i] = ec._InstrumentationLibrary_libraryName(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "options": - out.Values[i] = ec._InstrumentationLibrary_options(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var instrumentationLibraryGlobalIdImplementors = []string{"InstrumentationLibraryGlobalId"} func (ec *executionContext) _InstrumentationLibraryGlobalId(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLibraryGlobalID) graphql.Marshaler { @@ -20118,50 +19474,6 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId(ctx context.Context, return out } -var instrumentationOptionImplementors = []string{"InstrumentationOption"} - -func (ec *executionContext) _InstrumentationOption(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationOption) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationOptionImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationOption") - case "optionKey": - out.Values[i] = ec._InstrumentationOption_optionKey(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "spanKind": - out.Values[i] = ec._InstrumentationOption_spanKind(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var instrumentationRuleImplementors = []string{"InstrumentationRule"} func (ec *executionContext) _InstrumentationRule(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationRule) graphql.Marshaler { @@ -20259,49 +19571,6 @@ func (ec *executionContext) _InstrumentedApplicationAnalyze(ctx context.Context, return out } -var instrumentedApplicationDetailsImplementors = []string{"InstrumentedApplicationDetails"} - -func (ec *executionContext) _InstrumentedApplicationDetails(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentedApplicationDetails) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentedApplicationDetailsImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentedApplicationDetails") - case "containers": - out.Values[i] = ec._InstrumentedApplicationDetails_containers(ctx, field, obj) - case "conditions": - out.Values[i] = ec._InstrumentedApplicationDetails_conditions(ctx, field, obj) - case "instrumentationOptions": - out.Values[i] = ec._InstrumentedApplicationDetails_instrumentationOptions(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var k8sActualNamespaceImplementors = []string{"K8sActualNamespace"} func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.SelectionSet, obj *model.K8sActualNamespace) graphql.Marshaler { @@ -20395,34 +19664,26 @@ func (ec *executionContext) _K8sActualSource(ctx context.Context, sel ast.Select if out.Values[i] == graphql.Null { out.Invalids++ } - case "kind": - out.Values[i] = ec._K8sActualSource_kind(ctx, field, obj) + case "name": + out.Values[i] = ec._K8sActualSource_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "name": - out.Values[i] = ec._K8sActualSource_name(ctx, field, obj) + case "kind": + out.Values[i] = ec._K8sActualSource_kind(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "serviceName": - out.Values[i] = ec._K8sActualSource_serviceName(ctx, field, obj) case "numberOfInstances": out.Values[i] = ec._K8sActualSource_numberOfInstances(ctx, field, obj) + case "serviceName": + out.Values[i] = ec._K8sActualSource_serviceName(ctx, field, obj) case "reportedName": out.Values[i] = ec._K8sActualSource_reportedName(ctx, field, obj) - case "autoInstrumented": - out.Values[i] = ec._K8sActualSource_autoInstrumented(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "autoInstrumentedDecision": - out.Values[i] = ec._K8sActualSource_autoInstrumentedDecision(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "instrumentedApplicationDetails": - out.Values[i] = ec._K8sActualSource_instrumentedApplicationDetails(ctx, field, obj) + case "containers": + out.Values[i] = ec._K8sActualSource_containers(ctx, field, obj) + case "conditions": + out.Values[i] = ec._K8sActualSource_conditions(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -23001,60 +22262,6 @@ func (ec *executionContext) marshalNInstrumentationLabelsAnalyze2ᚖgithubᚗcom return ec._InstrumentationLabelsAnalyze(ctx, sel, v) } -func (ec *executionContext) marshalNInstrumentationLibrary2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.InstrumentationLibrary) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNInstrumentationLibrary2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibrary(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNInstrumentationLibrary2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibrary(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationLibrary) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentationLibrary(ctx, sel, v) -} - func (ec *executionContext) marshalNInstrumentationLibraryGlobalId2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryGlobalID(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationLibraryGlobalID) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23070,60 +22277,6 @@ func (ec *executionContext) unmarshalNInstrumentationLibraryGlobalIdInput2ᚖgit return &res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNInstrumentationOption2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOptionᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.InstrumentationOption) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNInstrumentationOption2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOption(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNInstrumentationOption2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOption(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationOption) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentationOption(ctx, sel, v) -} - func (ec *executionContext) marshalNInstrumentationRule2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationRule(ctx context.Context, sel ast.SelectionSet, v model.InstrumentationRule) graphql.Marshaler { return ec._InstrumentationRule(ctx, sel, &v) } @@ -23757,16 +22910,6 @@ func (ec *executionContext) marshalNSourceContainerRuntimeDetails2ᚖgithubᚗco return ec._SourceContainerRuntimeDetails(ctx, sel, v) } -func (ec *executionContext) unmarshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, v interface{}) (model.SpanKind, error) { - var res model.SpanKind - err := res.UnmarshalGQL(v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, sel ast.SelectionSet, v model.SpanKind) graphql.Marshaler { - return v -} - func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -24316,13 +23459,6 @@ func (ec *executionContext) unmarshalOInstrumentationLibraryGlobalIdInput2ᚕᚖ return res, nil } -func (ec *executionContext) marshalOInstrumentedApplicationDetails2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationDetails(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentedApplicationDetails) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._InstrumentedApplicationDetails(ctx, sel, v) -} - func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v interface{}) (*int, error) { if v == nil { return nil, nil diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 880c47690..46078a80b 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -74,9 +74,8 @@ type ClusterInfo struct { type ComputePlatform struct { ComputePlatformType ComputePlatformType `json:"computePlatformType"` - K8sActualNamespace *K8sActualNamespace `json:"k8sActualNamespace,omitempty"` K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` - K8sActualSource *K8sActualSource `json:"k8sActualSource,omitempty"` + K8sActualNamespace *K8sActualNamespace `json:"k8sActualNamespace,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` Destinations []*Destination `json:"destinations"` Actions []*PipelineAction `json:"actions"` @@ -264,11 +263,6 @@ type InstrumentationLabelsAnalyze struct { InstrumentedText *EntityProperty `json:"instrumentedText,omitempty"` } -type InstrumentationLibrary struct { - LibraryName string `json:"libraryName"` - Options []*InstrumentationOption `json:"options"` -} - type InstrumentationLibraryGlobalID struct { Name string `json:"name"` SpanKind *SpanKind `json:"spanKind,omitempty"` @@ -281,11 +275,6 @@ type InstrumentationLibraryGlobalIDInput struct { Language *ProgrammingLanguage `json:"language,omitempty"` } -type InstrumentationOption struct { - OptionKey string `json:"optionKey"` - SpanKind SpanKind `json:"spanKind"` -} - type InstrumentationRule struct { RuleID string `json:"ruleId"` RuleName *string `json:"ruleName,omitempty"` @@ -311,12 +300,6 @@ type InstrumentedApplicationAnalyze struct { Containers []*ContainerRuntimeInfoAnalyze `json:"containers"` } -type InstrumentedApplicationDetails struct { - Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` - Conditions []*Condition `json:"conditions,omitempty"` - InstrumentationOptions []*InstrumentationLibrary `json:"instrumentationOptions"` -} - type K8sActualNamespace struct { Name string `json:"name"` InstrumentationLabelEnabled *bool `json:"instrumentationLabelEnabled,omitempty"` @@ -324,15 +307,14 @@ type K8sActualNamespace struct { } type K8sActualSource struct { - Namespace string `json:"namespace"` - Kind K8sResourceKind `json:"kind"` - Name string `json:"name"` - ServiceName *string `json:"serviceName,omitempty"` - NumberOfInstances *int `json:"numberOfInstances,omitempty"` - ReportedName *string `json:"reportedName,omitempty"` - AutoInstrumented bool `json:"autoInstrumented"` - AutoInstrumentedDecision string `json:"autoInstrumentedDecision"` - InstrumentedApplicationDetails *InstrumentedApplicationDetails `json:"instrumentedApplicationDetails,omitempty"` + Namespace string `json:"namespace"` + Name string `json:"name"` + Kind K8sResourceKind `json:"kind"` + NumberOfInstances *int `json:"numberOfInstances,omitempty"` + ServiceName *string `json:"serviceName,omitempty"` + ReportedName *string `json:"reportedName,omitempty"` + Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` + Conditions []*Condition `json:"conditions,omitempty"` } type K8sDesiredNamespaceInput struct { diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 808c9ca2c..49f4ba8d8 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -51,22 +51,6 @@ type SourceContainerRuntimeDetails { otherAgent: String } -type InstrumentationOption { - optionKey: String! - spanKind: SpanKind! -} - -type InstrumentationLibrary { - libraryName: String! - options: [InstrumentationOption!]! -} - -type InstrumentedApplicationDetails { - containers: [SourceContainerRuntimeDetails!] - conditions: [Condition!] - instrumentationOptions: [InstrumentationLibrary!]! -} - type Condition { status: ConditionStatus! type: String! @@ -91,14 +75,13 @@ input K8sNamespaceId { type K8sActualSource { namespace: String! - kind: K8sResourceKind! name: String! - serviceName: String + kind: K8sResourceKind! numberOfInstances: Int + serviceName: String reportedName: String - autoInstrumented: Boolean! - autoInstrumentedDecision: String! - instrumentedApplicationDetails: InstrumentedApplicationDetails + containers: [SourceContainerRuntimeDetails!] + conditions: [Condition!] } input K8sDesiredSourceInput { @@ -203,9 +186,8 @@ input MessagingPayloadCollectionInput { type ComputePlatform { computePlatformType: ComputePlatformType! - k8sActualNamespace(name: String!): K8sActualNamespace k8sActualNamespaces: [K8sActualNamespace]! - k8sActualSource(name: String, namespace: String, kind: String): K8sActualSource + k8sActualNamespace(name: String!): K8sActualNamespace k8sActualSources: [K8sActualSource]! destinations: [Destination!]! actions: [PipelineAction!]! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 72ecf8166..be2134cee 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -20,11 +20,33 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) +// K8sActualNamespaces is the resolver for the k8sActualNamespaces field. +func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) { + namespacesResponse := services.GetK8SNamespaces(ctx) + + K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) + for i, namespace := range namespacesResponse.Namespaces { + + namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) + + K8sActualNamespaces[i] = &model.K8sActualNamespace{ + Name: namespace.Name, + InstrumentationLabelEnabled: nsInstrumented, + } + } + + return K8sActualNamespaces, nil +} + // K8sActualNamespace is the resolver for the k8sActualNamespace field. func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) { namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, name, nil) @@ -52,59 +74,20 @@ func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *m }, nil } -// K8sActualNamespaces is the resolver for the k8sActualNamespaces field. -func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) { - namespacesResponse := services.GetK8SNamespaces(ctx) - - K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) - for i, namespace := range namespacesResponse.Namespaces { - - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) - - K8sActualNamespaces[i] = &model.K8sActualNamespace{ - Name: namespace.Name, - InstrumentationLabelEnabled: nsInstrumented, - } - } - - return K8sActualNamespaces, nil -} - -// K8sActualSource is the resolver for the k8sActualSource field. -func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) { - return nil, nil -} - // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { // Initialize an empty list of K8sActualSource var actualSources []*model.K8sActualSource - // TODO: remove "InstrumentedApplications" once we're ready to move over to "InstrumentationConfigs" combined with "Source CRDs" - instrumentedApplications, err := kube.DefaultClient.OdigosClient.InstrumentedApplications("").List(ctx, metav1.ListOptions{}) + instrumentationConfigs, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs("").List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } // Convert each instrumented application to the K8sActualSource type - for _, app := range instrumentedApplications.Items { - actualSource := instrumentedApplicationToActualSource(app) - services.AddHealthyInstrumentationInstancesCondition(ctx, &app, actualSource) - owner, _ := services.GetWorkload(ctx, actualSource.Namespace, string(actualSource.Kind), actualSource.Name) - if owner == nil { - continue - } - ownerAnnotations := owner.GetAnnotations() - var reportedName string - if ownerAnnotations != nil { - reportedName = ownerAnnotations[consts.OdigosReportedNameAnnotation] - } - actualSource.ReportedName = &reportedName + for _, instruConfig := range instrumentationConfigs.Items { + actualSource := instrumentationConfigToActualSource(instruConfig) + // services.AddHealthyInstrumentationInstancesCondition(ctx, &instruConfig, actualSource) actualSources = append(actualSources, actualSource) } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 9c548b473..51e7b483d 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -86,9 +86,9 @@ func GetWorkload(c context.Context, ns string, kind string, name string) (metav1 } } -func AddHealthyInstrumentationInstancesCondition(ctx context.Context, app *v1alpha1.InstrumentedApplication, source *model.K8sActualSource) error { - labelSelector := fmt.Sprintf("%s=%s", consts.InstrumentedAppNameLabel, app.Name) - instancesList, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(app.Namespace).List(ctx, metav1.ListOptions{ +func AddHealthyInstrumentationInstancesCondition(ctx context.Context, instruConfig *v1alpha1.InstrumentationConfig, source *model.K8sActualSource) error { + labelSelector := fmt.Sprintf("%s=%s", consts.InstrumentedAppNameLabel, instruConfig.Name) + instancesList, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(instruConfig.Namespace).List(ctx, metav1.ListOptions{ LabelSelector: labelSelector, }) @@ -120,7 +120,7 @@ func AddHealthyInstrumentationInstancesCondition(ctx context.Context, app *v1alp message := fmt.Sprintf("%d/%d instances are healthy", healthyInstances, totalInstances) lastTransitionTime := Metav1TimeToString(latestStatusTime) - source.InstrumentedApplicationDetails.Conditions = append(source.InstrumentedApplicationDetails.Conditions, &model.Condition{ + source.Conditions = append(source.Conditions, &model.Condition{ Type: "HealthyInstrumentationInstances", Status: status, LastTransitionTime: &lastTransitionTime, @@ -178,19 +178,19 @@ func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumenta var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().Deployments(namespace.Name).List, ctx, metav1.ListOptions{}, func(deps *appsv1.DeploymentList) error { for _, dep := range deps.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(dep.GetLabels(), string(WorkloadKindDeployment), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } + // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(dep.GetLabels(), string(WorkloadKindDeployment), namespace.GetLabels()) + // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { + // continue + // } numberOfInstances := int(dep.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ - Namespace: dep.Namespace, - Name: dep.Name, - Kind: k8sKindToGql(string(WorkloadKindDeployment)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: dep.Namespace, + Name: dep.Name, + Kind: k8sKindToGql(string(WorkloadKindDeployment)), + NumberOfInstances: &numberOfInstances, + // AutoInstrumented: autoInstrumented, + // AutoInstrumentedDecision: decisionText, + // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -207,19 +207,19 @@ func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentat var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().DaemonSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(dss *appsv1.DaemonSetList) error { for _, ds := range dss.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ds.GetLabels(), string(WorkloadKindDaemonSet), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } + // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ds.GetLabels(), string(WorkloadKindDaemonSet), namespace.GetLabels()) + // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { + // continue + // } numberOfInstances := int(ds.Status.NumberReady) response = append(response, model.K8sActualSource{ - Namespace: ds.Namespace, - Name: ds.Name, - Kind: k8sKindToGql(string(WorkloadKindDaemonSet)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: ds.Namespace, + Name: ds.Name, + Kind: k8sKindToGql(string(WorkloadKindDaemonSet)), + NumberOfInstances: &numberOfInstances, + // AutoInstrumented: autoInstrumented, + // AutoInstrumentedDecision: decisionText, + // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -236,19 +236,19 @@ func getStatefulSets(ctx context.Context, namespace corev1.Namespace, instrument var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().StatefulSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(sss *appsv1.StatefulSetList) error { for _, ss := range sss.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ss.GetLabels(), string(WorkloadKindStatefulSet), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } + // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ss.GetLabels(), string(WorkloadKindStatefulSet), namespace.GetLabels()) + // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { + // continue + // } numberOfInstances := int(ss.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ - Namespace: ss.Namespace, - Name: ss.Name, - Kind: k8sKindToGql(string(WorkloadKindStatefulSet)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: ss.Namespace, + Name: ss.Name, + Kind: k8sKindToGql(string(WorkloadKindStatefulSet)), + NumberOfInstances: &numberOfInstances, + // AutoInstrumented: autoInstrumented, + // AutoInstrumentedDecision: decisionText, + // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil diff --git a/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx b/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx index 571fc3bcd..5c687db28 100644 --- a/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx +++ b/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx @@ -20,8 +20,8 @@ export const ErrorDropdown: React.FC = ({ title = 'Error Message', value, const options = useMemo(() => { const payload: DropdownOption[] = []; - sources.forEach(({ instrumentedApplicationDetails: { conditions } }) => { - conditions.forEach(({ type, status, message }) => { + sources.forEach(({ conditions }) => { + conditions.forEach(({ status, message }) => { if (status === BACKEND_BOOLEAN.FALSE && !payload.find((opt) => opt.id === message)) { payload.push({ id: message, value: message }); } diff --git a/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx b/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx index 31c53ac9a..92fbfc011 100644 --- a/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx +++ b/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx @@ -19,7 +19,7 @@ export const LanguageDropdown: React.FC = ({ title = 'Programming Languag const options = useMemo(() => { const payload: DropdownOption[] = []; - sources.forEach(({ instrumentedApplicationDetails: { containers } }) => { + sources.forEach(({ containers }) => { containers.forEach(({ language }) => { if (!payload.find((opt) => opt.id === language)) { payload.push({ id: language, value: language }); diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts index e72c56c6b..8d190d9c9 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts +++ b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts @@ -3,16 +3,18 @@ import type { K8sActualSource, WorkloadId } from '@/types'; const buildDrawerItem = (id: WorkloadId, formData: { reportedName: string }, drawerItem: K8sActualSource): K8sActualSource => { const { namespace, name, kind } = id; const { reportedName } = formData; - const { selected, numberOfInstances, instrumentedApplicationDetails } = drawerItem; + const { numberOfInstances, serviceName, conditions, containers, selected } = drawerItem; return { namespace, name, kind, + numberOfInstances, + serviceName, reportedName, + conditions, + containers, selected, - numberOfInstances, - instrumentedApplicationDetails, }; }; diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx index 750696632..4ea0eb2ac 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -79,12 +79,11 @@ export const SourceDrawer: React.FC = () => { const { item } = selectedItem as { item: K8sActualSource }; const hasPresenceOfOtherAgent = - item?.instrumentedApplicationDetails?.conditions?.some( - (condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent'), - ) || false; + item?.conditions?.some((condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent')) || + false; return ( - item?.instrumentedApplicationDetails?.containers?.map( + item?.containers?.map( (container) => ({ type: DataCardFieldTypes.SOURCE_CONTAINER, @@ -146,7 +145,7 @@ export const SourceDrawer: React.FC = () => { ) : ( - + { k8sActualSources = k8sActualSources.filter((source) => !!filters.types.find((type) => type.id === source.kind)); } if (!!filters.onlyErrors) { - k8sActualSources = k8sActualSources.filter((source) => !!source.instrumentedApplicationDetails?.conditions?.find((cond) => cond.status === BACKEND_BOOLEAN.FALSE)); + k8sActualSources = k8sActualSources.filter((source) => !!source.conditions?.find((cond) => cond.status === BACKEND_BOOLEAN.FALSE)); } if (!!filters.errors.length) { - k8sActualSources = k8sActualSources.filter((source) => !!filters.errors.find((error) => !!source.instrumentedApplicationDetails?.conditions?.find((cond) => cond.message === error.id))); + k8sActualSources = k8sActualSources.filter((source) => !!filters.errors.find((error) => !!source.conditions?.find((cond) => cond.message === error.id))); } if (!!filters.languages.length) { - k8sActualSources = k8sActualSources.filter( - (source) => !!filters.languages.find((language) => !!source.instrumentedApplicationDetails?.containers?.find((cont) => cont.language === language.id)), - ); + k8sActualSources = k8sActualSources.filter((source) => !!filters.languages.find((language) => !!source.containers?.find((cont) => cont.language === language.id))); } if (!!filters.monitors.length) { destinations = destinations.filter((destination) => !!filters.monitors.find((metric) => destination.exportedSignals[metric.id])); diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index e4c65f2d4..9808d3545 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -12,8 +12,8 @@ interface ComputePlatformData { id: string; name: string; computePlatformType: string; - k8sActualNamespace?: K8sActualNamespace; k8sActualNamespaces: K8sActualNamespace[]; + k8sActualNamespace: K8sActualNamespace; k8sActualSources: K8sActualSource[]; destinations: ActualDestination[]; actions: ActionData[]; diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index f2ce90015..937fdd84c 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -9,16 +9,15 @@ export type SourceContainer = { }; export type K8sActualSource = { + namespace: string; name: string; kind: string; - namespace: string; - reportedName: string; numberOfInstances: number; - selected?: boolean; - instrumentedApplicationDetails: { - containers: Array; - conditions: Array; - }; + // serviceName: string; + reportedName: string; + containers: Array; + conditions: Array; + selected?: boolean; // not from backend }; export type WorkloadId = { diff --git a/frontend/webapp/utils/constants/programming-languages.ts b/frontend/webapp/utils/constants/programming-languages.ts index 994a90879..33b09af38 100644 --- a/frontend/webapp/utils/constants/programming-languages.ts +++ b/frontend/webapp/utils/constants/programming-languages.ts @@ -19,31 +19,24 @@ export enum WORKLOAD_PROGRAMMING_LANGUAGES { } export const getMainContainerLanguage = (source: K8sActualSource): WORKLOAD_PROGRAMMING_LANGUAGES => { - const ia = source?.instrumentedApplicationDetails; + const { numberOfInstances, containers } = source; - if (!ia) { - if (source?.numberOfInstances > 0) { + if (!containers) { + if (numberOfInstances > 0) { return WORKLOAD_PROGRAMMING_LANGUAGES.PROCESSING; } else { return WORKLOAD_PROGRAMMING_LANGUAGES.NO_RUNNING_PODS; } } - const containers = ia.containers; - if (!containers) { - return WORKLOAD_PROGRAMMING_LANGUAGES.PROCESSING; - } - // we will filter out the ignored languages as we don't want to account them in the main language - const noneIgnoredLanguages = containers.filter((container) => container.language !== 'ignored'); - if (noneIgnoredLanguages.length === 0) { - return WORKLOAD_PROGRAMMING_LANGUAGES.NO_CONTAINERS; - } + const noneIgnoredLanguages = containers.filter((container) => container.language !== WORKLOAD_PROGRAMMING_LANGUAGES.IGNORED); + if (!noneIgnoredLanguages.length) return WORKLOAD_PROGRAMMING_LANGUAGES.NO_CONTAINERS; // find the first container with valid language - const mainContainer = noneIgnoredLanguages.find((container) => container.language !== 'unknown'); - if (!mainContainer) { - return WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN; // no valid language found, return the first one - } - return mainContainer.language as WORKLOAD_PROGRAMMING_LANGUAGES; + const mainContainer = noneIgnoredLanguages.find((container) => container.language !== WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN); + // no valid language found, return the first one + if (!mainContainer) return WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN; + + return mainContainer.language; }; diff --git a/frontend/webapp/utils/functions/strings/get-entity-label/index.ts b/frontend/webapp/utils/functions/strings/get-entity-label/index.ts index 930bad81e..ff6c0cae5 100644 --- a/frontend/webapp/utils/functions/strings/get-entity-label/index.ts +++ b/frontend/webapp/utils/functions/strings/get-entity-label/index.ts @@ -39,7 +39,7 @@ export const getEntityLabel = ( break; } - if (extended) return type + (name ? ` (${name})` : ''); + if (extended) return type + (name && name !== type ? ` (${name})` : ''); else if (prioritizeDisplayName) return name || type; else return type; }; diff --git a/frontend/webapp/utils/functions/strings/get-health-status/index.ts b/frontend/webapp/utils/functions/strings/get-health-status/index.ts index 247fc09d1..f5de8bcf7 100644 --- a/frontend/webapp/utils/functions/strings/get-health-status/index.ts +++ b/frontend/webapp/utils/functions/strings/get-health-status/index.ts @@ -2,7 +2,7 @@ import { BACKEND_BOOLEAN } from '@/utils'; import { STATUSES, type ActualDestination, type K8sActualSource } from '@/types'; export const getHealthStatus = (item: K8sActualSource | ActualDestination) => { - const conditions = (item as K8sActualSource)?.instrumentedApplicationDetails?.conditions || (item as ActualDestination)?.conditions || []; + const conditions = item?.conditions || []; const isUnhealthy = !conditions.length || !!conditions.find(({ status }) => status === BACKEND_BOOLEAN.FALSE); return isUnhealthy ? STATUSES.UNHEALTHY : STATUSES.HEALTHY; From 5c9d44c24040461219a1aa567d5099cc7163b637 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sat, 28 Dec 2024 19:02:41 +0200 Subject: [PATCH 138/259] Remove 'sources' resource from UIClusterRole permissions to refine access control --- cli/cmd/resources/ui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index 6c50853f7..d0cea23a7 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -242,7 +242,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { }, { // Needed to watch instrumented applications APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances", "sources"}, + Resources: []string{"instrumentedapplications", "instrumentationinstances"}, Verbs: []string{"watch"}, }, }, From b698f57d8df478623b0cb766056cfdba671f220e Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 09:35:06 +0200 Subject: [PATCH 139/259] fix: build error --- .../main/sources/source-drawer-container/build-drawer-item.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts index 8d190d9c9..1ed993fec 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts +++ b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts @@ -3,14 +3,13 @@ import type { K8sActualSource, WorkloadId } from '@/types'; const buildDrawerItem = (id: WorkloadId, formData: { reportedName: string }, drawerItem: K8sActualSource): K8sActualSource => { const { namespace, name, kind } = id; const { reportedName } = formData; - const { numberOfInstances, serviceName, conditions, containers, selected } = drawerItem; + const { numberOfInstances, conditions, containers, selected } = drawerItem; return { namespace, name, kind, numberOfInstances, - serviceName, reportedName, conditions, containers, From 0293867534d8bb236a8e546af81a925a759a65a1 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 10:31:04 +0200 Subject: [PATCH 140/259] fix: code review --- common/consts/consts.go | 5 +- frontend/graph/conversions.go | 1 - frontend/graph/generated.go | 55 ------------------- frontend/graph/model/models_gen.go | 1 - frontend/graph/schema.graphqls | 1 - frontend/graph/schema.resolvers.go | 2 +- frontend/services/namespaces.go | 2 +- frontend/services/sources.go | 41 ++++---------- .../graphql/queries/compute-platform.ts | 1 - frontend/webapp/types/sources.ts | 1 - k8sutils/pkg/workload/workload.go | 39 ------------- 11 files changed, 16 insertions(+), 133 deletions(-) diff --git a/common/consts/consts.go b/common/consts/consts.go index e84a8c08d..f053c7b61 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -15,6 +15,9 @@ const ( OdigosInstrumentationLabel = "odigos-instrumentation" InstrumentationEnabled = "enabled" InstrumentationDisabled = "disabled" + OdigosNamespaceAnnotation = "odigos.io/workload-namespace" + OdigosWorkloadKindAnnotation = "odigos.io/workload-kind" + OdigosWorkloadNameAnnotation = "odigos.io/workload-name" OdigosReportedNameAnnotation = "odigos.io/reported-name" // GatewayMaxConnectionAge and GatewayMaxConnectionAgeGrace are the default values for the gateway collector. @@ -32,7 +35,7 @@ const ( ) var ( - PodsNotFoundErr = errors.New("could not find a ready pod") + ErrorPodsNotFound = errors.New("could not find a ready pod") ) var ( diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index d4f093047..d7a06642e 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -77,7 +77,6 @@ func instrumentationConfigToActualSource(instruConfig v1alpha1.InstrumentationCo Kind: k8sKindToGql(instruConfig.OwnerReferences[0].Kind), Name: instruConfig.OwnerReferences[0].Name, NumberOfInstances: nil, - ServiceName: &instruConfig.Name, ReportedName: &instruConfig.Spec.ServiceName, Containers: containers, Conditions: conditions, diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index 5da74e178..22cd3662b 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -275,7 +275,6 @@ type ComplexityRoot struct { Namespace func(childComplexity int) int NumberOfInstances func(childComplexity int) int ReportedName func(childComplexity int) int - ServiceName func(childComplexity int) int } LatencySamplerAction struct { @@ -1458,13 +1457,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualSource.ReportedName(childComplexity), true - case "K8sActualSource.serviceName": - if e.complexity.K8sActualSource.ServiceName == nil { - break - } - - return e.complexity.K8sActualSource.ServiceName(childComplexity), true - case "LatencySamplerAction.details": if e.complexity.LatencySamplerAction.Details == nil { break @@ -4031,8 +4023,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) case "containers": @@ -8765,8 +8755,6 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) case "containers": @@ -8964,47 +8952,6 @@ func (ec *executionContext) fieldContext_K8sActualSource_numberOfInstances(_ con return fc, nil } -func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_serviceName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ServiceName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*string) - fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_K8sActualSource_serviceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "K8sActualSource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) if err != nil { @@ -19676,8 +19623,6 @@ func (ec *executionContext) _K8sActualSource(ctx context.Context, sel ast.Select } case "numberOfInstances": out.Values[i] = ec._K8sActualSource_numberOfInstances(ctx, field, obj) - case "serviceName": - out.Values[i] = ec._K8sActualSource_serviceName(ctx, field, obj) case "reportedName": out.Values[i] = ec._K8sActualSource_reportedName(ctx, field, obj) case "containers": diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 46078a80b..6e4b07780 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -311,7 +311,6 @@ type K8sActualSource struct { Name string `json:"name"` Kind K8sResourceKind `json:"kind"` NumberOfInstances *int `json:"numberOfInstances,omitempty"` - ServiceName *string `json:"serviceName,omitempty"` ReportedName *string `json:"reportedName,omitempty"` Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` Conditions []*Condition `json:"conditions,omitempty"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 49f4ba8d8..f2fb30fa6 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -78,7 +78,6 @@ type K8sActualSource { name: String! kind: K8sResourceKind! numberOfInstances: Int - serviceName: String reportedName: String containers: [SourceContainerRuntimeDetails!] conditions: [Condition!] diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index be2134cee..c18266f24 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -87,7 +87,7 @@ func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *mod // Convert each instrumented application to the K8sActualSource type for _, instruConfig := range instrumentationConfigs.Items { actualSource := instrumentationConfigToActualSource(instruConfig) - // services.AddHealthyInstrumentationInstancesCondition(ctx, &instruConfig, actualSource) + services.AddHealthyInstrumentationInstancesCondition(ctx, &instruConfig, actualSource) actualSources = append(actualSources, actualSource) } diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index a4fb57780..7b4e34a62 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -179,7 +179,7 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo for _, workload := range workloads { currWorkload := workload g.Go(func() error { - // Only label selected sources, ignore the rest + // Only instrument/uninstrument selected workloads, ignore the rest if currWorkload.Selected != nil { return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 51e7b483d..81bcc7f60 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -178,19 +178,12 @@ func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumenta var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().Deployments(namespace.Name).List, ctx, metav1.ListOptions{}, func(deps *appsv1.DeploymentList) error { for _, dep := range deps.Items { - // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(dep.GetLabels(), string(WorkloadKindDeployment), namespace.GetLabels()) - // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - // continue - // } numberOfInstances := int(dep.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ Namespace: dep.Namespace, Name: dep.Name, Kind: k8sKindToGql(string(WorkloadKindDeployment)), NumberOfInstances: &numberOfInstances, - // AutoInstrumented: autoInstrumented, - // AutoInstrumentedDecision: decisionText, - // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -207,19 +200,12 @@ func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentat var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().DaemonSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(dss *appsv1.DaemonSetList) error { for _, ds := range dss.Items { - // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ds.GetLabels(), string(WorkloadKindDaemonSet), namespace.GetLabels()) - // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - // continue - // } numberOfInstances := int(ds.Status.NumberReady) response = append(response, model.K8sActualSource{ Namespace: ds.Namespace, Name: ds.Name, Kind: k8sKindToGql(string(WorkloadKindDaemonSet)), NumberOfInstances: &numberOfInstances, - // AutoInstrumented: autoInstrumented, - // AutoInstrumentedDecision: decisionText, - // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -236,19 +222,12 @@ func getStatefulSets(ctx context.Context, namespace corev1.Namespace, instrument var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().StatefulSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(sss *appsv1.StatefulSetList) error { for _, ss := range sss.Items { - // _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ss.GetLabels(), string(WorkloadKindStatefulSet), namespace.GetLabels()) - // if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - // continue - // } numberOfInstances := int(ss.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ Namespace: ss.Namespace, Name: ss.Name, Kind: k8sKindToGql(string(WorkloadKindStatefulSet)), NumberOfInstances: &numberOfInstances, - // AutoInstrumented: autoInstrumented, - // AutoInstrumentedDecision: decisionText, - // InstrumentedApplicationDetails: nil, // TODO: fill this }) } return nil @@ -320,26 +299,26 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s } func getSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { - sourceList, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ - "odigos.io/workload-namespace": nsName, - "odigos.io/workload-name": workloadName, - "odigos.io/workload-kind": string(workloadKind), + source, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ + consts.OdigosNamespaceAnnotation: nsName, + consts.OdigosWorkloadNameAnnotation: workloadName, + consts.OdigosWorkloadKindAnnotation: string(workloadKind), }).String()}) if err != nil { return nil, err } - if len(sourceList.Items) == 0 { + if len(source.Items) == 0 { return nil, errors.New("\"" + workloadName + "\"" + " source not found") } - if len(sourceList.Items) > 1 { - return nil, errors.New("\"" + workloadName + "\"" + " expected to get 1 source got " + string(len(sourceList.Items))) + if len(source.Items) > 1 { + return nil, errors.New("\"" + workloadName + "\"" + " expected to get 1 source got " + string(len(source.Items))) } - crdName := sourceList.Items[0].Name - source, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) + crdName := source.Items[0].Name + crd, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) - return source, err + return crd, err } func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index f8c7b0dd8..afe9ca761 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -11,7 +11,6 @@ export const GET_COMPUTE_PLATFORM = gql` name kind numberOfInstances - # serviceName reportedName containers { containerName diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index 937fdd84c..62c207116 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -13,7 +13,6 @@ export type K8sActualSource = { name: string; kind: string; numberOfInstances: number; - // serviceName: string; reportedName: string; containers: Array; conditions: Array; diff --git a/k8sutils/pkg/workload/workload.go b/k8sutils/pkg/workload/workload.go index d8b6369a8..581540cd0 100644 --- a/k8sutils/pkg/workload/workload.go +++ b/k8sutils/pkg/workload/workload.go @@ -120,45 +120,6 @@ func GetInstrumentationLabelValue(labels map[string]string) *bool { return nil } -func GetInstrumentationLabelTexts(workloadLabels map[string]string, workloadKind string, nsLabels map[string]string) (workloadText, nsText, decisionText string, sourceInstrumented bool) { - workloadLabel, workloadFound := workloadLabels[consts.OdigosInstrumentationLabel] - nsLabel, nsFound := nsLabels[consts.OdigosInstrumentationLabel] - - if workloadFound { - workloadText = consts.OdigosInstrumentationLabel + "=" + workloadLabel - } else { - workloadText = consts.OdigosInstrumentationLabel + " label not set" - } - - if nsFound { - nsText = consts.OdigosInstrumentationLabel + "=" + nsLabel - } else { - nsText = consts.OdigosInstrumentationLabel + " label not set" - } - - if workloadFound { - sourceInstrumented = workloadLabel == consts.InstrumentationEnabled - if sourceInstrumented { - decisionText = "Workload is instrumented because the " + workloadKind + " contains the label '" + consts.OdigosInstrumentationLabel + "=" + workloadLabel + "'" - } else { - decisionText = "Workload is NOT instrumented because the " + workloadKind + " contains the label '" + consts.OdigosInstrumentationLabel + "=" + workloadLabel + "'" - } - } else { - sourceInstrumented = nsLabel == consts.InstrumentationEnabled - if sourceInstrumented { - decisionText = "Workload is instrumented because the " + workloadKind + " is not labeled, and the namespace is labeled with '" + consts.OdigosInstrumentationLabel + "=" + nsLabel + "'" - } else { - if nsFound { - decisionText = "Workload is NOT instrumented because the " + workloadKind + " is not labeled, and the namespace is labeled with '" + consts.OdigosInstrumentationLabel + "=" + nsLabel + "'" - } else { - decisionText = "Workload is NOT instrumented because neither the workload nor the namespace has the '" + consts.OdigosInstrumentationLabel + "' label set" - } - } - } - - return -} - func GetWorkloadObject(ctx context.Context, objectKey client.ObjectKey, kind WorkloadKind, kubeClient client.Client) (metav1.Object, error) { switch kind { case WorkloadKindDeployment: From d796c4fff4a9995d977ac919425b132ad124f41e Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 17:45:22 +0200 Subject: [PATCH 141/259] feat: add more status to SSE --- frontend/endpoints/sse/sse.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/endpoints/sse/sse.go b/frontend/endpoints/sse/sse.go index afffa9115..4215a8a49 100644 --- a/frontend/endpoints/sse/sse.go +++ b/frontend/endpoints/sse/sse.go @@ -11,22 +11,25 @@ import ( type MessageType string const ( - MessageTypeSuccess MessageType = "success" + MessageTypeWarning MessageType = "warning" MessageTypeError MessageType = "error" + MessageTypeSuccess MessageType = "success" + MessageTypeInfo MessageType = "info" + MessageTypeDefault MessageType = "default" ) type MessageEvent string const ( - MessageEventDeleted MessageEvent = "Deleted" - MessageEventModified MessageEvent = "Modified" MessageEventAdded MessageEvent = "Added" + MessageEventDeleted MessageEvent = "Deleted" + MessageEventModified MessageEvent = "Modified" ) type SSEMessage struct { Type MessageType `json:"type"` - Data string `json:"data"` Event MessageEvent `json:"event"` + Data string `json:"data"` Target string `json:"target"` CRDType string `json:"crdType"` } From e230cbc4b891455b5a47d980d53aed8cb42e169b Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 17:46:18 +0200 Subject: [PATCH 142/259] feat: replace instru. apps watcher with istru. configs watcher --- cli/cmd/resources/ui.go | 4 +- ...r.go => instrumentation_config_watcher.go} | 42 ++++++++++--------- frontend/main.go | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) rename frontend/kube/watchers/{instrumented_application_watcher.go => instrumentation_config_watcher.go} (54%) diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index d0cea23a7..5d0fbdab6 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -232,7 +232,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { }, { // Needed to read Odigos entities APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances", "instrumentationconfigs"}, + Resources: []string{"instrumentationinstances", "instrumentationconfigs"}, Verbs: []string{"get", "list"}, }, { // Needed to instrument / uninstrument applications @@ -242,7 +242,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { }, { // Needed to watch instrumented applications APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances"}, + Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, Verbs: []string{"watch"}, }, }, diff --git a/frontend/kube/watchers/instrumented_application_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go similarity index 54% rename from frontend/kube/watchers/instrumented_application_watcher.go rename to frontend/kube/watchers/instrumentation_config_watcher.go index b17271289..fa0e3e74f 100644 --- a/frontend/kube/watchers/instrumented_application_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -15,11 +15,11 @@ import ( var addedEventBatcher *EventBatcher var deletedEventBatcher *EventBatcher -func StartInstrumentedApplicationWatcher(ctx context.Context, namespace string) error { +func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { addedEventBatcher = NewEventBatcher( EventBatcherConfig{ Event: sse.MessageEventAdded, - CRDType: "InstrumentedApplication", + CRDType: "InstrumentationConfigs", SuccessBatchMessageFunc: func(count int, crdType string) string { return fmt.Sprintf("successfully added %d sources", count) }, @@ -32,7 +32,7 @@ func StartInstrumentedApplicationWatcher(ctx context.Context, namespace string) deletedEventBatcher = NewEventBatcher( EventBatcherConfig{ Event: sse.MessageEventDeleted, - CRDType: "InstrumentedApplication", + CRDType: "InstrumentationConfigs", SuccessBatchMessageFunc: func(count int, crdType string) string { return fmt.Sprintf("successfully deleted %d sources", count) }, @@ -42,16 +42,16 @@ func StartInstrumentedApplicationWatcher(ctx context.Context, namespace string) }, ) - watcher, err := kube.DefaultClient.OdigosClient.InstrumentedApplications(namespace).Watch(context.Background(), metav1.ListOptions{}) + watcher, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { return fmt.Errorf("error creating watcher: %v", err) } - go handleInstrumentedApplicationWatchEvents(ctx, watcher) + go handleInstrumentationConfigWatchEvents(ctx, watcher) return nil } -func handleInstrumentedApplicationWatchEvents(ctx context.Context, watcher watch.Interface) { +func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() defer addedEventBatcher.Cancel() defer deletedEventBatcher.Cancel() @@ -66,32 +66,36 @@ func handleInstrumentedApplicationWatchEvents(ctx context.Context, watcher watch } switch event.Type { case watch.Added: - handleAddedEvent(event.Object.(*v1alpha1.InstrumentedApplication)) + handleAddedEvent(event.Object.(*v1alpha1.InstrumentationConfig)) case watch.Deleted: - handleDeletedEvent(event.Object.(*v1alpha1.InstrumentedApplication)) + handleDeletedEvent(event.Object.(*v1alpha1.InstrumentationConfig)) } } } } -func handleAddedEvent(app *v1alpha1.InstrumentedApplication) { - name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(app.Name) +func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { - genericErrorMessage(sse.MessageEventAdded, "InstrumentedApplication", "error getting workload info") + genericErrorMessage(sse.MessageEventAdded, "InstrumentationConfig", err.Error()) return } - namespace := app.Namespace - target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf("InstrumentedApplication %s created", name) + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf("InstrumentationConfig %s created successfully", name) addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } -func handleDeletedEvent(app *v1alpha1.InstrumentedApplication) { - name, _, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(app.Name) +func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { - genericErrorMessage(sse.MessageEventDeleted, "InstrumentedApplication", "error getting workload info") + genericErrorMessage(sse.MessageEventDeleted, "InstrumentationConfig", err.Error()) return } - data := fmt.Sprintf("Source %s deleted successfully", name) - deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, "") + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf("InstrumentationConfig %s deleted successfully", name) + deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/main.go b/frontend/main.go index ce99dbd7b..a340138ef 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -318,7 +318,7 @@ func main() { } // Start watchers - err = watchers.StartInstrumentedApplicationWatcher(ctx, "") + err = watchers.StartInstrumentationConfigWatcher(ctx, "") if err != nil { log.Printf("Error starting InstrumentedApplication watcher: %v", err) } From 0cf2ce72c3f9dc30dec96e05d0cc366477479143 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 18:03:27 +0200 Subject: [PATCH 143/259] revert pull Ron PR --- .../odigos.io_instrumentationconfigs.yaml | 56 ------------------- .../v1alpha1/instrumentationconfigstatus.go | 20 +------ .../v1alpha1/instrumentationconfig_types.go | 37 ------------ .../v1alpha1/instrumentedapplication_types.go | 27 +++++---- api/odigos/v1alpha1/zz_generated.deepcopy.go | 7 --- .../odigos.io_instrumentationconfigs.yaml | 56 ------------------- .../instrumentationdevice/common.go | 46 +++++++-------- .../instrumentation/instrumentation.go | 20 +++---- 8 files changed, 51 insertions(+), 218 deletions(-) diff --git a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml index 5a49486ff..8fb450866 100644 --- a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml +++ b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml @@ -423,62 +423,6 @@ spec: type: object status: properties: - conditions: - 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 runtimeDetailsByContainer: description: Capture Runtime Details for the workloads that this CR applies to. diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go index f6796dea5..02b0468ee 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go @@ -17,15 +17,10 @@ limitations under the License. package v1alpha1 -import ( - v1 "k8s.io/client-go/applyconfigurations/meta/v1" -) - // InstrumentationConfigStatusApplyConfiguration represents a declarative configuration of the InstrumentationConfigStatus type for use // with apply. type InstrumentationConfigStatusApplyConfiguration struct { RuntimeDetailsByContainer []RuntimeDetailsByContainerApplyConfiguration `json:"runtimeDetailsByContainer,omitempty"` - Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` } // InstrumentationConfigStatusApplyConfiguration constructs a declarative configuration of the InstrumentationConfigStatus type for use with @@ -45,17 +40,4 @@ func (b *InstrumentationConfigStatusApplyConfiguration) WithRuntimeDetailsByCont b.RuntimeDetailsByContainer = append(b.RuntimeDetailsByContainer, *values[i]) } return b -} - -// 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 *InstrumentationConfigStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentationConfigStatusApplyConfiguration { - for i := range values { - if values[i] == nil { - panic("nil value passed to WithConditions") - } - b.Conditions = append(b.Conditions, *values[i]) - } - return b -} +} \ No newline at end of file diff --git a/api/odigos/v1alpha1/instrumentationconfig_types.go b/api/odigos/v1alpha1/instrumentationconfig_types.go index a8158d713..0c157f3a3 100644 --- a/api/odigos/v1alpha1/instrumentationconfig_types.go +++ b/api/odigos/v1alpha1/instrumentationconfig_types.go @@ -5,16 +5,8 @@ import ( "github.com/odigos-io/odigos/common" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) -// +kubebuilder:object:generate=false -type WorkloadDetailsObject interface { - client.Object - RuntimeDetailsByContainer() []RuntimeDetailsByContainer - Conditions() *[]metav1.Condition -} - // +genclient // +kubebuilder:object:root=true // +kubebuilder:subresource:status @@ -28,38 +20,9 @@ type InstrumentationConfig struct { Status InstrumentationConfigStatus `json:"status,omitempty"` } -var _ WorkloadDetailsObject = &InstrumentationConfig{} - -func (ic *InstrumentationConfig) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { - return ic.Status.RuntimeDetailsByContainer -} - -func (ic *InstrumentationConfig) Conditions() *[]metav1.Condition { - return &ic.Status.Conditions -} - -// +kubebuilder:object:generate=true -type RuntimeDetailsByContainer struct { - ContainerName string `json:"containerName"` - Language common.ProgrammingLanguage `json:"language"` - RuntimeVersion string `json:"runtimeVersion,omitempty"` - EnvVars []EnvVar `json:"envVars,omitempty"` - OtherAgent *OtherAgent `json:"otherAgent,omitempty"` - LibCType *common.LibCType `json:"libCType,omitempty"` - - // Stores the error message from the CRI runtime if returned to prevent instrumenting the container if an error exists. - CriErrorMessage *string `json:"criErrorMessage,omitempty"` - // Holds the environment variables retrieved from the container runtime. - EnvFromContainerRuntime []EnvVar `json:"envFromContainerRuntime,omitempty"` - // A temporary variable used during migration to track whether the new runtime detection process has been executed. If empty, it indicates the process has not yet been run. This field may be removed later. - RuntimeUpdateState *ProcessingState `json:"runtimeUpdateState,omitempty"` -} - type InstrumentationConfigStatus struct { // Capture Runtime Details for the workloads that this CR applies to. RuntimeDetailsByContainer []RuntimeDetailsByContainer `json:"runtimeDetailsByContainer,omitempty"` - - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" protobuf:"bytes,1,rep,name=conditions"` } // Config for the OpenTelemeetry SDKs that should be applied to a workload. diff --git a/api/odigos/v1alpha1/instrumentedapplication_types.go b/api/odigos/v1alpha1/instrumentedapplication_types.go index b66a6c6a2..cbedca502 100644 --- a/api/odigos/v1alpha1/instrumentedapplication_types.go +++ b/api/odigos/v1alpha1/instrumentedapplication_types.go @@ -51,6 +51,23 @@ const ( ProcessingStateSkipped ProcessingState = "Skipped" ) +// +kubebuilder:object:generate=true +type RuntimeDetailsByContainer struct { + ContainerName string `json:"containerName"` + Language common.ProgrammingLanguage `json:"language"` + RuntimeVersion string `json:"runtimeVersion,omitempty"` + EnvVars []EnvVar `json:"envVars,omitempty"` + OtherAgent *OtherAgent `json:"otherAgent,omitempty"` + LibCType *common.LibCType `json:"libCType,omitempty"` + + // Stores the error message from the CRI runtime if returned to prevent instrumenting the container if an error exists. + CriErrorMessage *string `json:"criErrorMessage,omitempty"` + // Holds the environment variables retrieved from the container runtime. + EnvFromContainerRuntime []EnvVar `json:"envFromContainerRuntime,omitempty"` + // A temporary variable used during migration to track whether the new runtime detection process has been executed. If empty, it indicates the process has not yet been run. This field may be removed later. + RuntimeUpdateState *ProcessingState `json:"runtimeUpdateState,omitempty"` +} + // +kubebuilder:object:generate=true type OptionByContainer struct { ContainerName string `json:"containerName"` @@ -93,16 +110,6 @@ type InstrumentedApplicationList struct { Items []InstrumentedApplication `json:"items"` } -var _ WorkloadDetailsObject = &InstrumentedApplication{} - -func (ia *InstrumentedApplication) RuntimeDetailsByContainer() []RuntimeDetailsByContainer { - return ia.Spec.RuntimeDetails -} - -func (ia *InstrumentedApplication) Conditions() *[]metav1.Condition { - return &ia.Status.Conditions -} - func init() { SchemeBuilder.Register(&InstrumentedApplication{}, &InstrumentedApplicationList{}) } diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 4d703eb33..5c7edb7ad 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -484,13 +484,6 @@ func (in *InstrumentationConfigStatus) DeepCopyInto(out *InstrumentationConfigSt (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationConfigStatus. diff --git a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml index 5a49486ff..8fb450866 100644 --- a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml +++ b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml @@ -423,62 +423,6 @@ spec: type: object status: properties: - conditions: - 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 runtimeDetailsByContainer: description: Capture Runtime Details for the workloads that this CR applies to. diff --git a/instrumentor/controllers/instrumentationdevice/common.go b/instrumentor/controllers/instrumentationdevice/common.go index 7adaecab1..a0de5727a 100644 --- a/instrumentor/controllers/instrumentationdevice/common.go +++ b/instrumentor/controllers/instrumentationdevice/common.go @@ -66,13 +66,13 @@ func isDataCollectionReady(ctx context.Context, c client.Client) bool { return nodeCollectorsGroup.Status.Ready } -func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetailsObject) (error, bool) { +func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.Client, runtimeDetails *odigosv1.InstrumentedApplication) (error, bool) { // devicePartiallyApplied is used to indicate that the instrumentation device was partially applied for some of the containers. devicePartiallyApplied := false deviceNotAppliedDueToPresenceOfAnotherAgent := false logger := log.FromContext(ctx) - obj, err := getWorkloadObject(ctx, kubeClient, workloadDetails) + obj, err := getWorkloadObject(ctx, kubeClient, runtimeDetails) if err != nil { return err, false } @@ -133,7 +133,7 @@ func addInstrumentationDeviceToWorkload(ctx context.Context, kubeClient client.C agentsCanRunConcurrently = *odigosConfiguration.AllowConcurrentAgents } - err, deviceApplied, deviceSkippedDueToOtherAgent := instrumentation.ApplyInstrumentationDevicesToPodTemplate(podSpec, workloadDetails.RuntimeDetailsByContainer(), otelSdkToUse, obj, logger, agentsCanRunConcurrently) + err, deviceApplied, deviceSkippedDueToOtherAgent := instrumentation.ApplyInstrumentationDevicesToPodTemplate(podSpec, runtimeDetails, otelSdkToUse, obj, logger, agentsCanRunConcurrently) if err != nil { return err } @@ -213,8 +213,8 @@ func removeInstrumentationDeviceFromWorkload(ctx context.Context, kubeClient cli return nil } -func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDetailsObject client.Object) (client.Object, error) { - name, kind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(runtimeDetailsObject.GetName()) +func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDetails *odigosv1.InstrumentedApplication) (client.Object, error) { + name, kind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(runtimeDetails.Name) if err != nil { return nil, err } @@ -225,7 +225,7 @@ func getWorkloadObject(ctx context.Context, kubeClient client.Client, runtimeDet } err = kubeClient.Get(ctx, client.ObjectKey{ - Namespace: runtimeDetailsObject.GetNamespace(), + Namespace: runtimeDetails.Namespace, Name: name, }, workloadObject) if err != nil { @@ -251,45 +251,45 @@ func getPodSpecFromObject(obj client.Object) (*corev1.PodTemplateSpec, error) { // reconciles a single workload, which might be triggered by a change in multiple resources. // each time a relevant resource changes, this function is called to reconcile the workload // and always writes the status into the InstrumentedApplication CR -func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, workloadDetails odigosv1.WorkloadDetailsObject, isNodeCollectorReady bool) error { +func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, instrumentedApplication *odigosv1.InstrumentedApplication, isNodeCollectorReady bool) error { - workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(workloadDetails.GetName()) + workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedApplication.Name) if err != nil { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) return err } if !isNodeCollectorReady { - err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonDataCollectionNotReady) + err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonDataCollectionNotReady) if err == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonDataCollectionNotReady), "OpenTelemetry pipeline not yet ready to receive data") + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonDataCollectionNotReady), "OpenTelemetry pipeline not yet ready to receive data") } else { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) } return err } - if len(workloadDetails.RuntimeDetailsByContainer()) == 0 { - err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonNoRuntimeDetails) + if len(instrumentedApplication.Spec.RuntimeDetails) == 0 { + err := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonNoRuntimeDetails) if err == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonNoRuntimeDetails), "No runtime details found") + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonNoRuntimeDetails), "No runtime details found") } else { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), err.Error()) } return err } - runtimeVersionSupport, err := versionsupport.IsRuntimeVersionSupported(ctx, workloadDetails.RuntimeDetailsByContainer()) + runtimeVersionSupport, err := versionsupport.IsRuntimeVersionSupported(ctx, instrumentedApplication.Spec.RuntimeDetails) if !runtimeVersionSupport { - errRemove := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, workloadDetails.GetNamespace(), workloadKind, workloadName, ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported) + errRemove := removeInstrumentationDeviceFromWorkload(ctx, kubeClient, instrumentedApplication.Namespace, workloadKind, workloadName, ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported) if errRemove == nil { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonRuntimeVersionNotSupported), err.Error()) } else { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), errRemove.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrRemoving), errRemove.Error()) } return nil } - err, devicePartiallyApplied := addInstrumentationDeviceToWorkload(ctx, kubeClient, workloadDetails) + err, devicePartiallyApplied := addInstrumentationDeviceToWorkload(ctx, kubeClient, instrumentedApplication) if err == nil { var successMessage string if devicePartiallyApplied { @@ -297,9 +297,9 @@ func reconcileSingleWorkload(ctx context.Context, kubeClient client.Client, work } else { successMessage = "Instrumentation device applied successfully" } - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionTrue, appliedInstrumentationDeviceType, "InstrumentationDeviceApplied", successMessage) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionTrue, appliedInstrumentationDeviceType, "InstrumentationDeviceApplied", successMessage) } else { - conditions.UpdateStatusConditions(ctx, kubeClient, workloadDetails, workloadDetails.Conditions(), metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrApplying), err.Error()) + conditions.UpdateStatusConditions(ctx, kubeClient, instrumentedApplication, &instrumentedApplication.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, string(ApplyInstrumentationDeviceReasonErrApplying), err.Error()) } return err } diff --git a/instrumentor/instrumentation/instrumentation.go b/instrumentor/instrumentation/instrumentation.go index 5451356e3..f5afd09b4 100644 --- a/instrumentor/instrumentation/instrumentation.go +++ b/instrumentor/instrumentation/instrumentation.go @@ -22,7 +22,7 @@ var ( ErrPatchEnvVars = errors.New("failed to patch env vars") ) -func ApplyInstrumentationDevicesToPodTemplate(original *corev1.PodTemplateSpec, runtimeDetails []odigosv1.RuntimeDetailsByContainer, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object, +func ApplyInstrumentationDevicesToPodTemplate(original *corev1.PodTemplateSpec, runtimeDetails *odigosv1.InstrumentedApplication, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object, logger logr.Logger, agentsCanRunConcurrently bool) (error, bool, bool) { // delete any existing instrumentation devices. // this is necessary for example when migrating from community to enterprise, @@ -154,8 +154,8 @@ func RevertInstrumentationDevices(original *corev1.PodTemplateSpec) bool { return changed } -func getLanguageOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) common.ProgrammingLanguage { - for _, l := range runtimeDetails { +func getLanguageOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) common.ProgrammingLanguage { + for _, l := range instrumentation.Spec.RuntimeDetails { if l.ContainerName == containerName { return l.Language } @@ -164,8 +164,8 @@ func getLanguageOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, return common.UnknownProgrammingLanguage } -func getContainerOtherAgents(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) *odigosv1.OtherAgent { - for _, l := range runtimeDetails { +func getContainerOtherAgents(instrumentation *odigosv1.InstrumentedApplication, containerName string) *odigosv1.OtherAgent { + for _, l := range instrumentation.Spec.RuntimeDetails { if l.ContainerName == containerName { if l.OtherAgent != nil && *l.OtherAgent != (odigosv1.OtherAgent{}) { return l.OtherAgent @@ -175,8 +175,8 @@ func getContainerOtherAgents(runtimeDetails []odigosv1.RuntimeDetailsByContainer return nil } -func getLibCTypeOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) *common.LibCType { - for _, l := range runtimeDetails { +func getLibCTypeOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) *common.LibCType { + for _, l := range instrumentation.Spec.RuntimeDetails { if l.ContainerName == containerName { return l.LibCType } @@ -187,10 +187,10 @@ func getLibCTypeOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, // getEnvVarsOfContainer returns the env vars which are defined for the given container and are used for instrumentation purposes. // This function also returns env vars which are declared in the container build. -func getEnvVarsOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, containerName string) map[string]string { +func getEnvVarsOfContainer(instrumentation *odigosv1.InstrumentedApplication, containerName string) map[string]string { envVars := make(map[string]string) - for _, l := range runtimeDetails { + for _, l := range instrumentation.Spec.RuntimeDetails { if l.ContainerName == containerName { for _, env := range l.EnvVars { envVars[env.Name] = env.Value @@ -204,7 +204,7 @@ func getEnvVarsOfContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, // when otelsdk is nil, it means that the container is not instrumented. // this will trigger reverting of any existing env vars which were set by odigos before. -func patchEnvVarsForContainer(runtimeDetails []odigosv1.RuntimeDetailsByContainer, container *corev1.Container, sdk *common.OtelSdk, programmingLanguage common.ProgrammingLanguage, manifestEnvOriginal *envoverwrite.OrigWorkloadEnvValues) error { +func patchEnvVarsForContainer(runtimeDetails *odigosv1.InstrumentedApplication, container *corev1.Container, sdk *common.OtelSdk, programmingLanguage common.ProgrammingLanguage, manifestEnvOriginal *envoverwrite.OrigWorkloadEnvValues) error { observedEnvs := getEnvVarsOfContainer(runtimeDetails, container.Name) From 51465e1161080503e254fb47657cfbbc739d2b50 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 18:05:36 +0200 Subject: [PATCH 144/259] fix: empty line --- .../odigos/v1alpha1/instrumentationconfigstatus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go index 02b0468ee..efab8f15b 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigstatus.go @@ -40,4 +40,4 @@ func (b *InstrumentationConfigStatusApplyConfiguration) WithRuntimeDetailsByCont b.RuntimeDetailsByContainer = append(b.RuntimeDetailsByContainer, *values[i]) } return b -} \ No newline at end of file +} From 933e5b03142780a3e5bd290c56ee2338f7fa8802 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 18:40:55 +0200 Subject: [PATCH 145/259] fix: build error --- frontend/graph/conversions.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index d7a06642e..695c1c86a 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -61,15 +61,15 @@ func instrumentationConfigToActualSource(instruConfig v1alpha1.InstrumentationCo // Map the conditions var conditions []*model.Condition - for _, condition := range instruConfig.Status.Conditions { - conditions = append(conditions, &model.Condition{ - Status: k8sConditionStatusToGql(condition.Status), - Type: condition.Type, - Reason: &condition.Reason, - Message: &condition.Message, - LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), - }) - } + // for _, condition := range instruConfig.Status.Conditions { + // conditions = append(conditions, &model.Condition{ + // Status: k8sConditionStatusToGql(condition.Status), + // Type: condition.Type, + // Reason: &condition.Reason, + // Message: &condition.Message, + // LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), + // }) + // } // Return the converted K8sActualSource object return &model.K8sActualSource{ From 4e5d8ba5e0530fc71c6cc6b9e8a3eb20c6b90309 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 18:49:25 +0200 Subject: [PATCH 146/259] fix: improve health status determination logic --- .../webapp/utils/functions/strings/get-health-status/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/webapp/utils/functions/strings/get-health-status/index.ts b/frontend/webapp/utils/functions/strings/get-health-status/index.ts index f5de8bcf7..ff0169151 100644 --- a/frontend/webapp/utils/functions/strings/get-health-status/index.ts +++ b/frontend/webapp/utils/functions/strings/get-health-status/index.ts @@ -3,7 +3,7 @@ import { STATUSES, type ActualDestination, type K8sActualSource } from '@/types' export const getHealthStatus = (item: K8sActualSource | ActualDestination) => { const conditions = item?.conditions || []; - const isUnhealthy = !conditions.length || !!conditions.find(({ status }) => status === BACKEND_BOOLEAN.FALSE); + const isUnhealthy = !!conditions.find(({ status }) => status === BACKEND_BOOLEAN.FALSE); return isUnhealthy ? STATUSES.UNHEALTHY : STATUSES.HEALTHY; }; From f3e4e6d8ed020ace65f6575c985b06093de9c7b9 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 19:00:47 +0200 Subject: [PATCH 147/259] fix: improve error messages in source management functions --- frontend/services/sources.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 81bcc7f60..c0dfb0f34 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -2,7 +2,6 @@ package services import ( "context" - "errors" "fmt" "time" @@ -309,10 +308,10 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, err } if len(source.Items) == 0 { - return nil, errors.New("\"" + workloadName + "\"" + " source not found") + return nil, fmt.Errorf(`source "%s" not found`, workloadName) } if len(source.Items) > 1 { - return nil, errors.New("\"" + workloadName + "\"" + " expected to get 1 source got " + string(len(source.Items))) + return nil, fmt.Errorf(`expected to get 1 source "%s", got %s`, workloadName, fmt.Sprint(len(source.Items))) } crdName := source.Items[0].Name @@ -329,7 +328,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return errors.New("\"" + workloadName + "\"" + " source already exists") + return fmt.Errorf(`source "%s" already exists`, workloadName) } newSource := &v1alpha1.Source{ @@ -366,7 +365,7 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { if enabled == nil { - return errors.New("enabled must be provided") + return fmt.Errorf("enabled must be provided") } if *enabled { From 0d0c27b0a879902d7e724da4f74633b7d956aaf9 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 19:01:12 +0200 Subject: [PATCH 148/259] fix: correct terminology for InstrumentationConfig in watchers and logs --- frontend/kube/watchers/instrumentation_config_watcher.go | 4 ++-- frontend/main.go | 2 +- frontend/webapp/hooks/notification/useClickNotif.ts | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index fa0e3e74f..b762d585a 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -19,7 +19,7 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er addedEventBatcher = NewEventBatcher( EventBatcherConfig{ Event: sse.MessageEventAdded, - CRDType: "InstrumentationConfigs", + CRDType: "InstrumentationConfig", SuccessBatchMessageFunc: func(count int, crdType string) string { return fmt.Sprintf("successfully added %d sources", count) }, @@ -32,7 +32,7 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er deletedEventBatcher = NewEventBatcher( EventBatcherConfig{ Event: sse.MessageEventDeleted, - CRDType: "InstrumentationConfigs", + CRDType: "InstrumentationConfig", SuccessBatchMessageFunc: func(count int, crdType string) string { return fmt.Sprintf("successfully deleted %d sources", count) }, diff --git a/frontend/main.go b/frontend/main.go index a340138ef..5b5bb13a4 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -320,7 +320,7 @@ func main() { // Start watchers err = watchers.StartInstrumentationConfigWatcher(ctx, "") if err != nil { - log.Printf("Error starting InstrumentedApplication watcher: %v", err) + log.Printf("Error starting InstrumentationConfig watcher: %v", err) } err = watchers.StartDestinationWatcher(ctx, flags.Namespace) diff --git a/frontend/webapp/hooks/notification/useClickNotif.ts b/frontend/webapp/hooks/notification/useClickNotif.ts index a9ba5a970..1f9fc090b 100644 --- a/frontend/webapp/hooks/notification/useClickNotif.ts +++ b/frontend/webapp/hooks/notification/useClickNotif.ts @@ -29,6 +29,7 @@ export const useClickNotif = () => { break; case OVERVIEW_ENTITY_TYPES.SOURCE: + case 'InstrumentationConfig': case 'InstrumentedApplication': case 'InstrumentationInstance': drawerItem['type'] = OVERVIEW_ENTITY_TYPES.SOURCE; From bcdedbac6f66ee81edc8431a316f13cafa54560d Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 29 Dec 2024 19:04:07 +0200 Subject: [PATCH 149/259] fix: remove obsolete case for InstrumentedApplication in useClickNotif hook --- frontend/webapp/hooks/notification/useClickNotif.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/webapp/hooks/notification/useClickNotif.ts b/frontend/webapp/hooks/notification/useClickNotif.ts index 1f9fc090b..0be9bc130 100644 --- a/frontend/webapp/hooks/notification/useClickNotif.ts +++ b/frontend/webapp/hooks/notification/useClickNotif.ts @@ -30,7 +30,6 @@ export const useClickNotif = () => { case OVERVIEW_ENTITY_TYPES.SOURCE: case 'InstrumentationConfig': - case 'InstrumentedApplication': case 'InstrumentationInstance': drawerItem['type'] = OVERVIEW_ENTITY_TYPES.SOURCE; drawerItem['id'] = getIdFromSseTarget(target, OVERVIEW_ENTITY_TYPES.SOURCE); From 9a78b0cd8888ab6093e6550d3e14f3866f279c70 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 10:33:05 +0200 Subject: [PATCH 150/259] feat: add CRD types and improve error message formatting --- common/consts/consts.go | 6 ++++++ frontend/kube/watchers/batcher.go | 2 +- frontend/services/sources.go | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/common/consts/consts.go b/common/consts/consts.go index f053c7b61..a98d34179 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -29,9 +29,15 @@ const ( // or odigos is uninstalled. // Should only be used for environment variables that are modified by odigos. ManifestEnvOriginalValAnnotation = "odigos.io/manifest-env-original-val" + // Used to label instrumentation instances by the corresponding // instrumented app for better query performance. InstrumentedAppNameLabel = "instrumented-app" + + // CRD types + InstrumentationConfig = "InstrumentationConfig" + InstrumentationInstance = "InstrumentationInstance" + Destination = "Destination" ) var ( diff --git a/frontend/kube/watchers/batcher.go b/frontend/kube/watchers/batcher.go index a0aa74cf2..5cf020319 100644 --- a/frontend/kube/watchers/batcher.go +++ b/frontend/kube/watchers/batcher.go @@ -32,7 +32,7 @@ type EventBatcher struct { } type EventBatcherConfig struct { - // Event to batch, not configuring this value (empty string) will cause to all events to be batched + // Event to batch, not configuring this value (empty string) will cause all events to be batched Event sse.MessageEvent // Message type to batch, not configuring this value (empty string) will cause all messages to be batched MessageType sse.MessageType diff --git a/frontend/services/sources.go b/frontend/services/sources.go index c0dfb0f34..fc53b250f 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -311,7 +311,7 @@ func getSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, fmt.Errorf(`source "%s" not found`, workloadName) } if len(source.Items) > 1 { - return nil, fmt.Errorf(`expected to get 1 source "%s", got %s`, workloadName, fmt.Sprint(len(source.Items))) + return nil, fmt.Errorf(`expected to get 1 source "%s", got %d`, workloadName, len(source.Items)) } crdName := source.Items[0].Name From 0063fd3652c6f4efaaf20f4d14c1cc22ee119b75 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 10:34:05 +0200 Subject: [PATCH 151/259] refactor: improve event handling and message formatting for destination and instrumentation config watchers --- frontend/kube/watchers/destination_watcher.go | 70 ++++++++++++------- .../instrumentation_config_watcher.go | 32 +++++---- .../instrumentation_instance_watcher.go | 42 +++++------ 3 files changed, 78 insertions(+), 66 deletions(-) diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index c08058405..a7c348eb8 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -3,9 +3,9 @@ package watchers import ( "context" "fmt" - "log" "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/endpoints/sse" "github.com/odigos-io/odigos/frontend/kube" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -35,50 +35,66 @@ func handleDestinationWatchEvents(ctx context.Context, watcher watch.Interface) } switch event.Type { case watch.Added: - handleAddedDestination(event) + handleAddedDestination(event.Object.(*v1alpha1.Destination)) case watch.Modified: - handleModifiedDestination(event) + handleModifiedDestination(event.Object.(*v1alpha1.Destination)) case watch.Deleted: - handleDeletedDestination(event) - default: - log.Printf("unexpected type: %T", event.Object) + handleDeletedDestination(event.Object.(*v1alpha1.Destination)) } } } } -func handleAddedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventAdded, "Destination", "error type assertion") +func handleAddedDestination(destination *v1alpha1.Destination) { + name := destination.Spec.DestinationName + if name == "" { + name = string(destination.Spec.Type) } - data := fmt.Sprintf("Destination %s created", destination.Spec.DestinationName) - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventAdded, Type: "success", Target: destination.Name, Data: data, CRDType: "Destination"}) + + data := fmt.Sprintf(`Successfully added "%s" %s`, name, consts.Destination) + sse.SendMessageToClient(sse.SSEMessage{ + Type: sse.MessageTypeSuccess, + Event: sse.MessageEventAdded, + Data: data, + CRDType: consts.Destination, + Target: destination.Name, + }) } -func handleModifiedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventModified, "Destination", "error type assertion") - } - if len(destination.Status.Conditions) == 0 { +func handleModifiedDestination(destination *v1alpha1.Destination) { + length := len(destination.Status.Conditions) + if length == 0 { return } - lastCondition := destination.Status.Conditions[len(destination.Status.Conditions)-1] + lastCondition := destination.Status.Conditions[length-1] data := lastCondition.Message conditionType := sse.MessageTypeSuccess - if lastCondition.Status == "False" { + if lastCondition.Status == metav1.ConditionFalse { conditionType = sse.MessageTypeError } - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventModified, Type: conditionType, Target: destination.Name, Data: data, CRDType: "Destination"}) + + sse.SendMessageToClient(sse.SSEMessage{ + Type: conditionType, + Event: sse.MessageEventModified, + Data: data, + CRDType: consts.Destination, + Target: destination.Name, + }) } -func handleDeletedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventDeleted, "Destination", "error type assertion") +func handleDeletedDestination(destination *v1alpha1.Destination) { + name := destination.Spec.DestinationName + if name == "" { + name = string(destination.Spec.Type) } - data := fmt.Sprintf("Destination %s deleted successfully", destination.Spec.DestinationName) - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventDeleted, Type: "success", Target: "", Data: data, CRDType: "Destination"}) + + data := fmt.Sprintf(`Successfully removed "%s" %s`, name, consts.Destination) + sse.SendMessageToClient(sse.SSEMessage{ + Type: sse.MessageTypeSuccess, + Event: sse.MessageEventDeleted, + Data: data, + CRDType: consts.Destination, + Target: destination.Name, + }) } diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index b762d585a..9b230a1e3 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -3,8 +3,10 @@ package watchers import ( "context" "fmt" + "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/endpoints/sse" "github.com/odigos-io/odigos/frontend/kube" commonutils "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -18,33 +20,37 @@ var deletedEventBatcher *EventBatcher func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { addedEventBatcher = NewEventBatcher( EventBatcherConfig{ - Event: sse.MessageEventAdded, - CRDType: "InstrumentationConfig", + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventAdded, + CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("successfully added %d sources", count) + return fmt.Sprintf("Successfully added %d sources", count) }, FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("failed to add %d sources", count) + return fmt.Sprintf("Failed to add %d sources", count) }, }, ) deletedEventBatcher = NewEventBatcher( EventBatcherConfig{ - Event: sse.MessageEventDeleted, - CRDType: "InstrumentationConfig", + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventDeleted, + CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("successfully deleted %d sources", count) + return fmt.Sprintf("Successfully removed %d sources", count) }, FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("failed to delete %d sources", count) + return fmt.Sprintf("Failed to remove %d sources", count) }, }, ) watcher, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { - return fmt.Errorf("error creating watcher: %v", err) + return fmt.Errorf("error creating watcher: %w", err) } go handleInstrumentationConfigWatchEvents(ctx, watcher) @@ -78,12 +84,12 @@ func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { - genericErrorMessage(sse.MessageEventAdded, "InstrumentationConfig", err.Error()) + genericErrorMessage(sse.MessageEventAdded, consts.InstrumentationConfig, err.Error()) return } target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf("InstrumentationConfig %s created successfully", name) + data := fmt.Sprintf(`Successfully added "%s" source`, name) addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } @@ -91,11 +97,11 @@ func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { - genericErrorMessage(sse.MessageEventDeleted, "InstrumentationConfig", err.Error()) + genericErrorMessage(sse.MessageEventDeleted, consts.InstrumentationConfig, err.Error()) return } target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf("InstrumentationConfig %s deleted successfully", name) + data := fmt.Sprintf(`Successfully removed "%s" source`, name) deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index feb1e7b4d..412fdbbf1 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -19,10 +19,11 @@ var modifiedBatcher *EventBatcher func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) error { modifiedBatcher = NewEventBatcher( EventBatcherConfig{ - Event: sse.MessageEventModified, - MessageType: sse.MessageTypeError, - Duration: 10 * time.Second, - CRDType: "InstrumentationInstance", + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventModified, + MessageType: sse.MessageTypeError, + CRDType: consts.InstrumentationInstance, FailureBatchMessageFunc: func(batchSize int, crd string) string { return fmt.Sprintf("Failed to instrument %d instances", batchSize) }, @@ -51,48 +52,37 @@ func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch } switch event.Type { case watch.Modified: - handleModifiedInstrumentationInstance(event) + handleModifiedInstrumentationInstance(event.Object.(*v1alpha1.InstrumentationInstance)) } } } } -func handleModifiedInstrumentationInstance(event watch.Event) { - instrumentedInstance, ok := event.Object.(*v1alpha1.InstrumentationInstance) - if !ok { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error type assertion") - } - healthy := instrumentedInstance.Status.Healthy - - if healthy == nil { - return - } - - if *healthy { +func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.InstrumentationInstance) { + healthy := instruInsta.Status.Healthy + if healthy == nil || *healthy { // send notification to frontend only if the instance is not healthy return } - labels := instrumentedInstance.GetLabels() + labels := instruInsta.GetLabels() if labels == nil { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting labels") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting labels") } instrumentedAppName, ok := labels[consts.InstrumentedAppNameLabel] if !ok { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting instrumented app name from labels") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting instrumented app name from labels") } + namespace := instruInsta.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedAppName) if err != nil { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting workload info") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting workload info") } - namespace := instrumentedInstance.Namespace - target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf("%s %s", instrumentedInstance.Status.Reason, instrumentedInstance.Status.Message) - - fmt.Printf("InstrumentationInstance %s modified\n", name) + data := fmt.Sprintf("%s %s", instruInsta.Status.Reason, instruInsta.Status.Message) + fmt.Printf("%s %s modified\n", consts.InstrumentationInstance, name) modifiedBatcher.AddEvent(sse.MessageTypeError, data, target) } From a146201831b429e91128c872190060a65b1da911 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 10:39:30 +0200 Subject: [PATCH 152/259] fix: correct message formatting in SSEMessage and improve error handling in instrumentation watcher --- frontend/kube/watchers/batcher.go | 4 ++-- frontend/kube/watchers/common.go | 10 +++++----- .../kube/watchers/instrumentation_instance_watcher.go | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/kube/watchers/batcher.go b/frontend/kube/watchers/batcher.go index 5cf020319..ae50a9d3e 100644 --- a/frontend/kube/watchers/batcher.go +++ b/frontend/kube/watchers/batcher.go @@ -83,11 +83,11 @@ func (eb *EventBatcher) AddEvent(msgType sse.MessageType, data, target string) e defer eb.mu.Unlock() message := sse.SSEMessage{ - Event: eb.config.Event, Type: msgType, - Target: target, + Event: eb.config.Event, Data: data, CRDType: eb.config.CRDType, + Target: target, } eb.batch = append(eb.batch, message) diff --git a/frontend/kube/watchers/common.go b/frontend/kube/watchers/common.go index bf4343c84..ebc70c4cd 100644 --- a/frontend/kube/watchers/common.go +++ b/frontend/kube/watchers/common.go @@ -6,10 +6,10 @@ import ( func genericErrorMessage(event sse.MessageEvent, crd string, data string) { sse.SendMessageToClient(sse.SSEMessage{ - Event: event, - Type: sse.MessageTypeError, - Target: "", - Data: "Something went wrong: " + data, + Type: sse.MessageTypeError, + Event: event, + Data: "Something went wrong: " + data, CRDType: crd, + Target: "", }) -} \ No newline at end of file +} diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 412fdbbf1..2af8df875 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -78,7 +78,7 @@ func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.Instrumentation namespace := instruInsta.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedAppName) if err != nil { - genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting workload info") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, err.Error()) } target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) From 640056e38bf5e75af8f5175880047dedb32d714f Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 11:11:06 +0200 Subject: [PATCH 153/259] fix: improve message formatting for destination and instrumentation events --- autoscaler/controllers/gateway/configmap.go | 2 +- frontend/kube/watchers/batcher.go | 8 ++++---- frontend/kube/watchers/destination_watcher.go | 11 +++++++---- .../kube/watchers/instrumentation_config_watcher.go | 12 ++++++------ .../watchers/instrumentation_instance_watcher.go | 5 ++--- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/autoscaler/controllers/gateway/configmap.go b/autoscaler/controllers/gateway/configmap.go index c94c90327..f4070571f 100644 --- a/autoscaler/controllers/gateway/configmap.go +++ b/autoscaler/controllers/gateway/configmap.go @@ -150,7 +150,7 @@ func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.Proc logger.Error(err, "Failed to update destination error status conditions") } } else { - err := odgiosK8s.UpdateStatusConditions(ctx, c, &dest, &dest.Status.Conditions, metav1.ConditionTrue, destinationConfiguredType, "TransformedToOtelcolConfig", "destination successfully transformed to otelcol configuration") + err := odgiosK8s.UpdateStatusConditions(ctx, c, &dest, &dest.Status.Conditions, metav1.ConditionTrue, destinationConfiguredType, "TransformedToOtelcolConfig", "Destination successfully transformed to otelcol configuration") if err != nil { logger.Error(err, "Failed to update destination success status conditions") } diff --git a/frontend/kube/watchers/batcher.go b/frontend/kube/watchers/batcher.go index ae50a9d3e..18e4dac03 100644 --- a/frontend/kube/watchers/batcher.go +++ b/frontend/kube/watchers/batcher.go @@ -141,21 +141,21 @@ func (eb *EventBatcher) prepareBatchMessage() []sse.SSEMessage { var result []sse.SSEMessage if successCount > 0 { result = append(result, sse.SSEMessage{ - Event: eb.config.Event, Type: sse.MessageTypeSuccess, - Target: "", + Event: eb.config.Event, Data: eb.config.SuccessBatchMessageFunc(successCount, eb.config.CRDType), CRDType: eb.config.CRDType, + Target: "", }) } if failureCount > 0 { result = append(result, sse.SSEMessage{ - Event: eb.config.Event, Type: sse.MessageTypeError, - Target: "", + Event: eb.config.Event, Data: eb.config.FailureBatchMessageFunc(failureCount, eb.config.CRDType), CRDType: eb.config.CRDType, + Target: "", }) } return result diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index a7c348eb8..a12b07488 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -51,7 +51,7 @@ func handleAddedDestination(destination *v1alpha1.Destination) { name = string(destination.Spec.Type) } - data := fmt.Sprintf(`Successfully added "%s" %s`, name, consts.Destination) + data := fmt.Sprintf(`%s "%s" created`, consts.Destination, name) sse.SendMessageToClient(sse.SSEMessage{ Type: sse.MessageTypeSuccess, Event: sse.MessageEventAdded, @@ -69,8 +69,11 @@ func handleModifiedDestination(destination *v1alpha1.Destination) { lastCondition := destination.Status.Conditions[length-1] data := lastCondition.Message - conditionType := sse.MessageTypeSuccess - if lastCondition.Status == metav1.ConditionFalse { + + conditionType := sse.MessageTypeInfo + if lastCondition.Status == metav1.ConditionTrue { + conditionType = sse.MessageTypeSuccess + } else if lastCondition.Status == metav1.ConditionFalse { conditionType = sse.MessageTypeError } @@ -89,7 +92,7 @@ func handleDeletedDestination(destination *v1alpha1.Destination) { name = string(destination.Spec.Type) } - data := fmt.Sprintf(`Successfully removed "%s" %s`, name, consts.Destination) + data := fmt.Sprintf(`%s "%s" deleted`, consts.Destination, name) sse.SendMessageToClient(sse.SSEMessage{ Type: sse.MessageTypeSuccess, Event: sse.MessageEventDeleted, diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index 9b230a1e3..feccf2f67 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -25,10 +25,10 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er Event: sse.MessageEventAdded, CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("Successfully added %d sources", count) + return fmt.Sprintf("Successfully created %d sources", count) }, FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("Failed to add %d sources", count) + return fmt.Sprintf("Failed to create %d sources", count) }, }, ) @@ -40,10 +40,10 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er Event: sse.MessageEventDeleted, CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("Successfully removed %d sources", count) + return fmt.Sprintf("Successfully deleted %d sources", count) }, FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("Failed to remove %d sources", count) + return fmt.Sprintf("Failed to delete %d sources", count) }, }, ) @@ -89,7 +89,7 @@ func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { } target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf(`Successfully added "%s" source`, name) + data := fmt.Sprintf(`Source "%s" created`, name) addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } @@ -102,6 +102,6 @@ func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { } target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf(`Successfully removed "%s" source`, name) + data := fmt.Sprintf(`Source "%s" deleted`, name) deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 2af8df875..15dddf9ab 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -21,8 +21,8 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, - Event: sse.MessageEventModified, MessageType: sse.MessageTypeError, + Event: sse.MessageEventModified, CRDType: consts.InstrumentationInstance, FailureBatchMessageFunc: func(batchSize int, crd string) string { return fmt.Sprintf("Failed to instrument %d instances", batchSize) @@ -82,7 +82,6 @@ func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.Instrumentation } target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf("%s %s", instruInsta.Status.Reason, instruInsta.Status.Message) - fmt.Printf("%s %s modified\n", consts.InstrumentationInstance, name) + data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInsta.Status.Reason, instruInsta.Status.Message) modifiedBatcher.AddEvent(sse.MessageTypeError, data, target) } From 5b2fe60472d270dbf4ffe4f5bf96cb8edb745998 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:09:33 +0200 Subject: [PATCH 154/259] refactor: streamline error handling and notification messages across hooks --- .../destinations/add-destination/index.tsx | 5 +- .../webapp/hooks/actions/useActionCRUD.ts | 46 +++++++-------- .../compute-platform/useComputePlatform.ts | 16 +++--- .../hooks/compute-platform/useNamespace.ts | 17 +++--- .../hooks/destinations/useDestinationCRUD.ts | 46 +++++---------- .../destinations/useDestinationFormData.ts | 2 +- .../useInstrumentationRuleCRUD.ts | 56 ++++++++----------- frontend/webapp/hooks/notification/useSSE.ts | 2 +- .../webapp/hooks/sources/useSourceCRUD.ts | 36 ++++++------ 9 files changed, 95 insertions(+), 131 deletions(-) diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index 9720c7bb2..a1bb1530e 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -61,12 +61,11 @@ export function AddDestinationContainer() { await createSources(configuredSources, configuredFutureApps); await Promise.all(configuredDestinations.map(async ({ form }) => await createDestination(form))); - // Delay redirect by 3 seconds to allow the sources to be created on the backend 1st, - // otherwise we would have to apply polling on the overview page on every mount. + // Delay redirect by 1 seconds to allow the data to reach the backend 1st setTimeout(() => { resetState(); router.push(ROUTES.OVERVIEW); - }, 3000); + }, 1000); }; const isSourcesListEmpty = () => !Object.values(configuredSources).some((sources) => !!sources.length); diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index f261ee47e..695b78a51 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -25,52 +25,48 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + const handleComplete = (actionType: string) => { refetch(); - params?.onSuccess?.(title); + params?.onSuccess?.(actionType); }; const [createAction, cState] = useMutation<{ createAction: { id: string } }>(CREATE_ACTION, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: (res, req) => { - const id = res.createAction.id; - const type = req?.variables?.action.type; - const name = req?.variables?.action.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `action "${label}" was created`, id); - }, + onCompleted: () => handleComplete(ACTION.CREATE), }); const [updateAction, uState] = useMutation<{ updateAction: { id: string } }>(UPDATE_ACTION, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = res.updateAction.id; - const type = req?.variables?.action.type; - const name = req?.variables?.action.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `action "${label}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); const [deleteAction, dState] = useMutation<{ deleteAction: boolean }>(DELETE_ACTION, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.id; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.ACTION)); - handleComplete(ACTION.DELETE, `action "${id}" was deleted`); + handleComplete(ACTION.DELETE); }, }); return { loading: cState.loading || uState.loading || dState.loading, - actions: data?.computePlatform.actions || [], + actions: data?.computePlatform?.actions || [], - createAction: (action: ActionInput) => createAction({ variables: { action } }), - updateAction: (id: string, action: ActionInput) => updateAction({ variables: { id, action } }), - deleteAction: (id: string, actionType: ActionsType) => deleteAction({ variables: { id, actionType } }), + createAction: (action: ActionInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating pipeline action...'); + createAction({ variables: { action } }); + }, + updateAction: (id: string, action: ActionInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating pipeline action...'); + updateAction({ variables: { id, action } }); + }, + deleteAction: (id: string, actionType: ActionsType) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting pipeline action...'); + deleteAction({ variables: { id, actionType } }); + }, }; }; diff --git a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts index d9597d5cd..8af6b93de 100644 --- a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts +++ b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts @@ -1,9 +1,9 @@ -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { useQuery } from '@apollo/client'; import { useNotificationStore } from '@/store'; import { GET_COMPUTE_PLATFORM } from '@/graphql'; import { useFilterStore } from '@/store/useFilterStore'; -import { BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils'; +import { ACTION, BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils'; import { NOTIFICATION_TYPE, type ActionItem, type ComputePlatform, type ComputePlatformMapped } from '@/types'; type UseComputePlatformHook = { @@ -15,19 +15,17 @@ type UseComputePlatformHook = { }; export const useComputePlatform = (): UseComputePlatformHook => { - const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM); const { addNotification } = useNotificationStore(); const filters = useFilterStore(); - useEffect(() => { - if (error) { + const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM, { + onError: (error) => addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: error.name, + title: error.name || ACTION.FETCH, message: error.cause?.message, - }); - } - }, [error]); + }), + }); const mappedData = useMemo(() => { if (!data) return undefined; diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index fe95b9ea5..3b7df92f8 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -1,3 +1,4 @@ +import { ACTION } from '@/utils'; import { useNotificationStore } from '@/store'; import { useMutation, useQuery } from '@apollo/client'; import { useComputePlatform } from './useComputePlatform'; @@ -8,14 +9,6 @@ export const useNamespace = (namespaceName?: string, instrumentationLabeled = nu const { addNotification } = useNotificationStore(); const cp = useComputePlatform(); - const handleError = (title: string, message: string) => { - addNotification({ type: NOTIFICATION_TYPE.ERROR, title, message }); - }; - - const handleComplete = (title: string, message: string) => { - addNotification({ type: NOTIFICATION_TYPE.SUCCESS, title, message }); - }; - const { data, loading, error } = useQuery(GET_NAMESPACES, { skip: !namespaceName, fetchPolicy: 'cache-first', @@ -23,8 +16,12 @@ export const useNamespace = (namespaceName?: string, instrumentationLabeled = nu }); const [persistNamespaceMutation] = useMutation(PERSIST_NAMESPACE, { - onError: (error) => handleError('', error.message), - onCompleted: (res, req) => {}, + onError: (error) => + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: error.name || ACTION.FETCH, + message: error.message, + }), }); return { diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 4870b6617..31eeb1c34 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -25,63 +25,47 @@ export const useDestinationCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + const handleComplete = (actionType: string) => { refetch(); - params?.onSuccess?.(title); + params?.onSuccess?.(actionType); }; - const [createDestination, cState] = useMutation<{ - createNewDestination: { id: string }; - }>(CREATE_DESTINATION, { + const [createDestination, cState] = useMutation<{ createNewDestination: { id: string } }>(CREATE_DESTINATION, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: (res, req) => { - const id = res.createNewDestination.id; - const type = req?.variables?.destination.type; - const name = req?.variables?.destination.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `destination "${label}" was created`, id); - }, + onCompleted: () => handleComplete(ACTION.CREATE), }); - const [updateDestination, uState] = useMutation<{ - updateDestination: { id: string }; - }>(UPDATE_DESTINATION, { + const [updateDestination, uState] = useMutation<{ updateDestination: { id: string } }>(UPDATE_DESTINATION, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = res.updateDestination.id; - const type = req?.variables?.destination.type; - const name = req?.variables?.destination.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `destination "${label}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); - const [deleteDestination, dState] = useMutation<{ - deleteDestination: boolean; - }>(DELETE_DESTINATION, { + const [deleteDestination, dState] = useMutation<{ deleteDestination: boolean }>(DELETE_DESTINATION, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.id; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.DESTINATION)); - handleComplete(ACTION.DELETE, `destination "${id}" was deleted`); + handleComplete(ACTION.DELETE); }, }); return { loading: cState.loading || uState.loading || dState.loading, - destinations: data?.computePlatform.destinations || [], + destinations: data?.computePlatform?.destinations || [], createDestination: (destination: DestinationInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating destination...'); createDestination({ variables: { destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, updateDestination: (id: string, destination: DestinationInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating destination...'); updateDestination({ variables: { id, destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, deleteDestination: (id: string) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting destination...'); deleteDestination({ variables: { id } }); }, }; diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 8fbb73bb9..1e230085b 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -104,7 +104,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp onError: (error) => addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: ACTION.FETCH, + title: error.name || ACTION.FETCH, message: error.message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, }), diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 407c8961b..6c8718365 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -1,7 +1,7 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; +import { ACTION, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; -import { ACTION, deriveTypeFromRule, getSseTargetFromId } from '@/utils'; import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput } from '@/types'; import { CREATE_INSTRUMENTATION_RULE, UPDATE_INSTRUMENTATION_RULE, DELETE_INSTRUMENTATION_RULE } from '@/graphql/mutations'; @@ -25,58 +25,48 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); + const handleError = (title: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, title, message); params?.onError?.(title); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + const handleComplete = (title: string) => { refetch(); params?.onSuccess?.(title); }; - const [createInstrumentationRule, cState] = useMutation<{ - createInstrumentationRule: { ruleId: string }; - }>(CREATE_INSTRUMENTATION_RULE, { + const [createInstrumentationRule, cState] = useMutation<{ createInstrumentationRule: { ruleId: string } }>(CREATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: (res, req) => { - const id = res.createInstrumentationRule.ruleId; - const type = deriveTypeFromRule(req?.variables?.instrumentationRule); - const name = req?.variables?.instrumentationRule.ruleName; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `instrumentation rule "${label}" was created`, id); - }, + onCompleted: () => handleComplete(ACTION.CREATE), }); - const [updateInstrumentationRule, uState] = useMutation<{ - updateInstrumentationRule: { ruleId: string }; - }>(UPDATE_INSTRUMENTATION_RULE, { + const [updateInstrumentationRule, uState] = useMutation<{ updateInstrumentationRule: { ruleId: string } }>(UPDATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = res.updateInstrumentationRule.ruleId; - const type = deriveTypeFromRule(req?.variables?.instrumentationRule); - const name = req?.variables?.instrumentationRule.ruleName; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `instrumentation rule "${label}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); - const [deleteInstrumentationRule, dState] = useMutation<{ - deleteInstrumentationRule: boolean; - }>(DELETE_INSTRUMENTATION_RULE, { + const [deleteInstrumentationRule, dState] = useMutation<{ deleteInstrumentationRule: boolean }>(DELETE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.ruleId; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.RULE)); - handleComplete(ACTION.DELETE, `instrumentation rule "${id}" was deleted`); + handleComplete(ACTION.DELETE); }, }); return { loading: cState.loading || uState.loading || dState.loading, - instrumentationRules: data?.computePlatform.instrumentationRules || [], + instrumentationRules: data?.computePlatform?.instrumentationRules || [], - createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => createInstrumentationRule({ variables: { instrumentationRule } }), - updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }), - deleteInstrumentationRule: (ruleId: string) => deleteInstrumentationRule({ variables: { ruleId } }), + createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating instrumentation rule...'); + createInstrumentationRule({ variables: { instrumentationRule } }); + }, + updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating instrumentation rule...'); + updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }); + }, + deleteInstrumentationRule: (ruleId: string) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting instrumentation rule...'); + deleteInstrumentationRule({ variables: { ruleId } }); + }, }; }; diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts index 4fc506a42..dc69a0e92 100644 --- a/frontend/webapp/hooks/notification/useSSE.ts +++ b/frontend/webapp/hooks/notification/useSSE.ts @@ -40,7 +40,7 @@ export const useSSE = () => { target: data.target, }; - notification.type = modifyType(notification); + // notification.type = modifyType(notification); // Dispatch the notification to the store addNotification(notification); diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index f70c0d706..48b79bd92 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -41,14 +41,13 @@ export const useSourceCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string, id?: WorkloadId) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); + const handleError = (title: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, title, message); params?.onError?.(title); }; - const handleComplete = (title: string, message: string, id?: WorkloadId) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); - startPolling(); + const handleComplete = (title: string) => { + refetch(); params?.onSuccess?.(title); }; @@ -60,31 +59,25 @@ export const useSourceCRUD = (params?: Params) => { handleError(action, error.message); }, onCompleted: (res, req) => { + const count = req?.variables?.sources.length; const namespace = req?.variables?.namespace; const { name, kind, selected } = req?.variables?.sources?.[0] || {}; - - const count = req?.variables?.sources.length; const action = selected ? ACTION.CREATE : ACTION.DELETE; - const fromOrIn = selected ? 'in' : 'from'; if (count > 1) { - handleComplete(action, `${count} sources were ${action.toLowerCase()}d ${fromOrIn} "${namespace}"`); + handleComplete(action); } else { - const id = { kind, name, namespace }; + const id = { namespace, name, kind }; if (!selected) removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE)); if (!selected) setConfiguredSources({ ...configuredSources, [namespace]: configuredSources[namespace]?.filter((source) => source.name !== name) || [] }); - handleComplete(action, `source "${name}" was ${action.toLowerCase()}d ${fromOrIn} "${namespace}"`, selected ? id : undefined); + handleComplete(action); } }, }); const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = req?.variables?.sourceId; - const name = id?.name; - handleComplete(ACTION.UPDATE, `source "${name}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); const persistNamespaces = async (items: { [key: string]: boolean }) => { @@ -113,10 +106,17 @@ export const useSourceCRUD = (params?: Params) => { sources: data?.computePlatform.k8sActualSources || [], createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating sources...'); await persistNamespaces(futureSelectAppsList); await persistSources(selectAppsList, true); }, - updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => await updateSource({ variables: { sourceId, patchSourceRequest } }), - deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => await persistSources(selectAppsList, false), + updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating sources...'); + await updateSource({ variables: { sourceId, patchSourceRequest } }); + }, + deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting sources...'); + await persistSources(selectAppsList, false); + }, }; }; From 4bfe4d187bb4c592d1dc03a2b3ab4dba6a29a45f Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:11:24 +0200 Subject: [PATCH 155/259] refactor: rename event batcher variables for instrumentation config and instance watchers --- .../watchers/instrumentation_config_watcher.go | 16 ++++++++-------- .../instrumentation_instance_watcher.go | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index feccf2f67..c09107fe0 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -14,11 +14,11 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var addedEventBatcher *EventBatcher -var deletedEventBatcher *EventBatcher +var instruConfigAddedEventBatcher *EventBatcher +var instruConfigDeletedEventBatcher *EventBatcher func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { - addedEventBatcher = NewEventBatcher( + instruConfigAddedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -33,7 +33,7 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er }, ) - deletedEventBatcher = NewEventBatcher( + instruConfigDeletedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -59,8 +59,8 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer addedEventBatcher.Cancel() - defer deletedEventBatcher.Cancel() + defer instruConfigAddedEventBatcher.Cancel() + defer instruConfigDeletedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -90,7 +90,7 @@ func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) data := fmt.Sprintf(`Source "%s" created`, name) - addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) + instruConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { @@ -103,5 +103,5 @@ func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) data := fmt.Sprintf(`Source "%s" deleted`, name) - deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) + instruConfigDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 15dddf9ab..60b858771 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -14,10 +14,10 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var modifiedBatcher *EventBatcher +var instruInstanceModifiedBatcher *EventBatcher func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) error { - modifiedBatcher = NewEventBatcher( + instruInstanceModifiedBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -40,7 +40,7 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer modifiedBatcher.Cancel() + defer instruInstanceModifiedBatcher.Cancel() for { select { case <-ctx.Done(): @@ -58,14 +58,14 @@ func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch } } -func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.InstrumentationInstance) { - healthy := instruInsta.Status.Healthy +func handleModifiedInstrumentationInstance(instruInstance *v1alpha1.InstrumentationInstance) { + healthy := instruInstance.Status.Healthy if healthy == nil || *healthy { // send notification to frontend only if the instance is not healthy return } - labels := instruInsta.GetLabels() + labels := instruInstance.GetLabels() if labels == nil { genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting labels") } @@ -75,13 +75,13 @@ func handleModifiedInstrumentationInstance(instruInsta *v1alpha1.Instrumentation genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting instrumented app name from labels") } - namespace := instruInsta.Namespace + namespace := instruInstance.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedAppName) if err != nil { genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, err.Error()) } target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInsta.Status.Reason, instruInsta.Status.Message) - modifiedBatcher.AddEvent(sse.MessageTypeError, data, target) + data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInstance.Status.Reason, instruInstance.Status.Message) + instruInstanceModifiedBatcher.AddEvent(sse.MessageTypeError, data, target) } From 4a82ec3259e00877e27f3a3dcacf8c1e4459d909 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:20:24 +0200 Subject: [PATCH 156/259] feat: implement event batching for destination and instrumentation instance watchers --- frontend/kube/watchers/destination_watcher.go | 80 ++++++++++++++----- .../instrumentation_instance_watcher.go | 1 + 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index a12b07488..706fd6928 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -3,6 +3,7 @@ package watchers import ( "context" "fmt" + "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" @@ -12,7 +13,56 @@ import ( "k8s.io/apimachinery/pkg/watch" ) +var destinationAddedEventBatcher *EventBatcher +var destinationModifiedBatcher *EventBatcher +var destinationDeletedEventBatcher *EventBatcher + func StartDestinationWatcher(ctx context.Context, namespace string) error { + destinationAddedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventAdded, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Successfully created %d destinations", count) + }, + FailureBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Failed to create %d destinations", count) + }, + }, + ) + + destinationModifiedBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventModified, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Successfully transformed %d destinations to otelcol configuration", batchSize) + }, + FailureBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Failed to transform %d destinations to otelcol configuration", batchSize) + }, + }, + ) + + destinationDeletedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventDeleted, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Successfully deleted %d destinations", count) + }, + FailureBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Failed to delete %d destinations", count) + }, + }, + ) + watcher, err := kube.DefaultClient.OdigosClient.Destinations(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { return fmt.Errorf("error creating watcher: %v", err) @@ -24,6 +74,9 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { func handleDestinationWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() + defer destinationAddedEventBatcher.Cancel() + defer destinationModifiedBatcher.Cancel() + defer destinationDeletedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -51,14 +104,9 @@ func handleAddedDestination(destination *v1alpha1.Destination) { name = string(destination.Spec.Type) } + target := destination.Name data := fmt.Sprintf(`%s "%s" created`, consts.Destination, name) - sse.SendMessageToClient(sse.SSEMessage{ - Type: sse.MessageTypeSuccess, - Event: sse.MessageEventAdded, - Data: data, - CRDType: consts.Destination, - Target: destination.Name, - }) + destinationAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } func handleModifiedDestination(destination *v1alpha1.Destination) { @@ -67,6 +115,7 @@ func handleModifiedDestination(destination *v1alpha1.Destination) { return } + target := destination.Name lastCondition := destination.Status.Conditions[length-1] data := lastCondition.Message @@ -77,13 +126,7 @@ func handleModifiedDestination(destination *v1alpha1.Destination) { conditionType = sse.MessageTypeError } - sse.SendMessageToClient(sse.SSEMessage{ - Type: conditionType, - Event: sse.MessageEventModified, - Data: data, - CRDType: consts.Destination, - Target: destination.Name, - }) + destinationModifiedBatcher.AddEvent(conditionType, data, target) } func handleDeletedDestination(destination *v1alpha1.Destination) { @@ -92,12 +135,7 @@ func handleDeletedDestination(destination *v1alpha1.Destination) { name = string(destination.Spec.Type) } + target := destination.Name data := fmt.Sprintf(`%s "%s" deleted`, consts.Destination, name) - sse.SendMessageToClient(sse.SSEMessage{ - Type: sse.MessageTypeSuccess, - Event: sse.MessageEventDeleted, - Data: data, - CRDType: consts.Destination, - Target: destination.Name, - }) + destinationDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 60b858771..5f3bc3c03 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -29,6 +29,7 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) }, }, ) + watcher, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { return fmt.Errorf("error creating watcher: %v", err) From 2400dc9ed09f73812c04a411fea941a5d6f80696 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:29:34 +0200 Subject: [PATCH 157/259] feat: add hideFromHistory option to notifications and improve error message handling --- frontend/webapp/app/page.tsx | 6 +++--- .../notification/notification-manager.tsx | 3 ++- .../instrumentation-rules/rule-drawer/index.tsx | 9 ++++++++- frontend/webapp/hooks/actions/useActionCRUD.ts | 9 +++++---- frontend/webapp/hooks/actions/useActionFormData.ts | 1 + .../hooks/compute-platform/useComputePlatform.ts | 2 +- .../webapp/hooks/compute-platform/useNamespace.ts | 2 +- .../hooks/destinations/useDestinationCRUD.ts | 9 +++++---- .../hooks/destinations/useDestinationFormData.ts | 3 ++- .../useInstrumentationRuleCRUD.ts | 9 +++++---- .../useInstrumentationRuleFormData.ts | 1 + frontend/webapp/hooks/notification/useSSE.ts | 14 -------------- frontend/webapp/hooks/sources/useSourceCRUD.ts | 9 +++++---- frontend/webapp/types/common.ts | 1 + 14 files changed, 40 insertions(+), 38 deletions(-) diff --git a/frontend/webapp/app/page.tsx b/frontend/webapp/app/page.tsx index 9df649d3c..2dcaf764a 100644 --- a/frontend/webapp/app/page.tsx +++ b/frontend/webapp/app/page.tsx @@ -2,10 +2,10 @@ import { useEffect } from 'react'; import { useConfig } from '@/hooks'; import { CenterThis } from '@/styles'; -import { ROUTES, CONFIG } from '@/utils'; import { NOTIFICATION_TYPE } from '@/types'; import { useRouter } from 'next/navigation'; import { useNotificationStore } from '@/store'; +import { ROUTES, CONFIG, ACTION } from '@/utils'; import { FadeLoader } from '@/reuseable-components'; export default function App() { @@ -17,8 +17,8 @@ export default function App() { if (error) { addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: error.name, - message: error.message, + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, }); } else if (data) { const { installation } = data; diff --git a/frontend/webapp/components/notification/notification-manager.tsx b/frontend/webapp/components/notification/notification-manager.tsx index 0bc4791b1..d90188bdf 100644 --- a/frontend/webapp/components/notification/notification-manager.tsx +++ b/frontend/webapp/components/notification/notification-manager.tsx @@ -64,7 +64,8 @@ const NewCount = styled(Text)` `; export const NotificationManager = () => { - const { notifications, markAsSeen } = useNotificationStore(); + const { notifications: n, markAsSeen } = useNotificationStore(); + const notifications = n.filter(({ hideFromHistory }) => !hideFromHistory); const unseen = notifications.filter(({ seen }) => !seen); const unseenCount = unseen.length; diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx index cdcbfeda9..1a1b02573 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx @@ -72,7 +72,14 @@ export const RuleDrawer: React.FC = () => { const handleEdit = (bool?: boolean) => { if (item.type === InstrumentationRuleType.UNKNOWN_TYPE && (bool || bool === undefined)) { - addNotification({ type: NOTIFICATION_TYPE.WARNING, title: FORM_ALERTS.FORBIDDEN, message: FORM_ALERTS.CANNOT_EDIT_RULE, crdType: OVERVIEW_ENTITY_TYPES.RULE, target: id }); + addNotification({ + type: NOTIFICATION_TYPE.WARNING, + title: FORM_ALERTS.FORBIDDEN, + message: FORM_ALERTS.CANNOT_EDIT_RULE, + crdType: OVERVIEW_ENTITY_TYPES.RULE, + target: id, + hideFromHistory: true, + }); } else { setIsEditing(typeof bool === 'boolean' ? bool : true); } diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index 695b78a51..ae79d508f 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -15,13 +15,14 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.ACTION, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.ACTION) : undefined, + hideFromHistory, }); }; @@ -57,15 +58,15 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { actions: data?.computePlatform?.actions || [], createAction: (action: ActionInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating pipeline action...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating pipeline action...', undefined, true); createAction({ variables: { action } }); }, updateAction: (id: string, action: ActionInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating pipeline action...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating pipeline action...', undefined, true); updateAction({ variables: { id, action } }); }, deleteAction: (id: string, actionType: ActionsType) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting pipeline action...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting pipeline action...', undefined, true); deleteAction({ variables: { id, actionType } }); }, }; diff --git a/frontend/webapp/hooks/actions/useActionFormData.ts b/frontend/webapp/hooks/actions/useActionFormData.ts index 55b880056..5f6aed157 100644 --- a/frontend/webapp/hooks/actions/useActionFormData.ts +++ b/frontend/webapp/hooks/actions/useActionFormData.ts @@ -55,6 +55,7 @@ export function useActionFormData() { type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts index 8af6b93de..be53749ad 100644 --- a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts +++ b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts @@ -23,7 +23,7 @@ export const useComputePlatform = (): UseComputePlatformHook => { addNotification({ type: NOTIFICATION_TYPE.ERROR, title: error.name || ACTION.FETCH, - message: error.cause?.message, + message: error.cause?.message || error.message, }), }); diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index 3b7df92f8..b84fa8022 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -20,7 +20,7 @@ export const useNamespace = (namespaceName?: string, instrumentationLabeled = nu addNotification({ type: NOTIFICATION_TYPE.ERROR, title: error.name || ACTION.FETCH, - message: error.message, + message: error.cause?.message || error.message, }), }); diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 31eeb1c34..f571f65cd 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -15,13 +15,14 @@ export const useDestinationCRUD = (params?: Params) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.DESTINATION) : undefined, + hideFromHistory, }); }; @@ -57,15 +58,15 @@ export const useDestinationCRUD = (params?: Params) => { destinations: data?.computePlatform?.destinations || [], createDestination: (destination: DestinationInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating destination...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating destination...', undefined, true); createDestination({ variables: { destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, updateDestination: (id: string, destination: DestinationInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating destination...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating destination...', undefined, true); updateDestination({ variables: { id, destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, deleteDestination: (id: string) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting destination...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting destination...', undefined, true); deleteDestination({ variables: { id } }); }, }; diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 1e230085b..916bece0d 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -105,7 +105,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp addNotification({ type: NOTIFICATION_TYPE.ERROR, title: error.name || ACTION.FETCH, - message: error.message, + message: error.cause?.message || error.message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, }), }); @@ -171,6 +171,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 6c8718365..fbfbce747 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -15,13 +15,14 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.RULE, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.RULE) : undefined, + hideFromHistory, }); }; @@ -57,15 +58,15 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { instrumentationRules: data?.computePlatform?.instrumentationRules || [], createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating instrumentation rule...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating instrumentation rule...', undefined, true); createInstrumentationRule({ variables: { instrumentationRule } }); }, updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating instrumentation rule...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating instrumentation rule...', undefined, true); updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }); }, deleteInstrumentationRule: (ruleId: string) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting instrumentation rule...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting instrumentation rule...', undefined, true); deleteInstrumentationRule({ variables: { ruleId } }); }, }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts index 20d14e7df..e430d7ff7 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts @@ -45,6 +45,7 @@ export function useInstrumentationRuleFormData() { type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts index dc69a0e92..55e9e24be 100644 --- a/frontend/webapp/hooks/notification/useSSE.ts +++ b/frontend/webapp/hooks/notification/useSSE.ts @@ -4,18 +4,6 @@ import { NOTIFICATION_TYPE } from '@/types'; import { useComputePlatform } from '../compute-platform'; import { type NotifyPayload, useConnectionStore, useNotificationStore } from '@/store'; -const modifyType = (notification: NotifyPayload) => { - if (notification.title === 'Modified') { - if (notification.message?.indexOf('ProcessTerminated') === 0 || notification.message?.indexOf('NoHeartbeat') === 0 || notification.message?.indexOf('Failed') === 0) { - return NOTIFICATION_TYPE.ERROR; - } else { - return NOTIFICATION_TYPE.INFO; - } - } - - return notification.type; -}; - export const useSSE = () => { const { addNotification } = useNotificationStore(); const { setConnectionStore } = useConnectionStore(); @@ -40,8 +28,6 @@ export const useSSE = () => { target: data.target, }; - // notification.type = modifyType(notification); - // Dispatch the notification to the store addNotification(notification); refetchComputePlatform(); diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 48b79bd92..007924a16 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -31,13 +31,14 @@ export const useSourceCRUD = (params?: Params) => { } }, [refetch]); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.SOURCE, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE) : undefined, + hideFromHistory, }); }; @@ -106,16 +107,16 @@ export const useSourceCRUD = (params?: Params) => { sources: data?.computePlatform.k8sActualSources || [], createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating sources...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating sources...', undefined, true); await persistNamespaces(futureSelectAppsList); await persistSources(selectAppsList, true); }, updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating sources...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating sources...', undefined, true); await updateSource({ variables: { sourceId, patchSourceRequest } }); }, deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting sources...'); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting sources...', undefined, true); await persistSources(selectAppsList, false); }, }; diff --git a/frontend/webapp/types/common.ts b/frontend/webapp/types/common.ts index 5fa10761a..c0254c292 100644 --- a/frontend/webapp/types/common.ts +++ b/frontend/webapp/types/common.ts @@ -29,6 +29,7 @@ export interface Notification { target?: string; dismissed: boolean; seen: boolean; + hideFromHistory?: boolean; time: string; } From b3d0bbf5c0ec2a943e95707f9d17071e3297d668 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 12:44:28 +0200 Subject: [PATCH 158/259] refactor: update role permissions and resource names for clarity and consistency --- cli/cmd/resources/README.md | 77 ++++++++++++----------- cli/cmd/resources/ui.go | 16 ++--- helm/odigos/templates/ui/clusterrole.yaml | 14 ++++- 3 files changed, 59 insertions(+), 48 deletions(-) diff --git a/cli/cmd/resources/README.md b/cli/cmd/resources/README.md index 37beeaf66..b6bb7d7be 100644 --- a/cli/cmd/resources/README.md +++ b/cli/cmd/resources/README.md @@ -4,38 +4,40 @@ In this doc, we'll keep track of the permissions requested across different reso ## ClusterRole -| Component | APIGroups | Resources | Verbs | Comments | -| ------------ | --------- | -------------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------- | -| Odiglet | "" | pods | get, list, watch | Needed for language detection. | -| Odiglet | "" | pods/status | get | Needed for language detection. | -| Odiglet | "" | nodes | get, list, watch | Needed for virtual device registration. | -| Odiglet | apps | deployments, daemonsets, statefulsets | get, list, watch | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | apps | deployments/status, daemonsets/status, statefulsets/status | get | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | apps | replicasets | get | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | odigos.io | instrumentedapplications | get, list, watch, create, patch, update | Stores runtime details and language detection (temporary until migration to configs). | -| Odiglet | odigos.io | instrumentedapplications/status | get, patch, update | Updates status of instrumented applications. | -| Odiglet | odigos.io | instrumentationinstances | create, get, list, patch, update, watch, delete | Manages instrumentation instances for process state storage. | -| Odiglet | odigos.io | instrumentationinstances/status | get, patch, update | Updates status of instrumentation instances. | -| Odiglet | odigos.io | instrumentationconfigs | get, list, watch, patch, update | Manages instrumentation configurations (future update for runtime details storage). | -| Odiglet | odigos.io | instrumentationconfigs/status | get, patch, update | Updates status of instrumentation configurations. | -| Odiglet | policy | podsecuritypolicies | use | Allows using privileged PSP (if enabled). | -| Instrumentor | "" | nodes, namespaces | get, list, watch | Tracks runtime detection and resource labels for instrumentation. | -| Instrumentor | apps | daemonsets, deployments, statefulsets | get, list, watch, update, patch | Adjusts pod specifications for instrumentation. | -| Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Reacts to runtime detections in workloads. | -| Instrumentor | odigos.io | instrumentedapplications/status | get, patch, update | Updates application statuses post-injection. | -| Instrumentor | odigos.io | instrumentationconfigs | create, delete, get, list, patch, update, watch | Manages instrumentation configurations. | -| Scheduler | odigos.io | instrumentationconfigs | get, list, watch | Monitors changes in instrumentation configurations for scheduling updates. | -| Autoscaler | odigos.io | instrumentationconfigs | get, list, watch | Reads instrumentation configurations to populate the `data-collector` configmaps. | -| Collector | "" | nodes/stats, nodes/proxy | get, list | Retrieves metrics for telemetry purposes. | -| Collector | "" | pods | get, list | Accesses metadata for resource name processors. | -| Collector | apps | replicasets, deployments, daemonsets, statefulsets | get, list | Fetches application details for instrumentation. | -| Collector | "" | endpoints | get, list, watch | Needed for load balancer. | -| Collector | policy | podsecuritypolicies | use | Supports clients enabling pod security policies (optional). | -| UI | "" | namespaces | get, list, patch | Required to retrieve and modify namespace configurations during instrumentation. | -| UI | "" | services, pods | get, list | Required for discovering potential destinations and describing application workloads. | -| UI | apps | deployments, statefulsets, daemonsets | get, list, patch, update | Needed for application instrumentation. | -| UI | apps | replicasets | get, list | Used for describing source and application configurations. | -| UI | odigos.io | instrumentedapplications, instrumentationinstances, instrumentationconfigs | get, list, watch | Used to retrieve and monitor instrumented applications and configurations. | +| Component | APIGroups | Resources | Verbs | Comments | +| ------------ | ----------------- | ---------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------- | +| Odiglet | "" | pods | get, list, watch | Needed for language detection. | +| Odiglet | "" | pods/status | get | Needed for language detection. | +| Odiglet | "" | nodes | get, list, watch | Needed for virtual device registration. | +| Odiglet | apps | deployments, daemonsets, statefulsets | get, list, watch | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | apps | deployments/status, daemonsets/status, statefulsets/status | get | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | apps | replicasets | get | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | odigos.io | instrumentedapplications | get, list, watch, create, patch, update | Stores runtime details and language detection (temporary until migration to configs). | +| Odiglet | odigos.io | instrumentedapplications/status | get, patch, update | Updates status of instrumented applications. | +| Odiglet | odigos.io | instrumentationinstances | create, get, list, patch, update, watch, delete | Manages instrumentation instances for process state storage. | +| Odiglet | odigos.io | instrumentationinstances/status | get, patch, update | Updates status of instrumentation instances. | +| Odiglet | odigos.io | instrumentationconfigs | get, list, watch, patch, update | Manages instrumentation configurations (future update for runtime details storage). | +| Odiglet | odigos.io | instrumentationconfigs/status | get, patch, update | Updates status of instrumentation configurations. | +| Odiglet | policy | podsecuritypolicies | use | Allows using privileged PSP (if enabled). | +| Instrumentor | "" | nodes, namespaces | get, list, watch | Tracks runtime detection and resource labels for instrumentation. | +| Instrumentor | apps | daemonsets, deployments, statefulsets | get, list, watch, update, patch | Adjusts pod specifications for instrumentation. | +| Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Reacts to runtime detections in workloads. | +| Instrumentor | odigos.io | instrumentedapplications/status | get, patch, update | Updates application statuses post-injection. | +| Instrumentor | odigos.io | instrumentationconfigs | create, delete, get, list, patch, update, watch | Manages instrumentation configurations. | +| Scheduler | odigos.io | instrumentationconfigs | get, list, watch | Monitors changes in instrumentation configurations for scheduling updates. | +| Autoscaler | odigos.io | instrumentationconfigs | get, list, watch | Reads instrumentation configurations to populate the `data-collector` configmaps. | +| Collector | "" | nodes/stats, nodes/proxy | get, list | Retrieves metrics for telemetry purposes. | +| Collector | "" | pods | get, list | Accesses metadata for resource name processors. | +| Collector | apps | replicasets, deployments, daemonsets, statefulsets | get, list | Fetches application details for instrumentation. | +| Collector | "" | endpoints | get, list, watch | Needed for load balancer. | +| Collector | policy | podsecuritypolicies | use | Supports clients enabling pod security policies (optional). | +| UI | "" | namespaces | get, list, patch | Needed to retrieve and instrument namespaces. | +| UI | "" | services, pods | get, list | Required for identifying and describing sources and potential destinations. | +| UI | apps | deployments, statefulsets, daemonsets | get, list, patch, update | Required for instrumentation of application sources. | +| UI | apps | replicasets | get, list | Needed for describing application and resource relationships. | +| UI | odigos.io | instrumentationconfigs, instrumentationinstances | get, list, watch | Monitors and retrieves configurations for instrumentation. | +| UI | odigos.io | sources | get, list, create, delete | Manages sources for instrumentation and monitoring. | +| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Handles pipeline actions for custom logic. | --- @@ -56,8 +58,9 @@ In this doc, we'll keep track of the permissions requested across different reso | Autoscaler | autoscaling | horizontalpodautoscalers | create, patch, update, delete | Implements autoscaling for gateway collectors. | | Autoscaler | odigos.io | destinations | get, list, watch | Tracks and synchronizes destination configurations. | | Autoscaler | odigos.io | collectorsgroups, destinations/status | get, patch, update | Monitors and updates statuses of collectors groups and destinations. | -| UI | "" | configmaps | get, list | Accesses `odigos-config` for UI configuration settings. | -| UI | "" | secrets | get, list, create, patch, update | Manages destination secrets. | -| UI | odigos.io | instrumentationrules, destinations | get, list, create, patch, update, delete, watch | CRUD operations for destinations and instrumentation rules. | -| UI | odigos.io | collectorsgroups | get, list | Monitors instrumentation groups. | -| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Handles pipeline actions for custom logic. | +| UI | "" | configmaps | get, list | Reads `odigos-config` for UI configuration. | +| UI | "" | secrets | get, list, create, patch, update | Manages destination secrets for configurations. | +| UI | odigos.io | instrumentationrules, destinations | get, list, create, patch, update, delete | CRUD for destinations and instrumentation rules. | +| UI | odigos.io | collectorsgroups | get, list | Monitors groupings of collectors for UI updates. | +| UI | odigos.io | sources | get, list, create, delete | Manages sources for instrumentation and monitoring. | +| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Pipeline action management. | diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index 5d0fbdab6..f87d7ee84 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -150,12 +150,12 @@ func NewUIRole(ns string) *rbacv1.Role { Resources: []string{"secrets"}, Verbs: []string{"get", "list", "create", "patch", "update"}, }, - { // Needed for CRUD on Odigos entities + { // Needed for CRUD on instr. rule and destinations APIGroups: []string{"odigos.io"}, Resources: []string{"instrumentationrules", "destinations"}, Verbs: []string{"get", "list", "create", "patch", "update", "delete"}, }, - { // Needed to watch Odigos entities + { // Needed to notify UI about changes with destinations APIGroups: []string{"odigos.io"}, Resources: []string{"destinations"}, Verbs: []string{"watch"}, @@ -165,7 +165,7 @@ func NewUIRole(ns string) *rbacv1.Role { Resources: []string{"collectorsgroups"}, Verbs: []string{"get", "list"}, }, - { // Needed for CRUD on Pipeline Actions + { // Needed for CRUD on pipeline actions APIGroups: []string{"actions.odigos.io"}, Resources: []string{"*"}, Verbs: []string{"get", "list", "create", "patch", "update", "delete"}, @@ -214,7 +214,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Resources: []string{"namespaces"}, Verbs: []string{"get", "list", "patch"}, }, - { // Needed to instrument applications + { // Needed to get and instrument sources APIGroups: []string{"apps"}, Resources: []string{"deployments", "statefulsets", "daemonsets"}, Verbs: []string{"get", "list", "patch", "update"}, @@ -230,17 +230,17 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Resources: []string{"services", "pods"}, Verbs: []string{"get", "list"}, }, - { // Needed to read Odigos entities + { // Needed to get sources APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentationinstances", "instrumentationconfigs"}, + Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, Verbs: []string{"get", "list"}, }, - { // Needed to instrument / uninstrument applications + { // Needed to instrument / uninstrument sources APIGroups: []string{"odigos.io"}, Resources: []string{"sources"}, Verbs: []string{"get", "list", "create", "delete"}, }, - { // Needed to watch instrumented applications + { // Needed to notify UI about changes with sources APIGroups: []string{"odigos.io"}, Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, Verbs: []string{"watch"}, diff --git a/helm/odigos/templates/ui/clusterrole.yaml b/helm/odigos/templates/ui/clusterrole.yaml index db40122df..75eaf295a 100644 --- a/helm/odigos/templates/ui/clusterrole.yaml +++ b/helm/odigos/templates/ui/clusterrole.yaml @@ -40,16 +40,24 @@ rules: - apiGroups: - odigos.io resources: - - instrumentedapplications - - instrumentationinstances - instrumentationconfigs + - instrumentationinstances verbs: - get - list - apiGroups: - odigos.io resources: - - instrumentedapplications + - sources + verbs: + - get + - list + - create + - delete + - apiGroups: + - odigos.io + resources: + - instrumentationconfigs - instrumentationinstances verbs: - watch From e5b838bf176b57b8a8b697c73390dae8e60b6a5b Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:01:37 +0200 Subject: [PATCH 159/259] cleanup --- .../webapp/hooks/sources/useSourceCRUD.ts | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 007924a16..84a6b283e 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -1,4 +1,3 @@ -import { useCallback } from 'react'; import { useMutation } from '@apollo/client'; import { ACTION, getSseTargetFromId } from '@/utils'; import { useAppStore, useNotificationStore } from '@/store'; @@ -19,18 +18,6 @@ export const useSourceCRUD = (params?: Params) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const startPolling = useCallback(async () => { - let retries = 0; - const maxRetries = 5; - const retryInterval = 1 * 1000; // time in milliseconds - - while (retries < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, retryInterval)); - refetch(); - retries++; - } - }, [refetch]); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { addNotification({ type, @@ -42,14 +29,14 @@ export const useSourceCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string) => { + const handleComplete = (actionType: string) => { refetch(); - params?.onSuccess?.(title); + params?.onSuccess?.(actionType); }; const [createOrDeleteSources, cdState] = useMutation<{ persistK8sSources: boolean }>(PERSIST_SOURCE, { From 15f8c53c9ec7d18bb286ca23e14f2f1e822f0537 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:01:44 +0200 Subject: [PATCH 160/259] feat: enhance notification messages for action and instrumentation rule CRUD operations --- .../webapp/hooks/actions/useActionCRUD.ts | 18 +++++++----- .../useInstrumentationRuleCRUD.ts | 28 +++++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index ae79d508f..f10b84b88 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -31,25 +31,32 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { params?.onError?.(actionType); }; - const handleComplete = (actionType: string) => { + const handleComplete = (actionType: string, message: string, id?: string) => { refetch(); + notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); params?.onSuccess?.(actionType); }; const [createAction, cState] = useMutation<{ createAction: { id: string } }>(CREATE_ACTION, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: () => handleComplete(ACTION.CREATE), + onCompleted: (res, req) => { + const id = res?.createAction?.id; + handleComplete(ACTION.CREATE, `Action "${id}" created`, id); + }, }); const [updateAction, uState] = useMutation<{ updateAction: { id: string } }>(UPDATE_ACTION, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: () => handleComplete(ACTION.UPDATE), + onCompleted: (res, req) => { + const id = res?.updateAction?.id; + handleComplete(ACTION.UPDATE, `Action "${id}" updated`, id); + }, }); const [deleteAction, dState] = useMutation<{ deleteAction: boolean }>(DELETE_ACTION, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.id; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.ACTION)); - handleComplete(ACTION.DELETE); + handleComplete(ACTION.DELETE, `Action "${id}" deleted`, id); }, }); @@ -58,15 +65,12 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { actions: data?.computePlatform?.actions || [], createAction: (action: ActionInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating pipeline action...', undefined, true); createAction({ variables: { action } }); }, updateAction: (id: string, action: ActionInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating pipeline action...', undefined, true); updateAction({ variables: { id, action } }); }, deleteAction: (id: string, actionType: ActionsType) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting pipeline action...', undefined, true); deleteAction({ variables: { id, actionType } }); }, }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index fbfbce747..230548285 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -1,6 +1,6 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; -import { ACTION, getSseTargetFromId } from '@/utils'; +import { ACTION, deriveTypeFromRule, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput } from '@/types'; import { CREATE_INSTRUMENTATION_RULE, UPDATE_INSTRUMENTATION_RULE, DELETE_INSTRUMENTATION_RULE } from '@/graphql/mutations'; @@ -26,30 +26,37 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { }); }; - const handleError = (title: string, message: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string) => { + const handleComplete = (actionType: string, message: string, id?: string) => { refetch(); - params?.onSuccess?.(title); + notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); + params?.onSuccess?.(actionType); }; const [createInstrumentationRule, cState] = useMutation<{ createInstrumentationRule: { ruleId: string } }>(CREATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: () => handleComplete(ACTION.CREATE), + onCompleted: (res, req) => { + const id = res?.createInstrumentationRule?.ruleId; + handleComplete(ACTION.CREATE, `Rule "${id}" created`, id); + }, }); const [updateInstrumentationRule, uState] = useMutation<{ updateInstrumentationRule: { ruleId: string } }>(UPDATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: () => handleComplete(ACTION.UPDATE), + onCompleted: (res, req) => { + const id = res?.updateInstrumentationRule?.ruleId; + handleComplete(ACTION.UPDATE, `Rule "${id}" updated`, id); + }, }); const [deleteInstrumentationRule, dState] = useMutation<{ deleteInstrumentationRule: boolean }>(DELETE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.ruleId; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.RULE)); - handleComplete(ACTION.DELETE); + handleComplete(ACTION.DELETE, `Rule "${id}" deleted`, id); }, }); @@ -58,15 +65,12 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { instrumentationRules: data?.computePlatform?.instrumentationRules || [], createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating instrumentation rule...', undefined, true); createInstrumentationRule({ variables: { instrumentationRule } }); }, updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating instrumentation rule...', undefined, true); updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }); }, deleteInstrumentationRule: (ruleId: string) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting instrumentation rule...', undefined, true); deleteInstrumentationRule({ variables: { ruleId } }); }, }; From bcac4b6d518e1048655456130090d6cf41e9a5fe Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:02:09 +0200 Subject: [PATCH 161/259] cleanup --- .../hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 230548285..6a6b5aa03 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -1,6 +1,6 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; -import { ACTION, deriveTypeFromRule, getSseTargetFromId } from '@/utils'; +import { ACTION, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput } from '@/types'; import { CREATE_INSTRUMENTATION_RULE, UPDATE_INSTRUMENTATION_RULE, DELETE_INSTRUMENTATION_RULE } from '@/graphql/mutations'; From a103bd7c858d4a3a5fdc8e9151e90b8b27c9abc6 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:17:05 +0200 Subject: [PATCH 162/259] refactor: remove instr. app from describe source --- frontend/graph/generated.go | 445 +++++------------- frontend/graph/model/models_gen.go | 32 +- frontend/graph/schema.graphqls | 18 +- .../source_describe/source_describe.go | 8 +- frontend/webapp/graphql/queries/describe.ts | 16 +- frontend/webapp/types/describe.ts | 10 +- k8sutils/pkg/describe/source.go | 20 +- k8sutils/pkg/describe/source/analyze.go | 61 +-- 8 files changed, 151 insertions(+), 459 deletions(-) diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index 22cd3662b..a07f00471 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -217,6 +217,7 @@ type ComplexityRoot struct { } InstrumentationConfigAnalyze struct { + Containers func(childComplexity int) int CreateTime func(childComplexity int) int Created func(childComplexity int) int } @@ -255,12 +256,6 @@ type ComplexityRoot struct { Workloads func(childComplexity int) int } - InstrumentedApplicationAnalyze struct { - Containers func(childComplexity int) int - CreateTime func(childComplexity int) int - Created func(childComplexity int) int - } - K8sActualNamespace struct { InstrumentationLabelEnabled func(childComplexity int) int K8sActualSources func(childComplexity int, instrumentationLabeled *bool) int @@ -434,17 +429,16 @@ type ComplexityRoot struct { } SourceAnalyze struct { - InstrumentationConfig func(childComplexity int) int - InstrumentationDevice func(childComplexity int) int - InstrumentedApplication func(childComplexity int) int - Kind func(childComplexity int) int - Labels func(childComplexity int) int - Name func(childComplexity int) int - Namespace func(childComplexity int) int - Pods func(childComplexity int) int - PodsPhasesCount func(childComplexity int) int - RuntimeInfo func(childComplexity int) int - TotalPods func(childComplexity int) int + InstrumentationConfig func(childComplexity int) int + InstrumentationDevice func(childComplexity int) int + Kind func(childComplexity int) int + Labels func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + Pods func(childComplexity int) int + PodsPhasesCount func(childComplexity int) int + RuntimeInfo func(childComplexity int) int + TotalPods func(childComplexity int) int } SourceContainerRuntimeDetails struct { @@ -1214,6 +1208,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.HttpPayloadCollection.MimeTypes(childComplexity), true + case "InstrumentationConfigAnalyze.containers": + if e.complexity.InstrumentationConfigAnalyze.Containers == nil { + break + } + + return e.complexity.InstrumentationConfigAnalyze.Containers(childComplexity), true + case "InstrumentationConfigAnalyze.createTime": if e.complexity.InstrumentationConfigAnalyze.CreateTime == nil { break @@ -1361,27 +1362,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationRule.Workloads(childComplexity), true - case "InstrumentedApplicationAnalyze.containers": - if e.complexity.InstrumentedApplicationAnalyze.Containers == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.Containers(childComplexity), true - - case "InstrumentedApplicationAnalyze.createTime": - if e.complexity.InstrumentedApplicationAnalyze.CreateTime == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.CreateTime(childComplexity), true - - case "InstrumentedApplicationAnalyze.created": - if e.complexity.InstrumentedApplicationAnalyze.Created == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.Created(childComplexity), true - case "K8sActualNamespace.instrumentationLabelEnabled": if e.complexity.K8sActualNamespace.InstrumentationLabelEnabled == nil { break @@ -2239,13 +2219,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SourceAnalyze.InstrumentationDevice(childComplexity), true - case "SourceAnalyze.instrumentedApplication": - if e.complexity.SourceAnalyze.InstrumentedApplication == nil { - break - } - - return e.complexity.SourceAnalyze.InstrumentedApplication(childComplexity), true - case "SourceAnalyze.kind": if e.complexity.SourceAnalyze.Kind == nil { break @@ -7550,6 +7523,60 @@ func (ec *executionContext) fieldContext_InstrumentationConfigAnalyze_createTime return fc, nil } +func (ec *executionContext) _InstrumentationConfigAnalyze_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationConfigAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationConfigAnalyze_containers(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Containers, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ContainerRuntimeInfoAnalyze) + fc.Result = res + return ec.marshalNContainerRuntimeInfoAnalyze2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐContainerRuntimeInfoAnalyzeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_InstrumentationConfigAnalyze_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "InstrumentationConfigAnalyze", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "containerName": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_containerName(ctx, field) + case "language": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_language(ctx, field) + case "runtimeVersion": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_runtimeVersion(ctx, field) + case "envVars": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_envVars(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ContainerRuntimeInfoAnalyze", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _InstrumentationDeviceAnalyze_statusText(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationDeviceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_InstrumentationDeviceAnalyze_statusText(ctx, field) if err != nil { @@ -8464,165 +8491,6 @@ func (ec *executionContext) fieldContext_InstrumentationRule_payloadCollection(_ return fc, nil } -func (ec *executionContext) _InstrumentedApplicationAnalyze_created(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_created(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Created, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.EntityProperty) - fc.Result = res - return ec.marshalNEntityProperty2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐEntityProperty(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_created(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_EntityProperty_name(ctx, field) - case "value": - return ec.fieldContext_EntityProperty_value(ctx, field) - case "status": - return ec.fieldContext_EntityProperty_status(ctx, field) - case "explain": - return ec.fieldContext_EntityProperty_explain(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationAnalyze_createTime(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_createTime(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.CreateTime, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*model.EntityProperty) - fc.Result = res - return ec.marshalOEntityProperty2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐEntityProperty(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_createTime(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_EntityProperty_name(ctx, field) - case "value": - return ec.fieldContext_EntityProperty_value(ctx, field) - case "status": - return ec.fieldContext_EntityProperty_status(ctx, field) - case "explain": - return ec.fieldContext_EntityProperty_explain(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationAnalyze_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_containers(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Containers, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.ContainerRuntimeInfoAnalyze) - fc.Result = res - return ec.marshalNContainerRuntimeInfoAnalyze2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐContainerRuntimeInfoAnalyzeᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "containerName": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_containerName(ctx, field) - case "language": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_language(ctx, field) - case "runtimeVersion": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_runtimeVersion(ctx, field) - case "envVars": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_envVars(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ContainerRuntimeInfoAnalyze", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualNamespace_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualNamespace_name(ctx, field) if err != nil { @@ -13201,12 +13069,10 @@ func (ec *executionContext) fieldContext_Query_describeSource(ctx context.Contex return ec.fieldContext_SourceAnalyze_namespace(ctx, field) case "labels": return ec.fieldContext_SourceAnalyze_labels(ctx, field) - case "instrumentationConfig": - return ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) case "runtimeInfo": return ec.fieldContext_SourceAnalyze_runtimeInfo(ctx, field) - case "instrumentedApplication": - return ec.fieldContext_SourceAnalyze_instrumentedApplication(ctx, field) + case "instrumentationConfig": + return ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) case "instrumentationDevice": return ec.fieldContext_SourceAnalyze_instrumentationDevice(ctx, field) case "totalPods": @@ -14340,56 +14206,6 @@ func (ec *executionContext) fieldContext_SourceAnalyze_labels(_ context.Context, return fc, nil } -func (ec *executionContext) _SourceAnalyze_instrumentationConfig(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.InstrumentationConfig, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.InstrumentationConfigAnalyze) - fc.Result = res - return ec.marshalNInstrumentationConfigAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationConfigAnalyze(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_SourceAnalyze_instrumentationConfig(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "SourceAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "created": - return ec.fieldContext_InstrumentationConfigAnalyze_created(ctx, field) - case "createTime": - return ec.fieldContext_InstrumentationConfigAnalyze_createTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationConfigAnalyze", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _SourceAnalyze_runtimeInfo(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SourceAnalyze_runtimeInfo(ctx, field) if err != nil { @@ -14411,11 +14227,14 @@ func (ec *executionContext) _SourceAnalyze_runtimeInfo(ctx context.Context, fiel return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*model.RuntimeInfoAnalyze) fc.Result = res - return ec.marshalORuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx, field.Selections, res) + return ec.marshalNRuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_SourceAnalyze_runtimeInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -14437,8 +14256,8 @@ func (ec *executionContext) fieldContext_SourceAnalyze_runtimeInfo(_ context.Con return fc, nil } -func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceAnalyze_instrumentedApplication(ctx, field) +func (ec *executionContext) _SourceAnalyze_instrumentationConfig(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) if err != nil { return graphql.Null } @@ -14451,7 +14270,7 @@ func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.InstrumentedApplication, nil + return obj.InstrumentationConfig, nil }) if err != nil { ec.Error(ctx, err) @@ -14463,12 +14282,12 @@ func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.C } return graphql.Null } - res := resTmp.(*model.InstrumentedApplicationAnalyze) + res := resTmp.(*model.InstrumentationConfigAnalyze) fc.Result = res - return ec.marshalNInstrumentedApplicationAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationAnalyze(ctx, field.Selections, res) + return ec.marshalNInstrumentationConfigAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationConfigAnalyze(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SourceAnalyze_instrumentedApplication(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SourceAnalyze_instrumentationConfig(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "SourceAnalyze", Field: field, @@ -14477,13 +14296,13 @@ func (ec *executionContext) fieldContext_SourceAnalyze_instrumentedApplication(_ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "created": - return ec.fieldContext_InstrumentedApplicationAnalyze_created(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_created(ctx, field) case "createTime": - return ec.fieldContext_InstrumentedApplicationAnalyze_createTime(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_createTime(ctx, field) case "containers": - return ec.fieldContext_InstrumentedApplicationAnalyze_containers(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_containers(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type InstrumentedApplicationAnalyze", field.Name) + return nil, fmt.Errorf("no field named %q was found under type InstrumentationConfigAnalyze", field.Name) }, } return fc, nil @@ -19220,6 +19039,11 @@ func (ec *executionContext) _InstrumentationConfigAnalyze(ctx context.Context, s } case "createTime": out.Values[i] = ec._InstrumentationConfigAnalyze_createTime(ctx, field, obj) + case "containers": + out.Values[i] = ec._InstrumentationConfigAnalyze_containers(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -19472,52 +19296,6 @@ func (ec *executionContext) _InstrumentationRule(ctx context.Context, sel ast.Se return out } -var instrumentedApplicationAnalyzeImplementors = []string{"InstrumentedApplicationAnalyze"} - -func (ec *executionContext) _InstrumentedApplicationAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentedApplicationAnalyze) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentedApplicationAnalyzeImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentedApplicationAnalyze") - case "created": - out.Values[i] = ec._InstrumentedApplicationAnalyze_created(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "createTime": - out.Values[i] = ec._InstrumentedApplicationAnalyze_createTime(ctx, field, obj) - case "containers": - out.Values[i] = ec._InstrumentedApplicationAnalyze_containers(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var k8sActualNamespaceImplementors = []string{"K8sActualNamespace"} func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.SelectionSet, obj *model.K8sActualNamespace) graphql.Marshaler { @@ -20929,15 +20707,13 @@ func (ec *executionContext) _SourceAnalyze(ctx context.Context, sel ast.Selectio if out.Values[i] == graphql.Null { out.Invalids++ } - case "instrumentationConfig": - out.Values[i] = ec._SourceAnalyze_instrumentationConfig(ctx, field, obj) + case "runtimeInfo": + out.Values[i] = ec._SourceAnalyze_runtimeInfo(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "runtimeInfo": - out.Values[i] = ec._SourceAnalyze_runtimeInfo(ctx, field, obj) - case "instrumentedApplication": - out.Values[i] = ec._SourceAnalyze_instrumentedApplication(ctx, field, obj) + case "instrumentationConfig": + out.Values[i] = ec._SourceAnalyze_instrumentationConfig(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -22285,16 +22061,6 @@ func (ec *executionContext) unmarshalNInstrumentationRuleInput2githubᚗcomᚋod return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNInstrumentedApplicationAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentedApplicationAnalyze) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentedApplicationAnalyze(ctx, sel, v) -} - func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { res, err := graphql.UnmarshalInt(v) return res, graphql.ErrorOnPath(ctx, err) @@ -22652,6 +22418,16 @@ func (ec *executionContext) unmarshalNPodWorkloadInput2ᚖgithubᚗcomᚋodigos return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNRuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.RuntimeInfoAnalyze) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._RuntimeInfoAnalyze(ctx, sel, v) +} + func (ec *executionContext) unmarshalNSignalType2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSignalType(ctx context.Context, v interface{}) (model.SignalType, error) { var res model.SignalType err := res.UnmarshalGQL(v) @@ -23547,13 +23323,6 @@ func (ec *executionContext) marshalOProgrammingLanguage2ᚖgithubᚗcomᚋodigos return v } -func (ec *executionContext) marshalORuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.RuntimeInfoAnalyze) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._RuntimeInfoAnalyze(ctx, sel, v) -} - func (ec *executionContext) marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.SourceContainerRuntimeDetails) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 6e4b07780..9bef3d092 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -241,8 +241,9 @@ type HTTPPayloadCollectionInput struct { } type InstrumentationConfigAnalyze struct { - Created *EntityProperty `json:"created"` - CreateTime *EntityProperty `json:"createTime,omitempty"` + Created *EntityProperty `json:"created"` + CreateTime *EntityProperty `json:"createTime,omitempty"` + Containers []*ContainerRuntimeInfoAnalyze `json:"containers"` } type InstrumentationDeviceAnalyze struct { @@ -294,12 +295,6 @@ type InstrumentationRuleInput struct { PayloadCollection *PayloadCollectionInput `json:"payloadCollection,omitempty"` } -type InstrumentedApplicationAnalyze struct { - Created *EntityProperty `json:"created"` - CreateTime *EntityProperty `json:"createTime,omitempty"` - Containers []*ContainerRuntimeInfoAnalyze `json:"containers"` -} - type K8sActualNamespace struct { Name string `json:"name"` InstrumentationLabelEnabled *bool `json:"instrumentationLabelEnabled,omitempty"` @@ -568,17 +563,16 @@ type SingleSourceMetricsResponse struct { } type SourceAnalyze struct { - Name *EntityProperty `json:"name"` - Kind *EntityProperty `json:"kind"` - Namespace *EntityProperty `json:"namespace"` - Labels *InstrumentationLabelsAnalyze `json:"labels"` - InstrumentationConfig *InstrumentationConfigAnalyze `json:"instrumentationConfig"` - RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo,omitempty"` - InstrumentedApplication *InstrumentedApplicationAnalyze `json:"instrumentedApplication"` - InstrumentationDevice *InstrumentationDeviceAnalyze `json:"instrumentationDevice"` - TotalPods int `json:"totalPods"` - PodsPhasesCount string `json:"podsPhasesCount"` - Pods []*PodAnalyze `json:"pods"` + Name *EntityProperty `json:"name"` + Kind *EntityProperty `json:"kind"` + Namespace *EntityProperty `json:"namespace"` + Labels *InstrumentationLabelsAnalyze `json:"labels"` + RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` + InstrumentationConfig *InstrumentationConfigAnalyze `json:"instrumentationConfig"` + InstrumentationDevice *InstrumentationDeviceAnalyze `json:"instrumentationDevice"` + TotalPods int `json:"totalPods"` + PodsPhasesCount string `json:"podsPhasesCount"` + Pods []*PodAnalyze `json:"pods"` } type SourceContainerRuntimeDetails struct { diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index f2fb30fa6..76be28369 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -447,11 +447,6 @@ type InstrumentationLabelsAnalyze { instrumentedText: EntityProperty } -type InstrumentationConfigAnalyze { - created: EntityProperty! - createTime: EntityProperty -} - type ContainerRuntimeInfoAnalyze { containerName: EntityProperty! language: EntityProperty! @@ -459,14 +454,14 @@ type ContainerRuntimeInfoAnalyze { envVars: [EntityProperty!]! } -type RuntimeInfoAnalyze { - generation: EntityProperty! +type InstrumentationConfigAnalyze { + created: EntityProperty! + createTime: EntityProperty containers: [ContainerRuntimeInfoAnalyze!]! } -type InstrumentedApplicationAnalyze { - created: EntityProperty! - createTime: EntityProperty +type RuntimeInfoAnalyze { + generation: EntityProperty! containers: [ContainerRuntimeInfoAnalyze!]! } @@ -506,9 +501,8 @@ type SourceAnalyze { namespace: EntityProperty! labels: InstrumentationLabelsAnalyze! + runtimeInfo: RuntimeInfoAnalyze! instrumentationConfig: InstrumentationConfigAnalyze! - runtimeInfo: RuntimeInfoAnalyze - instrumentedApplication: InstrumentedApplicationAnalyze! instrumentationDevice: InstrumentationDeviceAnalyze! totalPods: Int! diff --git a/frontend/services/describe/source_describe/source_describe.go b/frontend/services/describe/source_describe/source_describe.go index b422e9836..329ffc76c 100644 --- a/frontend/services/describe/source_describe/source_describe.go +++ b/frontend/services/describe/source_describe/source_describe.go @@ -50,15 +50,11 @@ func ConvertSourceAnalyzeToGQL(analyze *source.SourceAnalyze) *model.SourceAnaly Namespace: describe_utils.ConvertEntityPropertyToGQL(analyze.Labels.Namespace), InstrumentedText: describe_utils.ConvertEntityPropertyToGQL(&analyze.Labels.InstrumentedText), }, + RuntimeInfo: convertRuntimeInfoToGQL(analyze.RuntimeInfo), InstrumentationConfig: &model.InstrumentationConfigAnalyze{ Created: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentationConfig.Created), CreateTime: describe_utils.ConvertEntityPropertyToGQL(analyze.InstrumentationConfig.CreateTime), - }, - RuntimeInfo: convertRuntimeInfoToGQL(analyze.RuntimeInfo), - InstrumentedApplication: &model.InstrumentedApplicationAnalyze{ - Created: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentedApplication.Created), - CreateTime: describe_utils.ConvertEntityPropertyToGQL(analyze.InstrumentedApplication.CreateTime), - Containers: convertRuntimeInfoContainersToGQL(analyze.InstrumentedApplication.Containers), + Containers: convertRuntimeInfoContainersToGQL(analyze.InstrumentationConfig.Containers), }, InstrumentationDevice: &model.InstrumentationDeviceAnalyze{ StatusText: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentationDevice.StatusText), diff --git a/frontend/webapp/graphql/queries/describe.ts b/frontend/webapp/graphql/queries/describe.ts index 1280a312e..cfd09670e 100644 --- a/frontend/webapp/graphql/queries/describe.ts +++ b/frontend/webapp/graphql/queries/describe.ts @@ -188,20 +188,6 @@ export const DESCRIBE_SOURCE = gql` explain } } - instrumentationConfig { - created { - name - value - status - explain - } - createTime { - name - value - status - explain - } - } runtimeInfo { generation { name @@ -236,7 +222,7 @@ export const DESCRIBE_SOURCE = gql` } } } - instrumentedApplication { + instrumentationConfig { created { name value diff --git a/frontend/webapp/types/describe.ts b/frontend/webapp/types/describe.ts index ead5c51e0..556bd5f54 100644 --- a/frontend/webapp/types/describe.ts +++ b/frontend/webapp/types/describe.ts @@ -12,11 +12,6 @@ interface InstrumentationLabelsAnalyze { instrumentedText?: EntityProperty; } -interface InstrumentationConfigAnalyze { - created: EntityProperty; - createTime?: EntityProperty; -} - interface ContainerRuntimeInfoAnalyze { containerName: EntityProperty; language: EntityProperty; @@ -29,7 +24,7 @@ interface RuntimeInfoAnalyze { containers: ContainerRuntimeInfoAnalyze[]; } -interface InstrumentedApplicationAnalyze { +interface InstrumentationConfigAnalyze { created: EntityProperty; createTime?: EntityProperty; containers: ContainerRuntimeInfoAnalyze[]; @@ -71,9 +66,8 @@ interface SourceAnalyze { namespace: EntityProperty; labels: InstrumentationLabelsAnalyze; - instrumentationConfig: InstrumentationConfigAnalyze; runtimeInfo?: RuntimeInfoAnalyze; - instrumentedApplication: InstrumentedApplicationAnalyze; + instrumentationConfig: InstrumentationConfigAnalyze; instrumentationDevice: InstrumentationDeviceAnalyze; totalPods: number; diff --git a/k8sutils/pkg/describe/source.go b/k8sutils/pkg/describe/source.go index d5bf6a75e..38bd055bd 100644 --- a/k8sutils/pkg/describe/source.go +++ b/k8sutils/pkg/describe/source.go @@ -23,12 +23,6 @@ func printWorkloadManifestInfo(analyze *source.SourceAnalyze, sb *strings.Builde printProperty(sb, 1, &analyze.Labels.InstrumentedText) } -func printInstrumentationConfigInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { - describeText(sb, 0, "\nInstrumentation Config:") - printProperty(sb, 1, &analyze.InstrumentationConfig.Created) - printProperty(sb, 1, analyze.InstrumentationConfig.CreateTime) -} - func printRuntimeDetails(analyze *source.SourceAnalyze, sb *strings.Builder) { describeText(sb, 0, "\nRuntime Inspection Details (new):") @@ -52,14 +46,13 @@ func printRuntimeDetails(analyze *source.SourceAnalyze, sb *strings.Builder) { } } -func printInstrumentedApplicationInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { - - describeText(sb, 0, "\nRuntime Inspection Details (old):") - printProperty(sb, 1, &analyze.InstrumentedApplication.Created) - printProperty(sb, 1, analyze.InstrumentedApplication.CreateTime) +func printInstrumentationConfigInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { + describeText(sb, 0, "\nInstrumentation Config:") + printProperty(sb, 1, &analyze.InstrumentationConfig.Created) + printProperty(sb, 1, analyze.InstrumentationConfig.CreateTime) describeText(sb, 1, "Detected Containers:") - for _, container := range analyze.InstrumentedApplication.Containers { + for _, container := range analyze.InstrumentationConfig.Containers { printProperty(sb, 2, &container.ContainerName) printProperty(sb, 3, &container.Language) printProperty(sb, 3, &container.RuntimeVersion) @@ -122,9 +115,8 @@ func DescribeSourceToText(analyze *source.SourceAnalyze) string { var sb strings.Builder printWorkloadManifestInfo(analyze, &sb) - printInstrumentationConfigInfo(analyze, &sb) printRuntimeDetails(analyze, &sb) - printInstrumentedApplicationInfo(analyze, &sb) + printInstrumentationConfigInfo(analyze, &sb) printAppliedInstrumentationDeviceInfo(analyze, &sb) printPodsInfo(analyze, &sb) diff --git a/k8sutils/pkg/describe/source/analyze.go b/k8sutils/pkg/describe/source/analyze.go index a825b3929..0b03fc5a1 100644 --- a/k8sutils/pkg/describe/source/analyze.go +++ b/k8sutils/pkg/describe/source/analyze.go @@ -21,11 +21,6 @@ type InstrumentationLabelsAnalyze struct { InstrumentedText properties.EntityProperty `json:"instrumentedText"` } -type InstrumentationConfigAnalyze struct { - Created properties.EntityProperty `json:"created"` - CreateTime *properties.EntityProperty `json:"createTime"` -} - type ContainerRuntimeInfoAnalyze struct { ContainerName properties.EntityProperty `json:"containerName"` Language properties.EntityProperty `json:"language"` @@ -38,7 +33,7 @@ type RuntimeInfoAnalyze struct { Containers []ContainerRuntimeInfoAnalyze `json:"containers"` } -type InstrumentedApplicationAnalyze struct { +type InstrumentationConfigAnalyze struct { Created properties.EntityProperty `json:"created"` CreateTime *properties.EntityProperty `json:"createTime"` Containers []ContainerRuntimeInfoAnalyze `json:"containers"` @@ -80,10 +75,9 @@ type SourceAnalyze struct { Namespace properties.EntityProperty `json:"namespace"` Labels InstrumentationLabelsAnalyze `json:"labels"` - InstrumentationConfig InstrumentationConfigAnalyze `json:"instrumentationConfig"` - RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` - InstrumentedApplication InstrumentedApplicationAnalyze `json:"instrumentedApplication"` - InstrumentationDevice InstrumentationDeviceAnalyze `json:"instrumentationDevice"` + RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` + InstrumentationConfig InstrumentationConfigAnalyze `json:"instrumentationConfig"` + InstrumentationDevice InstrumentationDeviceAnalyze `json:"instrumentationDevice"` TotalPods int `json:"totalPods"` PodsPhasesCount string `json:"podsPhasesCount"` @@ -167,9 +161,15 @@ func analyzeInstrumentationConfig(resources *OdigosSourceResources, instrumented } } + containers := make([]ContainerRuntimeInfoAnalyze, 0) + if resources.InstrumentedApplication != nil { + containers = analyzeRuntimeDetails(resources.InstrumentedApplication.Spec.RuntimeDetails) + } + return InstrumentationConfigAnalyze{ Created: created, CreateTime: createdTime, + Containers: containers, } } @@ -229,37 +229,6 @@ func analyzeRuntimeInfo(resources *OdigosSourceResources) *RuntimeInfoAnalyze { } } -func analyzeInstrumentedApplication(resources *OdigosSourceResources) InstrumentedApplicationAnalyze { - instrumentedApplicationCreated := resources.InstrumentedApplication != nil - - created := properties.EntityProperty{ - Name: "Created", - Value: properties.GetTextCreated(instrumentedApplicationCreated), - Status: properties.GetSuccessOrTransitioning(instrumentedApplicationCreated), - Explain: "whether the instrumented application object exists in the cluster. When a workload is labeled for instrumentation, an instrumented application object is created", - } - - var createdTime *properties.EntityProperty - if instrumentedApplicationCreated { - createdTime = &properties.EntityProperty{ - Name: "create time", - Value: resources.InstrumentedApplication.GetCreationTimestamp().String(), - Explain: "the time when the instrumented application object was created", - } - } - - containers := make([]ContainerRuntimeInfoAnalyze, 0) - if resources.InstrumentedApplication != nil { - containers = analyzeRuntimeDetails(resources.InstrumentedApplication.Spec.RuntimeDetails) - } - - return InstrumentedApplicationAnalyze{ - Created: created, - CreateTime: createdTime, - Containers: containers, - } -} - func analyzeInstrumentationDevice(resources *OdigosSourceResources, workloadObj *K8sSourceObject, instrumented bool) InstrumentationDeviceAnalyze { instrumentedApplication := resources.InstrumentedApplication @@ -520,9 +489,8 @@ func analyzePods(resources *OdigosSourceResources, expectedDevices Instrumentati func AnalyzeSource(resources *OdigosSourceResources, workloadObj *K8sSourceObject) *SourceAnalyze { labelsAnalysis, instrumented := analyzeInstrumentationLabels(resources, workloadObj) - icAnalysis := analyzeInstrumentationConfig(resources, instrumented) runtimeAnalysis := analyzeRuntimeInfo(resources) - instrumentedApplication := analyzeInstrumentedApplication(resources) + icAnalysis := analyzeInstrumentationConfig(resources, instrumented) device := analyzeInstrumentationDevice(resources, workloadObj, instrumented) pods, podsText := analyzePods(resources, device) @@ -532,10 +500,9 @@ func AnalyzeSource(resources *OdigosSourceResources, workloadObj *K8sSourceObjec Namespace: properties.EntityProperty{Name: "Namespace", Value: workloadObj.GetNamespace(), Explain: "the namespace of the k8s workload object that this source describes"}, Labels: labelsAnalysis, - InstrumentationConfig: icAnalysis, - RuntimeInfo: runtimeAnalysis, - InstrumentedApplication: instrumentedApplication, - InstrumentationDevice: device, + RuntimeInfo: runtimeAnalysis, + InstrumentationConfig: icAnalysis, + InstrumentationDevice: device, TotalPods: len(pods), PodsPhasesCount: podsText, From 45b738dab0d56d6dc89e01381f7a221e644d0cab Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:24:45 +0200 Subject: [PATCH 163/259] cleanup --- frontend/services/sources.go | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index fc53b250f..394853e51 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -28,38 +28,6 @@ const ( WorkloadKindDaemonSet WorkloadKind = "DaemonSet" ) -type SourceLanguage struct { - ContainerName string `json:"container_name"` - Language string `json:"language"` -} - -type InstrumentedApplicationDetails struct { - Languages []SourceLanguage `json:"languages,omitempty"` - Conditions []metav1.Condition `json:"conditions,omitempty"` -} -type SourceID struct { - // combination of namespace, kind and name is unique - Name string `json:"name"` - Kind string `json:"kind"` - Namespace string `json:"namespace"` -} - -type Source struct { - ThinSource - ReportedName string `json:"reported_name,omitempty"` -} - -type PatchSourceRequest struct { - ReportedName *string `json:"reported_name"` -} - -// this object contains only part of the source fields. It is used to display the sources in the frontend -type ThinSource struct { - SourceID - NumberOfRunningInstances int `json:"number_of_running_instances"` - IaDetails *InstrumentedApplicationDetails `json:"instrumented_application_details"` -} - func GetWorkload(c context.Context, ns string, kind string, name string) (metav1.Object, int) { switch kind { case "Deployment": @@ -130,7 +98,6 @@ func AddHealthyInstrumentationInstancesCondition(ctx context.Context, instruConf } func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, nsName, metav1.GetOptions{}) if err != nil { return nil, err From da67a710bd03472ec41c37b5448eff82dc0bce84 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:39:16 +0200 Subject: [PATCH 164/259] refactor: remove instrumented application retrieval from source resources --- k8sutils/pkg/describe/source/resources.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/k8sutils/pkg/describe/source/resources.go b/k8sutils/pkg/describe/source/resources.go index 3f585f51f..caaaa4d89 100644 --- a/k8sutils/pkg/describe/source/resources.go +++ b/k8sutils/pkg/describe/source/resources.go @@ -43,13 +43,6 @@ func GetRelevantSourceResources(ctx context.Context, kubeClient kubernetes.Inter return nil, err } - ia, err := odigosClient.InstrumentedApplications(workloadNs).Get(ctx, runtimeObjectName, metav1.GetOptions{}) - if err == nil { - sourceResources.InstrumentedApplication = ia - } else if !apierrors.IsNotFound(err) { - return nil, err - } - instrumentedAppSelector := labels.SelectorFromSet(labels.Set{ "instrumented-app": runtimeObjectName, }) From 7534382988be5e5843fefcbf9e79618136bbc5fc Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:43:32 +0200 Subject: [PATCH 165/259] refactor: remove unnecessary refetch calls in destination and source CRUD hooks --- frontend/webapp/hooks/actions/useActionCRUD.ts | 2 +- frontend/webapp/hooks/destinations/useDestinationCRUD.ts | 3 +-- .../hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts | 2 +- frontend/webapp/hooks/sources/useSourceCRUD.ts | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index f10b84b88..9cadebd79 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -32,8 +32,8 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { }; const handleComplete = (actionType: string, message: string, id?: string) => { - refetch(); notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); + refetch(); params?.onSuccess?.(actionType); }; diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index f571f65cd..9fc73cc76 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -12,7 +12,7 @@ interface Params { export const useDestinationCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); - const { data, refetch } = useComputePlatform(); + const { data } = useComputePlatform(); const { addNotification } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { @@ -32,7 +32,6 @@ export const useDestinationCRUD = (params?: Params) => { }; const handleComplete = (actionType: string) => { - refetch(); params?.onSuccess?.(actionType); }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 6a6b5aa03..99b61bf17 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -32,8 +32,8 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { }; const handleComplete = (actionType: string, message: string, id?: string) => { - refetch(); notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); + refetch(); params?.onSuccess?.(actionType); }; diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 84a6b283e..f62e6e366 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -15,7 +15,7 @@ export const useSourceCRUD = (params?: Params) => { const { configuredSources, setConfiguredSources } = useAppStore(); const { persistNamespace } = useNamespace(); - const { data, refetch } = useComputePlatform(); + const { data } = useComputePlatform(); const { addNotification } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { @@ -35,7 +35,6 @@ export const useSourceCRUD = (params?: Params) => { }; const handleComplete = (actionType: string) => { - refetch(); params?.onSuccess?.(actionType); }; From 79293de5681222f2c0ef1b243a05b9ab88a0de42 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:53:41 +0200 Subject: [PATCH 166/259] refactor: add loading state for namespaces in sources list components --- .../choose-sources-body-fast/sources-list/index.tsx | 10 +++------- .../choose-sources-body-simple/sources-list/index.tsx | 9 +++------ frontend/webapp/hooks/sources/useSourceFormData.ts | 6 +++++- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx index 97231f57f..f38e35d4a 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import theme from '@/styles/theme'; import styled from 'styled-components'; import { UseSourceFormDataResponse } from '@/hooks'; -import { Checkbox, Divider, ExtendIcon, NoDataFound, Text, Toggle } from '@/reuseable-components'; +import { Checkbox, Divider, ExtendIcon, FadeLoader, NoDataFound, Text, Toggle } from '@/reuseable-components'; interface Props extends UseSourceFormDataResponse { isModal?: boolean; @@ -89,6 +89,7 @@ const NoDataFoundWrapper = styled.div` export const SourcesList: React.FC = ({ isModal = false, + namespacesLoading, selectedNamespace, onSelectNamespace, @@ -101,18 +102,13 @@ export const SourcesList: React.FC = ({ searchText, selectAllForNamespace, onSelectAll, - showSelectedOnly, filterSources, }) => { const namespaces = Object.entries(availableSources); if (!namespaces.length) { - return ( - - - - ); + return {namespacesLoading ? : }; } return ( diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx index 8bc147a45..76f4a163a 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { FolderIcon } from '@/assets'; import styled from 'styled-components'; import { type UseSourceFormDataResponse } from '@/hooks'; -import { Checkbox, NoDataFound, Text } from '@/reuseable-components'; +import { Checkbox, FadeLoader, NoDataFound, Text } from '@/reuseable-components'; interface Props extends UseSourceFormDataResponse { isModal?: boolean; @@ -74,6 +74,7 @@ const NoDataFoundWrapper = styled.div` export const SourcesList: React.FC = ({ isModal = false, + namespacesLoading, selectedNamespace, availableSources, @@ -85,11 +86,7 @@ export const SourcesList: React.FC = ({ const sources = availableSources[selectedNamespace] || []; if (!sources.length) { - return ( - - - - ); + return {namespacesLoading ? : }; } return ( diff --git a/frontend/webapp/hooks/sources/useSourceFormData.ts b/frontend/webapp/hooks/sources/useSourceFormData.ts index 8e16d1399..b877a1072 100644 --- a/frontend/webapp/hooks/sources/useSourceFormData.ts +++ b/frontend/webapp/hooks/sources/useSourceFormData.ts @@ -18,6 +18,8 @@ interface UseSourceFormDataParams { } export interface UseSourceFormDataResponse { + namespacesLoading: boolean; + selectedNamespace: SelectedNamespace; availableSources: SourcesByNamespace; selectedSources: SourcesByNamespace; @@ -50,7 +52,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo const [availableSources, setAvailableSources] = useState(appStore.availableSources); const [selectedSources, setSelectedSources] = useState(appStore.configuredSources); const [selectedFutureApps, setSelectedFutureApps] = useState(appStore.configuredFutureApps); - const { allNamespaces, data: namespacesData } = useNamespace(selectedNamespace, false); + const { allNamespaces, data: namespacesData, loading: namespacesLoading } = useNamespace(selectedNamespace, false); useEffect(() => { if (!!allNamespaces?.length) { @@ -197,6 +199,8 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo }; return { + namespacesLoading, + selectedNamespace, availableSources, selectedSources, From 6b8c3f7de3f957b671fa6faaf17727dae04236b8 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:55:34 +0200 Subject: [PATCH 167/259] refactor: handle potential undefined supportedSignals in destinations list --- .../choose-destination-body/destinations-list/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx index 5dd44ce68..ae906d20d 100644 --- a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx @@ -55,7 +55,7 @@ export const DestinationsList: React.FC = ({ items, setSe title={destinationItem.displayName} iconSrc={destinationItem.imageUrl} hoverText='Select' - monitors={Object.keys(destinationItem.supportedSignals).filter((signal) => destinationItem.supportedSignals[signal].supported)} + monitors={Object.keys(destinationItem.supportedSignals || {}).filter((signal) => destinationItem.supportedSignals[signal].supported)} monitorsWithLabels onClick={() => setSelectedItems(destinationItem)} /> From 2f08574a2ca29309d99e12fa9336bdd3dc7b2600 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 13:57:52 +0200 Subject: [PATCH 168/259] refactor: remove unnecessary delay in redirect after destination creation --- .../containers/main/destinations/add-destination/index.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index a1bb1530e..288e45f6c 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -61,11 +61,8 @@ export function AddDestinationContainer() { await createSources(configuredSources, configuredFutureApps); await Promise.all(configuredDestinations.map(async ({ form }) => await createDestination(form))); - // Delay redirect by 1 seconds to allow the data to reach the backend 1st - setTimeout(() => { - resetState(); - router.push(ROUTES.OVERVIEW); - }, 1000); + resetState(); + router.push(ROUTES.OVERVIEW); }; const isSourcesListEmpty = () => !Object.values(configuredSources).some((sources) => !!sources.length); From 3371116935f15f6b36a7466215b6d17a69b1fce6 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 14:03:24 +0200 Subject: [PATCH 169/259] fix: UI tests --- frontend/webapp/cypress/constants/index.ts | 4 ++-- frontend/webapp/cypress/e2e/03-sources.cy.ts | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/frontend/webapp/cypress/constants/index.ts b/frontend/webapp/cypress/constants/index.ts index fcc22c8b9..26cf3016f 100644 --- a/frontend/webapp/cypress/constants/index.ts +++ b/frontend/webapp/cypress/constants/index.ts @@ -100,6 +100,6 @@ export const TEXTS = { ACTION_WARN_MODAL_TITLE: `Delete action (${CYPRESS_TEST})`, INSTRUMENTATION_RULE_WARN_MODAL_TITLE: `Delete rule (${CYPRESS_TEST})`, - NOTIF_SOURCES_CREATED: 'successfully added 5 sources', - NOTIF_SOURCES_DELETED: 'successfully deleted 5 sources', + NOTIF_SOURCES_CREATED: 'Successfully created 5 sources', + NOTIF_SOURCES_DELETED: 'Successfully deleted 5 sources', }; diff --git a/frontend/webapp/cypress/e2e/03-sources.cy.ts b/frontend/webapp/cypress/e2e/03-sources.cy.ts index 84d3709e9..7ab0250e0 100644 --- a/frontend/webapp/cypress/e2e/03-sources.cy.ts +++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts @@ -26,8 +26,11 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { - cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); - cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { + cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); + cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); + }); }); }); }); @@ -70,8 +73,11 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { - cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); - cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { + cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); + cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); + }); }); }); }); From eee8b8af005db0aa34b5940290628c41489709cd Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 14:18:32 +0200 Subject: [PATCH 170/259] fix: shorter wait times in cypress test --- frontend/webapp/cypress/e2e/03-sources.cy.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/webapp/cypress/e2e/03-sources.cy.ts b/frontend/webapp/cypress/e2e/03-sources.cy.ts index 7ab0250e0..7ec7f82a7 100644 --- a/frontend/webapp/cypress/e2e/03-sources.cy.ts +++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts @@ -20,14 +20,14 @@ describe('Sources CRUD', () => { cy.get(DATA_IDS.MODAL_ADD_SOURCE).should('exist'); cy.get(DATA_IDS.SELECT_NAMESPACE).find(DATA_IDS.CHECKBOX).click(); - // Wait for 3 seconds to allow the namespace & it's resources to be loaded into the UI - cy.wait(3000).then(() => { + // Wait for 2 seconds to allow the namespace & it's resources to be loaded into the UI + cy.wait(2000).then(() => { cy.contains('button', BUTTONS.DONE).click(); cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { - // Wait for 10 seconds to allow the backend to batch an SSE notification - cy.wait(10000).then(() => { + // Wait for 5 seconds to allow the backend to batch an SSE notification + cy.wait(5000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); }); @@ -73,8 +73,8 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { - // Wait for 10 seconds to allow the backend to batch an SSE notification - cy.wait(10000).then(() => { + // Wait for 5 seconds to allow the backend to batch an SSE notification + cy.wait(5000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); }); From ed9cfdd128f8900ccd7903c17d7b46dc5b853344 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 14:42:52 +0200 Subject: [PATCH 171/259] feat: add handling for modified instrumentation configs --- frontend/kube/watchers/destination_watcher.go | 8 +-- .../instrumentation_config_watcher.go | 56 +++++++++++++++---- .../instrumentation_instance_watcher.go | 8 +-- .../hooks/destinations/useDestinationCRUD.ts | 6 +- .../webapp/hooks/sources/useSourceCRUD.ts | 10 ++-- 5 files changed, 60 insertions(+), 28 deletions(-) diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index 706fd6928..b290e275d 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -14,7 +14,7 @@ import ( ) var destinationAddedEventBatcher *EventBatcher -var destinationModifiedBatcher *EventBatcher +var destinationModifiedEventBatcher *EventBatcher var destinationDeletedEventBatcher *EventBatcher func StartDestinationWatcher(ctx context.Context, namespace string) error { @@ -33,7 +33,7 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { }, ) - destinationModifiedBatcher = NewEventBatcher( + destinationModifiedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -75,7 +75,7 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { func handleDestinationWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() defer destinationAddedEventBatcher.Cancel() - defer destinationModifiedBatcher.Cancel() + defer destinationModifiedEventBatcher.Cancel() defer destinationDeletedEventBatcher.Cancel() for { select { @@ -126,7 +126,7 @@ func handleModifiedDestination(destination *v1alpha1.Destination) { conditionType = sse.MessageTypeError } - destinationModifiedBatcher.AddEvent(conditionType, data, target) + destinationModifiedEventBatcher.AddEvent(conditionType, data, target) } func handleDeletedDestination(destination *v1alpha1.Destination) { diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index c09107fe0..ddb67b5b8 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -14,11 +14,12 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var instruConfigAddedEventBatcher *EventBatcher -var instruConfigDeletedEventBatcher *EventBatcher +var instrumentationConfigAddedEventBatcher *EventBatcher +var instrumentationConfigModifiedEventBatcher *EventBatcher +var instrumentationConfigDeletedEventBatcher *EventBatcher func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { - instruConfigAddedEventBatcher = NewEventBatcher( + instrumentationConfigAddedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -33,7 +34,22 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er }, ) - instruConfigDeletedEventBatcher = NewEventBatcher( + instrumentationConfigModifiedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventModified, + CRDType: consts.InstrumentationConfig, + SuccessBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Successfully updated %d sources", batchSize) + }, + FailureBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Failed to update %d sources", batchSize) + }, + }, + ) + + instrumentationConfigDeletedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -59,8 +75,9 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer instruConfigAddedEventBatcher.Cancel() - defer instruConfigDeletedEventBatcher.Cancel() + defer instrumentationConfigAddedEventBatcher.Cancel() + defer instrumentationConfigModifiedEventBatcher.Cancel() + defer instrumentationConfigDeletedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -72,15 +89,17 @@ func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.I } switch event.Type { case watch.Added: - handleAddedEvent(event.Object.(*v1alpha1.InstrumentationConfig)) + handleAddedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) + case watch.Modified: + handleModifiedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) case watch.Deleted: - handleDeletedEvent(event.Object.(*v1alpha1.InstrumentationConfig)) + handleDeletedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) } } } } -func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { +func handleAddedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { @@ -90,10 +109,23 @@ func handleAddedEvent(instruConfig *v1alpha1.InstrumentationConfig) { target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) data := fmt.Sprintf(`Source "%s" created`, name) - instruConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) + instrumentationConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) +} + +func handleModifiedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) + if err != nil { + genericErrorMessage(sse.MessageEventAdded, consts.InstrumentationConfig, err.Error()) + return + } + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf(`Source "%s" updated`, name) + instrumentationConfigModifiedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } -func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { +func handleDeletedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) if err != nil { @@ -103,5 +135,5 @@ func handleDeletedEvent(instruConfig *v1alpha1.InstrumentationConfig) { target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) data := fmt.Sprintf(`Source "%s" deleted`, name) - instruConfigDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) + instrumentationConfigDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 5f3bc3c03..0992faf63 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -14,10 +14,10 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var instruInstanceModifiedBatcher *EventBatcher +var instrumentationInstanceModifiedEventBatcher *EventBatcher func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) error { - instruInstanceModifiedBatcher = NewEventBatcher( + instrumentationInstanceModifiedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, Duration: 10 * time.Second, @@ -41,7 +41,7 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer instruInstanceModifiedBatcher.Cancel() + defer instrumentationInstanceModifiedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -84,5 +84,5 @@ func handleModifiedInstrumentationInstance(instruInstance *v1alpha1.Instrumentat target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInstance.Status.Reason, instruInstance.Status.Message) - instruInstanceModifiedBatcher.AddEvent(sse.MessageTypeError, data, target) + instrumentationInstanceModifiedEventBatcher.AddEvent(sse.MessageTypeError, data, target) } diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 9fc73cc76..93ea5b701 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -57,15 +57,15 @@ export const useDestinationCRUD = (params?: Params) => { destinations: data?.computePlatform?.destinations || [], createDestination: (destination: DestinationInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating destination...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Creating destination...', undefined, true); createDestination({ variables: { destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, updateDestination: (id: string, destination: DestinationInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating destination...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating destination...', undefined, true); updateDestination({ variables: { id, destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, deleteDestination: (id: string) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting destination...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Deleting destination...', undefined, true); deleteDestination({ variables: { id } }); }, }; diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index f62e6e366..0d96d8d17 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -14,8 +14,8 @@ export const useSourceCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { configuredSources, setConfiguredSources } = useAppStore(); - const { persistNamespace } = useNamespace(); const { data } = useComputePlatform(); + const { persistNamespace } = useNamespace(); const { addNotification } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { @@ -64,7 +64,7 @@ export const useSourceCRUD = (params?: Params) => { const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: () => handleComplete(ACTION.UPDATE), + onCompleted: (res, req) => handleComplete(ACTION.UPDATE), }); const persistNamespaces = async (items: { [key: string]: boolean }) => { @@ -93,16 +93,16 @@ export const useSourceCRUD = (params?: Params) => { sources: data?.computePlatform.k8sActualSources || [], createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'creating sources...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Creating sources...', undefined, true); await persistNamespaces(futureSelectAppsList); await persistSources(selectAppsList, true); }, updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'updating sources...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating sources...', undefined, true); await updateSource({ variables: { sourceId, patchSourceRequest } }); }, deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'deleting sources...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Deleting sources...', undefined, true); await persistSources(selectAppsList, false); }, }; From 0cbe6d1c997b2a28852b35ca72c76fcd90fc7d64 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 15:52:41 +0200 Subject: [PATCH 172/259] fix: UI tests again --- frontend/webapp/cypress/e2e/03-sources.cy.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/webapp/cypress/e2e/03-sources.cy.ts b/frontend/webapp/cypress/e2e/03-sources.cy.ts index 7ec7f82a7..7ab0250e0 100644 --- a/frontend/webapp/cypress/e2e/03-sources.cy.ts +++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts @@ -20,14 +20,14 @@ describe('Sources CRUD', () => { cy.get(DATA_IDS.MODAL_ADD_SOURCE).should('exist'); cy.get(DATA_IDS.SELECT_NAMESPACE).find(DATA_IDS.CHECKBOX).click(); - // Wait for 2 seconds to allow the namespace & it's resources to be loaded into the UI - cy.wait(2000).then(() => { + // Wait for 3 seconds to allow the namespace & it's resources to be loaded into the UI + cy.wait(3000).then(() => { cy.contains('button', BUTTONS.DONE).click(); cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { - // Wait for 5 seconds to allow the backend to batch an SSE notification - cy.wait(5000).then(() => { + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); }); @@ -73,8 +73,8 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { - // Wait for 5 seconds to allow the backend to batch an SSE notification - cy.wait(5000).then(() => { + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); }); From 58431b8b6f4324c64fdce5f9ec31c734ffb52e37 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 15:58:53 +0200 Subject: [PATCH 173/259] feat: mark instrumented sources when gettgin namespace --- frontend/graph/generated.go | 153 +++++++----------- frontend/graph/model/models_gen.go | 6 +- frontend/graph/schema.graphqls | 4 +- frontend/graph/schema.resolvers.go | 43 +++-- frontend/services/sources.go | 20 +-- .../graphql/queries/compute-platform.ts | 7 +- .../hooks/compute-platform/useNamespace.ts | 19 ++- .../webapp/hooks/sources/useSourceFormData.ts | 4 +- frontend/webapp/types/sources.ts | 2 +- k8sutils/pkg/workload/workload.go | 9 -- 10 files changed, 114 insertions(+), 153 deletions(-) diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index a07f00471..b41a613fe 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -257,9 +257,8 @@ type ComplexityRoot struct { } K8sActualNamespace struct { - InstrumentationLabelEnabled func(childComplexity int) int - K8sActualSources func(childComplexity int, instrumentationLabeled *bool) int - Name func(childComplexity int) int + K8sActualSources func(childComplexity int) int + Name func(childComplexity int) int } K8sActualSource struct { @@ -270,6 +269,7 @@ type ComplexityRoot struct { Namespace func(childComplexity int) int NumberOfInstances func(childComplexity int) int ReportedName func(childComplexity int) int + Selected func(childComplexity int) int } LatencySamplerAction struct { @@ -477,7 +477,7 @@ type DestinationResolver interface { Conditions(ctx context.Context, obj *model.Destination) ([]*model.Condition, error) } type K8sActualNamespaceResolver interface { - K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace, instrumentationLabeled *bool) ([]*model.K8sActualSource, error) + K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace) ([]*model.K8sActualSource, error) } type MutationResolver interface { CreateNewDestination(ctx context.Context, destination model.DestinationInput) (*model.Destination, error) @@ -1362,24 +1362,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationRule.Workloads(childComplexity), true - case "K8sActualNamespace.instrumentationLabelEnabled": - if e.complexity.K8sActualNamespace.InstrumentationLabelEnabled == nil { - break - } - - return e.complexity.K8sActualNamespace.InstrumentationLabelEnabled(childComplexity), true - case "K8sActualNamespace.k8sActualSources": if e.complexity.K8sActualNamespace.K8sActualSources == nil { break } - args, err := ec.field_K8sActualNamespace_k8sActualSources_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.K8sActualNamespace.K8sActualSources(childComplexity, args["instrumentationLabeled"].(*bool)), true + return e.complexity.K8sActualNamespace.K8sActualSources(childComplexity), true case "K8sActualNamespace.name": if e.complexity.K8sActualNamespace.Name == nil { @@ -1437,6 +1425,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualSource.ReportedName(childComplexity), true + case "K8sActualSource.selected": + if e.complexity.K8sActualSource.Selected == nil { + break + } + + return e.complexity.K8sActualSource.Selected(childComplexity), true + case "LatencySamplerAction.details": if e.complexity.LatencySamplerAction.Details == nil { break @@ -2516,21 +2511,6 @@ func (ec *executionContext) field_ComputePlatform_k8sActualNamespace_args(ctx co return args, nil } -func (ec *executionContext) field_K8sActualNamespace_k8sActualSources_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 *bool - if tmp, ok := rawArgs["instrumentationLabeled"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("instrumentationLabeled")) - arg0, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) - if err != nil { - return nil, err - } - } - args["instrumentationLabeled"] = arg0 - return args, nil -} - func (ec *executionContext) field_Mutation_createAction_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -3878,8 +3858,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespaces(_ c switch field.Name { case "name": return ec.fieldContext_K8sActualNamespace_name(ctx, field) - case "instrumentationLabelEnabled": - return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) case "k8sActualSources": return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } @@ -3927,8 +3905,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx switch field.Name { case "name": return ec.fieldContext_K8sActualNamespace_name(ctx, field) - case "instrumentationLabelEnabled": - return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) case "k8sActualSources": return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } @@ -3996,6 +3972,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "selected": + return ec.fieldContext_K8sActualSource_selected(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) case "containers": @@ -8535,47 +8513,6 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_name(_ context.Conte return fc, nil } -func (ec *executionContext) _K8sActualNamespace_instrumentationLabelEnabled(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.InstrumentationLabelEnabled, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*bool) - fc.Result = res - return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_K8sActualNamespace_instrumentationLabelEnabled(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "K8sActualNamespace", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) if err != nil { @@ -8590,7 +8527,7 @@ func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Con }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.K8sActualNamespace().K8sActualSources(rctx, obj, fc.Args["instrumentationLabeled"].(*bool)) + return ec.resolvers.K8sActualNamespace().K8sActualSources(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8607,7 +8544,7 @@ func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Con return ec.marshalNK8sActualSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualSource(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualNamespace", Field: field, @@ -8623,6 +8560,8 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "selected": + return ec.fieldContext_K8sActualSource_selected(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) case "containers": @@ -8633,17 +8572,6 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_K8sActualNamespace_k8sActualSources_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } @@ -8820,6 +8748,47 @@ func (ec *executionContext) fieldContext_K8sActualSource_numberOfInstances(_ con return fc, nil } +func (ec *executionContext) _K8sActualSource_selected(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_selected(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Selected, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*bool) + fc.Result = res + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_K8sActualSource_selected(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "K8sActualSource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) if err != nil { @@ -19312,8 +19281,6 @@ func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.Sel if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "instrumentationLabelEnabled": - out.Values[i] = ec._K8sActualNamespace_instrumentationLabelEnabled(ctx, field, obj) case "k8sActualSources": field := field @@ -19401,6 +19368,8 @@ func (ec *executionContext) _K8sActualSource(ctx context.Context, sel ast.Select } case "numberOfInstances": out.Values[i] = ec._K8sActualSource_numberOfInstances(ctx, field, obj) + case "selected": + out.Values[i] = ec._K8sActualSource_selected(ctx, field, obj) case "reportedName": out.Values[i] = ec._K8sActualSource_reportedName(ctx, field, obj) case "containers": diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 9bef3d092..bf9bf142b 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -296,9 +296,8 @@ type InstrumentationRuleInput struct { } type K8sActualNamespace struct { - Name string `json:"name"` - InstrumentationLabelEnabled *bool `json:"instrumentationLabelEnabled,omitempty"` - K8sActualSources []*K8sActualSource `json:"k8sActualSources"` + Name string `json:"name"` + K8sActualSources []*K8sActualSource `json:"k8sActualSources"` } type K8sActualSource struct { @@ -306,6 +305,7 @@ type K8sActualSource struct { Name string `json:"name"` Kind K8sResourceKind `json:"kind"` NumberOfInstances *int `json:"numberOfInstances,omitempty"` + Selected *bool `json:"selected,omitempty"` ReportedName *string `json:"reportedName,omitempty"` Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` Conditions []*Condition `json:"conditions,omitempty"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 76be28369..602e4440f 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -61,8 +61,7 @@ type Condition { type K8sActualNamespace { name: String! - instrumentationLabelEnabled: Boolean - k8sActualSources(instrumentationLabeled: Boolean): [K8sActualSource]! + k8sActualSources: [K8sActualSource]! } input K8sDesiredNamespaceInput { @@ -78,6 +77,7 @@ type K8sActualSource { name: String! kind: K8sResourceKind! numberOfInstances: Int + selected: Boolean reportedName: String containers: [SourceContainerRuntimeDetails!] conditions: [Condition!] diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index c18266f24..5c341760e 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -19,7 +19,6 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/odigos_describe" "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" - "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) @@ -30,17 +29,8 @@ func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj * K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) for i, namespace := range namespacesResponse.Namespaces { - - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) - K8sActualNamespaces[i] = &model.K8sActualNamespace{ - Name: namespace.Name, - InstrumentationLabelEnabled: nsInstrumented, + Name: namespace.Name, } } @@ -49,7 +39,12 @@ func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj * // K8sActualNamespace is the resolver for the k8sActualNamespace field. func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) { - namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, name, nil) + namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, name) if err != nil { return nil, err } @@ -60,17 +55,9 @@ func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *m namespaceActualSourcesPointers[i] = &source } - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) - return &model.K8sActualNamespace{ - Name: name, - InstrumentationLabelEnabled: nsInstrumented, - K8sActualSources: namespaceActualSourcesPointers, + Name: namespace.Name, + K8sActualSources: namespaceActualSourcesPointers, }, nil } @@ -268,8 +255,8 @@ func (r *destinationResolver) Conditions(ctx context.Context, obj *model.Destina } // K8sActualSources is the resolver for the k8sActualSources field. -func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace, instrumentationLabeled *bool) ([]*model.K8sActualSource, error) { - namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, obj.Name, instrumentationLabeled) +func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace) ([]*model.K8sActualSource, error) { + namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, obj.Name) if err != nil { return nil, err } @@ -278,6 +265,14 @@ func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj * namespaceActualSourcesPointers := make([]*model.K8sActualSource, len(namespaceActualSources)) for i, source := range namespaceActualSources { namespaceActualSourcesPointers[i] = &source + + crd, err := services.GetSourceCRD(ctx, obj.Name, source.Name, services.WorkloadKind(source.Kind)) + instrumented := false + if crd != nil && err == nil { + instrumented = true + } + + namespaceActualSourcesPointers[i].Selected = &instrumented } return namespaceActualSourcesPointers, nil diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 394853e51..add2b7a46 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -97,7 +97,7 @@ func AddHealthyInstrumentationInstancesCondition(ctx context.Context, instruConf return nil } -func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func GetWorkloadsInNamespace(ctx context.Context, nsName string) ([]model.K8sActualSource, error) { namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, nsName, metav1.GetOptions{}) if err != nil { return nil, err @@ -112,19 +112,19 @@ func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentation g.Go(func() error { var err error - deps, err = getDeployments(ctx, *namespace, instrumentationLabeled) + deps, err = getDeployments(ctx, *namespace) return err }) g.Go(func() error { var err error - ss, err = getStatefulSets(ctx, *namespace, instrumentationLabeled) + ss, err = getStatefulSets(ctx, *namespace) return err }) g.Go(func() error { var err error - dss, err = getDaemonSets(ctx, *namespace, instrumentationLabeled) + dss, err = getDaemonSets(ctx, *namespace) return err }) @@ -140,7 +140,7 @@ func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentation return items, nil } -func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getDeployments(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().Deployments(namespace.Name).List, ctx, metav1.ListOptions{}, func(deps *appsv1.DeploymentList) error { for _, dep := range deps.Items { @@ -162,7 +162,7 @@ func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumenta return response, nil } -func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getDaemonSets(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().DaemonSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(dss *appsv1.DaemonSetList) error { for _, ds := range dss.Items { @@ -184,7 +184,7 @@ func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentat return response, nil } -func getStatefulSets(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getStatefulSets(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().StatefulSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(sss *appsv1.StatefulSetList) error { for _, ss := range sss.Items { @@ -264,7 +264,7 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s return annotations } -func getSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { +func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { source, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ consts.OdigosNamespaceAnnotation: nsName, consts.OdigosWorkloadNameAnnotation: workloadName, @@ -293,7 +293,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } - source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { return fmt.Errorf(`source "%s" already exists`, workloadName) } @@ -321,7 +321,7 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } - source, err := getSourceCRD(ctx, nsName, workloadName, workloadKind) + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if err != nil { return err } diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index afe9ca761..c02fd80dd 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -11,6 +11,7 @@ export const GET_COMPUTE_PLATFORM = gql` name kind numberOfInstances + selected reportedName containers { containerName @@ -102,15 +103,15 @@ export const GET_COMPUTE_PLATFORM = gql` `; export const GET_NAMESPACES = gql` - query GetK8sActualNamespace($namespaceName: String!, $instrumentationLabeled: Boolean) { + query GetK8sActualNamespace($namespaceName: String!) { computePlatform { k8sActualNamespace(name: $namespaceName) { name - instrumentationLabelEnabled - k8sActualSources(instrumentationLabeled: $instrumentationLabeled) { + k8sActualSources { kind name numberOfInstances + selected } } } diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index b84fa8022..1383550f0 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -5,30 +5,35 @@ import { useComputePlatform } from './useComputePlatform'; import { GET_NAMESPACES, PERSIST_NAMESPACE } from '@/graphql'; import { type ComputePlatform, NOTIFICATION_TYPE, type PersistNamespaceItemInput } from '@/types'; -export const useNamespace = (namespaceName?: string, instrumentationLabeled = null as boolean | null) => { +export const useNamespace = (namespaceName?: string) => { const { addNotification } = useNotificationStore(); const cp = useComputePlatform(); - const { data, loading, error } = useQuery(GET_NAMESPACES, { + const { data, loading } = useQuery(GET_NAMESPACES, { skip: !namespaceName, fetchPolicy: 'cache-first', - variables: { namespaceName, instrumentationLabeled }, + variables: { namespaceName }, + onError: (error) => + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, + }), }); const [persistNamespaceMutation] = useMutation(PERSIST_NAMESPACE, { onError: (error) => addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: error.name || ACTION.FETCH, + title: error.name || ACTION.UPDATE, message: error.cause?.message || error.message, }), }); return { + loading, + data: data?.computePlatform.k8sActualNamespace, allNamespaces: cp.data?.computePlatform.k8sActualNamespaces, persistNamespace: async (namespace: PersistNamespaceItemInput) => await persistNamespaceMutation({ variables: { namespace } }), - data: data?.computePlatform.k8sActualNamespace, - loading, - error, }; }; diff --git a/frontend/webapp/hooks/sources/useSourceFormData.ts b/frontend/webapp/hooks/sources/useSourceFormData.ts index b877a1072..7f31a3ff9 100644 --- a/frontend/webapp/hooks/sources/useSourceFormData.ts +++ b/frontend/webapp/hooks/sources/useSourceFormData.ts @@ -52,7 +52,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo const [availableSources, setAvailableSources] = useState(appStore.availableSources); const [selectedSources, setSelectedSources] = useState(appStore.configuredSources); const [selectedFutureApps, setSelectedFutureApps] = useState(appStore.configuredFutureApps); - const { allNamespaces, data: namespacesData, loading: namespacesLoading } = useNamespace(selectedNamespace, false); + const { allNamespaces, data: namespacesData, loading: namespacesLoading } = useNamespace(selectedNamespace); useEffect(() => { if (!!allNamespaces?.length) { @@ -78,7 +78,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo // set available sources for current selected namespace const { name, k8sActualSources = [] } = namespacesData; setAvailableSources((prev) => ({ ...prev, [name]: k8sActualSources })); - setSelectedSources((prev) => ({ ...prev, [name]: prev[name] || [] })); + setSelectedSources((prev) => ({ ...prev, [name]: prev[name] || k8sActualSources.filter(({ selected }) => selected) })); } }, [namespacesData]); diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index 62c207116..945b8d88a 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -13,10 +13,10 @@ export type K8sActualSource = { name: string; kind: string; numberOfInstances: number; + selected: boolean; reportedName: string; containers: Array; conditions: Array; - selected?: boolean; // not from backend }; export type WorkloadId = { diff --git a/k8sutils/pkg/workload/workload.go b/k8sutils/pkg/workload/workload.go index 581540cd0..16fd5e699 100644 --- a/k8sutils/pkg/workload/workload.go +++ b/k8sutils/pkg/workload/workload.go @@ -111,15 +111,6 @@ func IsInstrumentationDisabledExplicitly(obj client.Object) bool { return false } -func GetInstrumentationLabelValue(labels map[string]string) *bool { - if val, exists := labels[consts.OdigosInstrumentationLabel]; exists { - enabled := val == consts.InstrumentationEnabled - return &enabled - } - - return nil -} - func GetWorkloadObject(ctx context.Context, objectKey client.ObjectKey, kind WorkloadKind, kubeClient client.Client) (metav1.Object, error) { switch kind { case WorkloadKindDeployment: From 28b6bd2b6ecfd448fd8965566f518d2fb658e3e6 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 16:47:59 +0200 Subject: [PATCH 174/259] feat: refactor source handling to allow UI to persist sources without errors --- frontend/services/sources.go | 6 ++-- .../destinations/add-destination/index.tsx | 13 +++++-- .../overview/multi-source-control/index.tsx | 10 ++++-- .../choose-source-modal/index.tsx | 14 ++++++-- .../sources/source-drawer-container/index.tsx | 5 +-- .../hooks/compute-platform/useNamespace.ts | 1 - .../webapp/hooks/sources/useSourceCRUD.ts | 35 +++++++------------ 7 files changed, 50 insertions(+), 34 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index add2b7a46..4167548d2 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -295,7 +295,8 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - return fmt.Errorf(`source "%s" already exists`, workloadName) + // we don't want to throw error, because we want to allow the UI to send an array of all sources even if instrumented + return nil } newSource := &v1alpha1.Source{ @@ -323,7 +324,8 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if err != nil { - return err + // we don't want to throw error, because we want to allow the UI to send an array of all sources even if not instrumented + return nil } err = kube.DefaultClient.OdigosClient.Sources(nsName).Delete(ctx, source.Name, metav1.DeleteOptions{}) diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index 288e45f6c..99b01179a 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -41,7 +41,7 @@ const StyledAddDestinationButton = styled(Button)` export function AddDestinationContainer() { const router = useRouter(); - const { createSources } = useSourceCRUD(); + const { persistSources } = useSourceCRUD(); const { createDestination } = useDestinationCRUD(); const { configuredSources, configuredFutureApps, configuredDestinations, resetState } = useAppStore((state) => state); @@ -58,7 +58,16 @@ export function AddDestinationContainer() { const clickDone = async () => { setIsLoading(true); - await createSources(configuredSources, configuredFutureApps); + const payload: typeof configuredSources = {}; + + Object.entries(configuredSources).forEach(([namespace, sources]) => { + payload[namespace] = sources.map((source) => ({ + ...source, + selected: true, + })); + }); + + await persistSources(payload, configuredFutureApps); await Promise.all(configuredDestinations.map(async ({ form }) => await createDestination(form))); resetState(); diff --git a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx index 4181dda72..ebe13b893 100644 --- a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx +++ b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx @@ -31,7 +31,7 @@ export const MultiSourceControl = () => { animateOut: slide.out['center'], }); - const { sources, deleteSources } = useSourceCRUD(); + const { sources, persistSources } = useSourceCRUD(); const { configuredSources, setConfiguredSources } = useAppStore(); const [isWarnModalOpen, setIsWarnModalOpen] = useState(false); @@ -50,7 +50,13 @@ export const MultiSourceControl = () => { }; const onDelete = () => { - deleteSources(configuredSources); + const payload: typeof configuredSources = {}; + + Object.entries(configuredSources).forEach(([namespace, selectedSources]) => { + payload[namespace] = selectedSources.map(({ selected, ...rest }) => ({ ...rest, selected: false })); + }); + + persistSources(payload, {}); onDeselect(); setIsWarnModalOpen(false); }; diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx index 9fe3201ab..d3c02e349 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx @@ -12,12 +12,20 @@ export const AddSourceModal: React.FC = ({ isOpen, onClose }) => { useKeyDown({ key: 'Enter', active: isOpen }, () => handleSubmit()); const menuState = useSourceFormData(); - const { createSources } = useSourceCRUD({ onSuccess: onClose }); + const { persistSources } = useSourceCRUD({ onSuccess: onClose }); const handleSubmit = async () => { - const { selectedSources, selectedFutureApps } = menuState; + const { availableSources, selectedSources, selectedFutureApps } = menuState; + const payload: typeof availableSources = {}; - await createSources(selectedSources, selectedFutureApps); + Object.entries(availableSources).forEach(([namespace, sources]) => { + payload[namespace] = sources.map((source) => ({ + ...source, + selected: !!selectedSources[namespace].find(({ kind, name }) => kind === source.kind && name === source.name), + })); + }); + + await persistSources(payload, selectedFutureApps); }; return ( diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx index 4ea0eb2ac..5e23efabb 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -33,7 +33,7 @@ const DataContainer = styled.div` export const SourceDrawer: React.FC = () => { const { selectedItem, setSelectedItem } = useDrawerStore(); - const { deleteSources, updateSource } = useSourceCRUD({ + const { persistSources, updateSource } = useSourceCRUD({ onSuccess: (type) => { setIsEditing(false); setIsFormDirty(false); @@ -112,7 +112,8 @@ export const SourceDrawer: React.FC = () => { const handleDelete = async () => { const { namespace } = item; - await deleteSources({ [namespace]: [item] }); + + await persistSources({ [namespace]: [{ ...item, selected: false }] }, {}); }; const handleSave = async () => { diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index 1383550f0..33d5a5c1d 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -11,7 +11,6 @@ export const useNamespace = (namespaceName?: string) => { const { data, loading } = useQuery(GET_NAMESPACES, { skip: !namespaceName, - fetchPolicy: 'cache-first', variables: { namespaceName }, onError: (error) => addNotification({ diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 0d96d8d17..5744ea9db 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -39,25 +39,20 @@ export const useSourceCRUD = (params?: Params) => { }; const [createOrDeleteSources, cdState] = useMutation<{ persistK8sSources: boolean }>(PERSIST_SOURCE, { - onError: (error, req) => { - const { selected } = req?.variables?.sources?.[0] || {}; - const action = selected ? ACTION.CREATE : ACTION.DELETE; - - handleError(action, error.message); - }, + onError: (error, req) => handleError('', error.message), onCompleted: (res, req) => { const count = req?.variables?.sources.length; - const namespace = req?.variables?.namespace; - const { name, kind, selected } = req?.variables?.sources?.[0] || {}; - const action = selected ? ACTION.CREATE : ACTION.DELETE; - if (count > 1) { - handleComplete(action); - } else { + if (count === 1) { + const namespace = req?.variables?.namespace; + const { name, kind, selected } = req?.variables?.sources?.[0] || {}; const id = { namespace, name, kind }; + if (!selected) removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE)); if (!selected) setConfiguredSources({ ...configuredSources, [namespace]: configuredSources[namespace]?.filter((source) => source.name !== name) || [] }); - handleComplete(action); + handleComplete(selected ? ACTION.CREATE : ACTION.DELETE); + } else { + handleComplete(''); } }, }); @@ -73,7 +68,7 @@ export const useSourceCRUD = (params?: Params) => { } }; - const persistSources = async (items: { [key: string]: K8sActualSource[] }, selected: boolean) => { + const persistSources = async (items: { [key: string]: K8sActualSource[] }) => { for (const [namespace, sources] of Object.entries(items)) { await createOrDeleteSources({ variables: { @@ -81,7 +76,7 @@ export const useSourceCRUD = (params?: Params) => { sources: sources.map((source) => ({ kind: source.kind, name: source.name, - selected, + selected: source.selected, })), }, }); @@ -92,18 +87,14 @@ export const useSourceCRUD = (params?: Params) => { loading: cdState.loading || uState.loading, sources: data?.computePlatform.k8sActualSources || [], - createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Creating sources...', undefined, true); + persistSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Persisting sources...', undefined, true); await persistNamespaces(futureSelectAppsList); - await persistSources(selectAppsList, true); + await persistSources(selectAppsList); }, updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating sources...', undefined, true); await updateSource({ variables: { sourceId, patchSourceRequest } }); }, - deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Deleting sources...', undefined, true); - await persistSources(selectAppsList, false); - }, }; }; From 9777c64a7935bb567c28a4e301c73abe367090e4 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 17:25:15 +0200 Subject: [PATCH 175/259] Revert "Merge branch 'feature/source-ui' of github.com:BenElferink/odigos into feature/source-ui" This reverts commit 9a249888cc9d6d53b1fdc4ecf5241bdbb935ff48, reversing changes made to 28b6bd2b6ecfd448fd8965566f518d2fb658e3e6. From 3bf587a8f6ab54990e48e3faf66c7e2045770b8b Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 17:25:27 +0200 Subject: [PATCH 176/259] cleanup --- frontend/graph/conversions.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index 695c1c86a..21b91c5ad 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -21,27 +21,6 @@ func k8sKindToGql(k8sResourceKind string) model.K8sResourceKind { return "" } -func k8sConditionStatusToGql(status v1.ConditionStatus) model.ConditionStatus { - switch status { - case v1.ConditionTrue: - return model.ConditionStatusTrue - case v1.ConditionFalse: - return model.ConditionStatusFalse - case v1.ConditionUnknown: - return model.ConditionStatusUnknown - } - return model.ConditionStatusUnknown - -} - -func k8sLastTransitionTimeToGql(t v1.Time) *string { - if t.IsZero() { - return nil - } - str := t.UTC().Format(time.RFC3339) - return &str -} - func instrumentationConfigToActualSource(instruConfig v1alpha1.InstrumentationConfig) *model.K8sActualSource { // Map the containers runtime details var containers []*model.SourceContainerRuntimeDetails From 348c308b8886236a586af683ebdd90a1222ce915 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Mon, 30 Dec 2024 17:29:49 +0200 Subject: [PATCH 177/259] chore: wip + pl ben fork --- autoscaler/controllers/common/processors.go | 117 +++++++++++++++++ autoscaler/controllers/gateway/configmap.go | 4 +- autoscaler/controllers/gateway/root.go | 12 +- collector/odigosotelcol/components.go | 3 + collector/odigosotelcol/go.mod | 5 + .../factory.go | 44 +++++++ .../generated_component_test.go | 14 ++ .../internal/metadata/generated_status.go | 4 +- .../metadata.yaml | 2 + .../processor.go | 121 +++++++++++++++++- 10 files changed, 315 insertions(+), 11 deletions(-) diff --git a/autoscaler/controllers/common/processors.go b/autoscaler/controllers/common/processors.go index fb54f12a1..af2a27087 100644 --- a/autoscaler/controllers/common/processors.go +++ b/autoscaler/controllers/common/processors.go @@ -2,12 +2,14 @@ package common import ( "encoding/json" + "fmt" "sort" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/log" ) func FilterAndSortProcessorsByOrderHint(processors *odigosv1.ProcessorList, collectorRole odigosv1.CollectorsGroupRole) []*odigosv1.Processor { @@ -70,3 +72,118 @@ func GetGenericBatchProcessor() odigosv1.Processor { }, } } + +type MatchCondition struct { + Name string `mapstructure:"name"` + Namespace string `mapstructure:"namespace"` + Kind string `mapstructure:"kind"` +} + +func AddFilterProcessors(allProcessors *odigosv1.ProcessorList, dests *odigosv1.DestinationList, sources *odigosv1.SourceList) { + for _, dest := range dests.Items { + //TODO: remove this log + logger := log.Log.WithValues("destination", dest.Name) + logger.Info("Processing destination for filter processor") + //TODO: remove this log + matchedSources := filterSources(sources.Items, dest.Spec.SourceSelector) + + if len(matchedSources) == 0 { + //TODO: remove this log + logger.Info("No matching sources found for destination. Skipping processor creation.") + + //TODO: remove this log + continue + } + //TODO: remove this log + logger.Info("Matched sources for destination", "matchedSources", matchedSources) + //TODO: remove this log + var matchConditions []map[string]string + for _, source := range matchedSources { + //TODO: remove this log + logger.Info("Adding match condition for source", "sourceName", source.Spec.Workload.Name, "namespace", source.Spec.Workload.Namespace, "kind", source.Spec.Workload.Kind) + //TODO: remove this log + + matchCondition := map[string]string{ + "name": source.Spec.Workload.Name, + "namespace": source.Spec.Workload.Namespace, + "kind": string(source.Spec.Workload.Kind), + } + matchConditions = append(matchConditions, matchCondition) + } + + filterConfig := map[string]interface{}{ + "match_conditions": matchConditions, + } + + allProcessors.Items = append(allProcessors.Items, odigosv1.Processor{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("odigossourcetodestinationfilter-%s", dest.Name), + }, + Spec: odigosv1.ProcessorSpec{ + Type: "odigossourcetodestinationfilterprocessor", + ProcessorConfig: runtime.RawExtension{Raw: marshalConfig(filterConfig)}, + CollectorRoles: []odigosv1.CollectorsGroupRole{ + odigosv1.CollectorsGroupRoleClusterGateway, + }, + OrderHint: len(allProcessors.Items) + 1, + Signals: dest.Spec.Signals, + }, + }) + //TODO: remove this log + logger.Info("Filter processor added successfully", "processorName", fmt.Sprintf("odigossourcetodestinationfilter-%s", dest.Name)) + //TODO: remove this log + + } +} + +func filterSources(sources []odigosv1.Source, selector *odigosv1.SourceSelector) []odigosv1.Source { + if selector == nil || selector.Mode == "all" { + return sources + } + + var filtered []odigosv1.Source + for _, source := range sources { + if selector.Mode == "namespaces" && contains(selector.Namespaces, source.Spec.Workload.Namespace) { + filtered = append(filtered, source) + } else if selector.Mode == "groups" && containsAny(selector.Groups, source.Spec.Groups) { + filtered = append(filtered, source) + } + } + return filtered +} + +func contains(arr []string, val string) bool { + for _, item := range arr { + if item == val { + return true + } + } + return false +} + +func containsAny(arr1, arr2 []string) bool { + for _, item1 := range arr1 { + for _, item2 := range arr2 { + if item1 == item2 { + return true + } + } + } + return false +} + +func buildFilterConditions(sources []odigosv1.Source) []string { + var conditions []string + for _, source := range sources { + conditions = append(conditions, source.Name) + } + return conditions +} + +func marshalConfig(config map[string]interface{}) []byte { + data, err := json.Marshal(config) + if err != nil { + log.Log.Error(err, "Failed to marshal processor config") + } + return data +} diff --git a/autoscaler/controllers/gateway/configmap.go b/autoscaler/controllers/gateway/configmap.go index c94c90327..874378412 100644 --- a/autoscaler/controllers/gateway/configmap.go +++ b/autoscaler/controllers/gateway/configmap.go @@ -111,10 +111,10 @@ func addSelfTelemetryPipeline(c *config.Config, ownTelemetryPort int32) error { return nil } -func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.ProcessorList, gateway *odigosv1.CollectorsGroup, ctx context.Context, c client.Client, scheme *runtime.Scheme) ([]odigoscommon.ObservabilitySignal, error) { +func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.ProcessorList, gateway *odigosv1.CollectorsGroup, ctx context.Context, c client.Client, scheme *runtime.Scheme, sources *odigosv1.SourceList) ([]odigoscommon.ObservabilitySignal, error) { logger := log.FromContext(ctx) memoryLimiterConfiguration := common.GetMemoryLimiterConfig(gateway.Spec.ResourcesSettings) - + common.AddFilterProcessors(allProcessors, dests, sources) processors := common.FilterAndSortProcessorsByOrderHint(allProcessors, odigosv1.CollectorsGroupRoleClusterGateway) desiredData, err, status, signals := config.Calculate( diff --git a/autoscaler/controllers/gateway/root.go b/autoscaler/controllers/gateway/root.go index 1869e35fe..a41bd685a 100644 --- a/autoscaler/controllers/gateway/root.go +++ b/autoscaler/controllers/gateway/root.go @@ -39,6 +39,12 @@ func Sync(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, return err } + var sources odigosv1.SourceList + if err := k8sClient.List(ctx, &sources); err != nil { + logger.Error(err, "Failed to list sources") + return err + } + var processors odigosv1.ProcessorList if err := k8sClient.List(ctx, &processors); err != nil { logger.Error(err, "Failed to list processors") @@ -47,7 +53,7 @@ func Sync(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, // Add the generic batch processor to the list of processors processors.Items = append(processors.Items, commonconf.GetGenericBatchProcessor()) - err = syncGateway(&dests, &processors, &gatewayCollectorGroup, ctx, k8sClient, scheme, imagePullSecrets, odigosVersion, config) + err = syncGateway(&dests, &processors, &gatewayCollectorGroup, ctx, k8sClient, scheme, imagePullSecrets, odigosVersion, config, &sources) statusPatchString := commonconf.GetCollectorsGroupDeployedConditionsPatch(err) statusErr := k8sClient.Status().Patch(ctx, &gatewayCollectorGroup, client.RawPatch(types.MergePatchType, []byte(statusPatchString))) if statusErr != nil { @@ -60,11 +66,11 @@ func Sync(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, func syncGateway(dests *odigosv1.DestinationList, processors *odigosv1.ProcessorList, gateway *odigosv1.CollectorsGroup, ctx context.Context, c client.Client, scheme *runtime.Scheme, imagePullSecrets []string, odigosVersion string, - config *controllerconfig.ControllerConfig) error { + config *controllerconfig.ControllerConfig, sources *odigosv1.SourceList) error { logger := log.FromContext(ctx) logger.V(0).Info("Syncing gateway") - signals, err := syncConfigMap(dests, processors, gateway, ctx, c, scheme) + signals, err := syncConfigMap(dests, processors, gateway, ctx, c, scheme, sources) if err != nil { logger.Error(err, "Failed to sync config map") return err diff --git a/collector/odigosotelcol/components.go b/collector/odigosotelcol/components.go index 1dc94fa3c..693e6c342 100644 --- a/collector/odigosotelcol/components.go +++ b/collector/odigosotelcol/components.go @@ -91,6 +91,7 @@ import ( transformprocessor "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor" remotetapprocessor "github.com/open-telemetry/opentelemetry-collector-contrib/processor/remotetapprocessor" odigostrafficmetrics "github.com/open-telemetry/opentelemetry-collector-contrib/odigos/processor/odigostrafficmetrics" + odigossourcetodestinationfilterprocessor "github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor" otlpreceiver "go.opentelemetry.io/collector/receiver/otlpreceiver" zipkinreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver" filelogreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver" @@ -256,6 +257,7 @@ func components() (otelcol.Factories, error) { transformprocessor.NewFactory(), remotetapprocessor.NewFactory(), odigostrafficmetrics.NewFactory(), + odigossourcetodestinationfilterprocessor.NewFactory(), ) if err != nil { return otelcol.Factories{}, err @@ -287,6 +289,7 @@ func components() (otelcol.Factories, error) { factories.ProcessorModules[transformprocessor.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor v0.106.0" factories.ProcessorModules[remotetapprocessor.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/processor/remotetapprocessor v0.106.0" factories.ProcessorModules[odigostrafficmetrics.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/odigos/processor/odigostrafficmetrics v0.106.0" + factories.ProcessorModules[odigossourcetodestinationfilterprocessor.NewFactory().Type()] = "github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor v0.106.0" factories.Connectors, err = connector.MakeFactoryMap( forwardconnector.NewFactory(), diff --git a/collector/odigosotelcol/go.mod b/collector/odigosotelcol/go.mod index c923bb9ed..6c078a947 100644 --- a/collector/odigosotelcol/go.mod +++ b/collector/odigosotelcol/go.mod @@ -4,7 +4,10 @@ module odigos.io/opentelemetry-collector/cmd/odigosotelcol go 1.23.0 +toolchain go1.23.4 + require ( + github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/datadogconnector v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector v0.106.0 @@ -605,4 +608,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/odigos/process replace go.opentelemetry.io/collector/odigos/providers/odigosfileprovider => ../providers/odigosfileprovider +replace github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor => ../processors/odigossourcetodestinationfilterprocessor + exclude github.com/knadh/koanf v1.5.0 diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/factory.go b/collector/processors/odigossourcetodestinationfilterprocessor/factory.go index 037961252..dc74e36bd 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/factory.go +++ b/collector/processors/odigossourcetodestinationfilterprocessor/factory.go @@ -14,6 +14,8 @@ func NewFactory() processor.Factory { component.MustNewType("odigossourcetodestinationfilterprocessor"), createDefaultConfig, processor.WithTraces(createTracesProcessor, component.StabilityLevelBeta), + processor.WithLogs(createLogsProcessor, component.StabilityLevelBeta), + processor.WithMetrics(createMetricsProcessor, component.StabilityLevelBeta), ) } @@ -43,3 +45,45 @@ func createTracesProcessor( processorhelper.WithCapabilities(consumer.Capabilities{MutatesData: true}), ) } + +func createLogsProcessor( + ctx context.Context, + set processor.Settings, + cfg component.Config, + nextConsumer consumer.Logs) (processor.Logs, error) { + + filterProc := &filterProcessor{ + logger: set.Logger, + config: cfg.(*Config), + } + + return processorhelper.NewLogsProcessor( + ctx, + set, + cfg, + nextConsumer, + filterProc.processLogs, + processorhelper.WithCapabilities(consumer.Capabilities{MutatesData: true}), + ) +} + +func createMetricsProcessor( + ctx context.Context, + set processor.Settings, + cfg component.Config, + nextConsumer consumer.Metrics) (processor.Metrics, error) { + + filterProc := &filterProcessor{ + logger: set.Logger, + config: cfg.(*Config), + } + + return processorhelper.NewMetricsProcessor( + ctx, + set, + cfg, + nextConsumer, + filterProc.processMetrics, + processorhelper.WithCapabilities(consumer.Capabilities{MutatesData: true}), + ) +} diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go b/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go index 2442a1317..db5ca6438 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go +++ b/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go @@ -36,6 +36,20 @@ func TestComponentLifecycle(t *testing.T) { createFn func(ctx context.Context, set processor.Settings, cfg component.Config) (component.Component, error) }{ + { + name: "logs", + createFn: func(ctx context.Context, set processor.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateLogsProcessor(ctx, set, cfg, consumertest.NewNop()) + }, + }, + + { + name: "metrics", + createFn: func(ctx context.Context, set processor.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateMetricsProcessor(ctx, set, cfg, consumertest.NewNop()) + }, + }, + { name: "traces", createFn: func(ctx context.Context, set processor.Settings, cfg component.Config) (component.Component, error) { diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go b/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go index 2a3ee7781..2cb2f390e 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go +++ b/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go @@ -11,5 +11,7 @@ var ( ) const ( - TracesStability = component.StabilityLevelBeta + TracesStability = component.StabilityLevelBeta + MetricsStability = component.StabilityLevelBeta + LogsStability = component.StabilityLevelBeta ) diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml b/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml index 21c1b03d9..280f01fd6 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml +++ b/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml @@ -5,6 +5,8 @@ status: stability: beta: - traces + - metrics + - logs distributions: - contrib codeowners: diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/processor.go b/collector/processors/odigossourcetodestinationfilterprocessor/processor.go index aaa4c007d..dec48003d 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/processor.go +++ b/collector/processors/odigossourcetodestinationfilterprocessor/processor.go @@ -3,6 +3,9 @@ package odigossourcetodestinationfilterprocessor import ( "context" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" "go.uber.org/zap" ) @@ -32,12 +35,69 @@ func (fp *filterProcessor) processTraces(ctx context.Context, td ptrace.Traces) return td, nil } -func (fp *filterProcessor) matches(span ptrace.Span, resourceSpan ptrace.ResourceSpans) bool { - attributes := resourceSpan.Resource().Attributes() +func (fp *filterProcessor) processMetrics(ctx context.Context, md pmetric.Metrics) (pmetric.Metrics, error) { + rMetrics := md.ResourceMetrics() + + for i := 0; i < rMetrics.Len(); i++ { + resourceMetric := rMetrics.At(i) + resourceAttributes := resourceMetric.Resource().Attributes() + ilMetrics := resourceMetric.ScopeMetrics() + + for j := 0; j < ilMetrics.Len(); j++ { + scopeMetric := ilMetrics.At(j) + metrics := scopeMetric.Metrics() + + metrics.RemoveIf(func(metric pmetric.Metric) bool { + return !fp.metricMatches(metric, resourceAttributes) + }) + } + } + + return md, nil +} + +func (fp *filterProcessor) metricMatches(metric pmetric.Metric, resourceAttributes pcommon.Map) bool { + for _, condition := range fp.config.MatchConditions { + name, _ := resourceAttributes.Get("name") + namespace, _ := resourceAttributes.Get("namespace") + kind, _ := resourceAttributes.Get("kind") + + if name.AsString() == condition.Name && + namespace.AsString() == condition.Namespace && + kind.AsString() == condition.Kind { + return true + } + } + + return false +} + +func (fp *filterProcessor) processLogs(ctx context.Context, ld plog.Logs) (plog.Logs, error) { + rLogs := ld.ResourceLogs() + + for i := 0; i < rLogs.Len(); i++ { + resourceLog := rLogs.At(i) + resourceAttributes := resourceLog.Resource().Attributes() + ilLogs := resourceLog.ScopeLogs() + + for j := 0; j < ilLogs.Len(); j++ { + scopeLog := ilLogs.At(j) + logRecords := scopeLog.LogRecords() + + logRecords.RemoveIf(func(log plog.LogRecord) bool { + return !fp.logMatches(log.Attributes(), resourceAttributes) + }) + } + } - name, _ := attributes.Get("name") - namespace, _ := attributes.Get("namespace") - kind, _ := attributes.Get("kind") + return ld, nil +} + +func (fp *filterProcessor) logMatches(logAttributes, resourceAttributes pcommon.Map) bool { + + name, _ := resourceAttributes.Get("name") + namespace, _ := resourceAttributes.Get("namespace") + kind, _ := resourceAttributes.Get("kind") for _, condition := range fp.config.MatchConditions { if name.AsString() == condition.Name && @@ -49,3 +109,54 @@ func (fp *filterProcessor) matches(span ptrace.Span, resourceSpan ptrace.Resourc return false } + +func (fp *filterProcessor) matches(span ptrace.Span, resourceSpan ptrace.ResourceSpans) bool { + attributes := resourceSpan.Resource().Attributes() + + namespace := getAttribute(attributes, "k8s.namespace.name") + if namespace == "" { + return false + } + + name, kind := getDynamicNameAndKind(attributes) + if name == "" || kind == "" { + return false + } + + for _, condition := range fp.config.MatchConditions { + if name == condition.Name && + namespace == condition.Namespace && + kind == condition.Kind { + return true + } + } + + return false +} + +func getDynamicNameAndKind(attributes pcommon.Map) (name string, kind string) { + + resourceTypes := []struct { + kind string + key string + }{ + {"deployment", "k8s.deployment.name"}, + {"statefulSet", "k8s.statefulset.name"}, + {"daemonSet", "k8s.daemonset.name"}, + } + + for _, resourceType := range resourceTypes { + if value, exists := attributes.Get(resourceType.key); exists { + return value.AsString(), resourceType.kind + } + } + + return "", "" +} + +func getAttribute(attributes pcommon.Map, key string) string { + if value, exists := attributes.Get(key); exists { + return value.AsString() + } + return "" +} From ccdcc4920be151c462af667d1b46e90bfce1e3c1 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 30 Dec 2024 19:45:15 +0200 Subject: [PATCH 178/259] Source CRD & Instru. Config handling in UI Backend (#2092) This pull request includes various changes across multiple files, focusing on updating configurations, refining comments, and enhancing functionality. The most important changes include updating Go version formatting in the build workflow, adding new annotations and constants, and refining permissions and roles in the resource files. ### Configuration Updates: * [`.github/workflows/build.yaml`](diffhunk://#diff-d0777657fa3fd81d23aaf7273e58aee453b04e67882517900c56daeef9b3e4c1L47-R47): Updated the Go version formatting from double quotes to single quotes in multiple job steps. [[1]](diffhunk://#diff-d0777657fa3fd81d23aaf7273e58aee453b04e67882517900c56daeef9b3e4c1L47-R47) [[2]](diffhunk://#diff-d0777657fa3fd81d23aaf7273e58aee453b04e67882517900c56daeef9b3e4c1L122-R122) [[3]](diffhunk://#diff-d0777657fa3fd81d23aaf7273e58aee453b04e67882517900c56daeef9b3e4c1L135-R135) [[4]](diffhunk://#diff-d0777657fa3fd81d23aaf7273e58aee453b04e67882517900c56daeef9b3e4c1L147-R147) [[5]](diffhunk://#diff-d0777657fa3fd81d23aaf7273e58aee453b04e67882517900c56daeef9b3e4c1L159-R159) ### Constants and Annotations: * [`common/consts/consts.go`](diffhunk://#diff-179a19de0058edabb08618c747a6bf22f25d8408fdeeeac6b888c9089c5c139fR18-R20): Added new annotations for workload namespace, kind, and name, as well as constants for CRD types. [[1]](diffhunk://#diff-179a19de0058edabb08618c747a6bf22f25d8408fdeeeac6b888c9089c5c139fR18-R20) [[2]](diffhunk://#diff-179a19de0058edabb08618c747a6bf22f25d8408fdeeeac6b888c9089c5c139fR32-R44) ### Permissions and Roles: * [`cli/cmd/resources/README.md`](diffhunk://#diff-7c3af7b93ef4c36c2e6a5b2866deb5b05c5cb4261ba09ff786c36a0385405bdcL8-R8): Updated the permissions table for various components, including adding new verbs and resources for the UI component. [[1]](diffhunk://#diff-7c3af7b93ef4c36c2e6a5b2866deb5b05c5cb4261ba09ff786c36a0385405bdcL8-R8) [[2]](diffhunk://#diff-7c3af7b93ef4c36c2e6a5b2866deb5b05c5cb4261ba09ff786c36a0385405bdcL34-R40) [[3]](diffhunk://#diff-7c3af7b93ef4c36c2e6a5b2866deb5b05c5cb4261ba09ff786c36a0385405bdcL59-R66) * [`cli/cmd/resources/ui.go`](diffhunk://#diff-c286e10d34710a80a59127b2b7951e8a33d9b9554e47d2f2b827fd690f2e53abL153-R158): Refined comments and resource definitions for roles, including CRUD operations and watch permissions for Odigos entities. [[1]](diffhunk://#diff-c286e10d34710a80a59127b2b7951e8a33d9b9554e47d2f2b827fd690f2e53abL153-R158) [[2]](diffhunk://#diff-c286e10d34710a80a59127b2b7951e8a33d9b9554e47d2f2b827fd690f2e53abL168-R168) [[3]](diffhunk://#diff-c286e10d34710a80a59127b2b7951e8a33d9b9554e47d2f2b827fd690f2e53abL217-R217) [[4]](diffhunk://#diff-c286e10d34710a80a59127b2b7951e8a33d9b9554e47d2f2b827fd690f2e53abL233-R245) ### Code Refinements: * [`frontend/endpoints/sse/sse.go`](diffhunk://#diff-282d3dde003340e1665a5c9880de17fbb1f94a6add776b513c5bcadc82b9ec68L14-R32): Added new message types and events for SSE messages. * [`frontend/graph/conversions.go`](diffhunk://#diff-019434fdf7b707424898bb20db99af0451a759ddf4678f82a3fbb2cee6bd3bd8L45-R48): Modified function to map instrumentation configurations to actual sources, including container runtime details. [[1]](diffhunk://#diff-019434fdf7b707424898bb20db99af0451a759ddf4678f82a3fbb2cee6bd3bd8L45-R48) [[2]](diffhunk://#diff-019434fdf7b707424898bb20db99af0451a759ddf4678f82a3fbb2cee6bd3bd8L62-L85) * [`frontend/graph/model/models_gen.go`](diffhunk://#diff-642ccd7ed71fdfa394bd7f7fd99c9c33e20ff18c876a91cb989d379a44390469L77-R78): Updated `ComputePlatform` struct to include new fields for actual sources and namespaces. ### Minor Changes: * [`Makefile`](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52L202-R202): Added `--nowait` flag to the `cli-install` target. * [`autoscaler/controllers/gateway/configmap.go`](diffhunk://#diff-5a57fdddd0c023b6580205ed7a5fb18405c69cc2861091d86fd1e6d692e634fbL153-R153): Corrected a capitalization error in a log message. These changes collectively improve the build process, enhance code readability, and ensure proper configuration and permissions management across the project. --------- Co-authored-by: Mike Dame Co-authored-by: Ron Federman --- .github/workflows/build.yaml | 12 +- Makefile | 2 +- autoscaler/controllers/gateway/configmap.go | 2 +- cli/cmd/resources/README.md | 77 +- cli/cmd/resources/ui.go | 21 +- common/consts/consts.go | 11 +- frontend/endpoints/sse/sse.go | 11 +- frontend/gqlgen.yml | 4 +- frontend/graph/conversions.go | 62 +- frontend/graph/generated.go | 1891 ++++------------- frontend/graph/model/models_gen.go | 75 +- frontend/graph/schema.graphqls | 51 +- frontend/graph/schema.resolvers.go | 99 +- frontend/kube/watchers/batcher.go | 14 +- frontend/kube/watchers/common.go | 10 +- frontend/kube/watchers/destination_watcher.go | 113 +- .../instrumentation_config_watcher.go | 139 ++ .../instrumentation_instance_watcher.go | 50 +- .../instrumented_application_watcher.go | 97 - frontend/main.go | 4 +- .../source_describe/source_describe.go | 8 +- frontend/services/namespaces.go | 5 +- frontend/services/sources.go | 192 +- frontend/services/utils.go | 44 +- frontend/webapp/app/page.tsx | 6 +- .../common/dropdowns/error-dropdown/index.tsx | 4 +- .../dropdowns/language-dropdown/index.tsx | 2 +- .../notification/notification-manager.tsx | 3 +- .../destinations/add-destination/index.tsx | 21 +- .../destinations-list/index.tsx | 2 +- .../rule-drawer/index.tsx | 9 +- .../overview/multi-source-control/index.tsx | 10 +- .../choose-source-modal/index.tsx | 14 +- .../sources-list/index.tsx | 10 +- .../sources-list/index.tsx | 9 +- .../build-drawer-item.ts | 7 +- .../sources/source-drawer-container/index.tsx | 14 +- frontend/webapp/cypress/constants/index.ts | 4 +- frontend/webapp/cypress/e2e/03-sources.cy.ts | 14 +- .../graphql/queries/compute-platform.ts | 41 +- frontend/webapp/graphql/queries/describe.ts | 16 +- .../webapp/hooks/actions/useActionCRUD.ts | 45 +- .../webapp/hooks/actions/useActionFormData.ts | 1 + .../compute-platform/useComputePlatform.ts | 26 +- .../hooks/compute-platform/useNamespace.ts | 35 +- .../hooks/destinations/useDestinationCRUD.ts | 52 +- .../destinations/useDestinationFormData.ts | 5 +- .../useInstrumentationRuleCRUD.ts | 59 +- .../useInstrumentationRuleFormData.ts | 1 + .../hooks/notification/useClickNotif.ts | 2 +- frontend/webapp/hooks/notification/useSSE.ts | 14 - .../webapp/hooks/sources/useSourceCRUD.ts | 76 +- .../webapp/hooks/sources/useSourceFormData.ts | 8 +- frontend/webapp/types/common.ts | 1 + frontend/webapp/types/compute-platform.ts | 4 +- frontend/webapp/types/describe.ts | 10 +- frontend/webapp/types/sources.ts | 12 +- .../utils/constants/programming-languages.ts | 27 +- .../strings/get-entity-label/index.ts | 2 +- .../strings/get-health-status/index.ts | 4 +- helm/odigos/templates/ui/clusterrole.yaml | 14 +- .../deleteinstrumentedapplication/manager.go | 22 +- .../source_controller.go | 2 +- k8sutils/pkg/describe/source.go | 20 +- k8sutils/pkg/describe/source/analyze.go | 61 +- k8sutils/pkg/describe/source/resources.go | 7 - k8sutils/pkg/predicate/creation.go | 2 +- k8sutils/pkg/workload/workload.go | 48 - 68 files changed, 1213 insertions(+), 2527 deletions(-) create mode 100644 frontend/kube/watchers/instrumentation_config_watcher.go delete mode 100644 frontend/kube/watchers/instrumented_application_watcher.go diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 55dce9449..1e6cd1f17 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -44,7 +44,7 @@ jobs: uses: docker/setup-buildx-action@v3 - uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Build Instrumentor Image uses: docker/build-push-action@v6 with: @@ -75,7 +75,7 @@ jobs: run: | make test build-and-test-odiglet: - runs-on: ubuntu-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx @@ -119,7 +119,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Set up Goreleaser uses: goreleaser/goreleaser-action@v5 with: @@ -132,7 +132,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Test k8sutils module working-directory: ./k8sutils run: | @@ -144,7 +144,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Test common module working-directory: ./common run: | @@ -156,7 +156,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "1.23.0" + go-version: '1.23.0' - name: Test procdiscovery module working-directory: ./procdiscovery run: | diff --git a/Makefile b/Makefile index 61f6679c7..96005d933 100644 --- a/Makefile +++ b/Makefile @@ -199,7 +199,7 @@ check-clean-work-tree: .PHONY: cli-install cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" - cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) + cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) --nowait .PHONY: cli-uninstall cli-uninstall: diff --git a/autoscaler/controllers/gateway/configmap.go b/autoscaler/controllers/gateway/configmap.go index c94c90327..f4070571f 100644 --- a/autoscaler/controllers/gateway/configmap.go +++ b/autoscaler/controllers/gateway/configmap.go @@ -150,7 +150,7 @@ func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.Proc logger.Error(err, "Failed to update destination error status conditions") } } else { - err := odgiosK8s.UpdateStatusConditions(ctx, c, &dest, &dest.Status.Conditions, metav1.ConditionTrue, destinationConfiguredType, "TransformedToOtelcolConfig", "destination successfully transformed to otelcol configuration") + err := odgiosK8s.UpdateStatusConditions(ctx, c, &dest, &dest.Status.Conditions, metav1.ConditionTrue, destinationConfiguredType, "TransformedToOtelcolConfig", "Destination successfully transformed to otelcol configuration") if err != nil { logger.Error(err, "Failed to update destination success status conditions") } diff --git a/cli/cmd/resources/README.md b/cli/cmd/resources/README.md index 37beeaf66..b6bb7d7be 100644 --- a/cli/cmd/resources/README.md +++ b/cli/cmd/resources/README.md @@ -4,38 +4,40 @@ In this doc, we'll keep track of the permissions requested across different reso ## ClusterRole -| Component | APIGroups | Resources | Verbs | Comments | -| ------------ | --------- | -------------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------- | -| Odiglet | "" | pods | get, list, watch | Needed for language detection. | -| Odiglet | "" | pods/status | get | Needed for language detection. | -| Odiglet | "" | nodes | get, list, watch | Needed for virtual device registration. | -| Odiglet | apps | deployments, daemonsets, statefulsets | get, list, watch | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | apps | deployments/status, daemonsets/status, statefulsets/status | get | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | apps | replicasets | get | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | odigos.io | instrumentedapplications | get, list, watch, create, patch, update | Stores runtime details and language detection (temporary until migration to configs). | -| Odiglet | odigos.io | instrumentedapplications/status | get, patch, update | Updates status of instrumented applications. | -| Odiglet | odigos.io | instrumentationinstances | create, get, list, patch, update, watch, delete | Manages instrumentation instances for process state storage. | -| Odiglet | odigos.io | instrumentationinstances/status | get, patch, update | Updates status of instrumentation instances. | -| Odiglet | odigos.io | instrumentationconfigs | get, list, watch, patch, update | Manages instrumentation configurations (future update for runtime details storage). | -| Odiglet | odigos.io | instrumentationconfigs/status | get, patch, update | Updates status of instrumentation configurations. | -| Odiglet | policy | podsecuritypolicies | use | Allows using privileged PSP (if enabled). | -| Instrumentor | "" | nodes, namespaces | get, list, watch | Tracks runtime detection and resource labels for instrumentation. | -| Instrumentor | apps | daemonsets, deployments, statefulsets | get, list, watch, update, patch | Adjusts pod specifications for instrumentation. | -| Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Reacts to runtime detections in workloads. | -| Instrumentor | odigos.io | instrumentedapplications/status | get, patch, update | Updates application statuses post-injection. | -| Instrumentor | odigos.io | instrumentationconfigs | create, delete, get, list, patch, update, watch | Manages instrumentation configurations. | -| Scheduler | odigos.io | instrumentationconfigs | get, list, watch | Monitors changes in instrumentation configurations for scheduling updates. | -| Autoscaler | odigos.io | instrumentationconfigs | get, list, watch | Reads instrumentation configurations to populate the `data-collector` configmaps. | -| Collector | "" | nodes/stats, nodes/proxy | get, list | Retrieves metrics for telemetry purposes. | -| Collector | "" | pods | get, list | Accesses metadata for resource name processors. | -| Collector | apps | replicasets, deployments, daemonsets, statefulsets | get, list | Fetches application details for instrumentation. | -| Collector | "" | endpoints | get, list, watch | Needed for load balancer. | -| Collector | policy | podsecuritypolicies | use | Supports clients enabling pod security policies (optional). | -| UI | "" | namespaces | get, list, patch | Required to retrieve and modify namespace configurations during instrumentation. | -| UI | "" | services, pods | get, list | Required for discovering potential destinations and describing application workloads. | -| UI | apps | deployments, statefulsets, daemonsets | get, list, patch, update | Needed for application instrumentation. | -| UI | apps | replicasets | get, list | Used for describing source and application configurations. | -| UI | odigos.io | instrumentedapplications, instrumentationinstances, instrumentationconfigs | get, list, watch | Used to retrieve and monitor instrumented applications and configurations. | +| Component | APIGroups | Resources | Verbs | Comments | +| ------------ | ----------------- | ---------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------- | +| Odiglet | "" | pods | get, list, watch | Needed for language detection. | +| Odiglet | "" | pods/status | get | Needed for language detection. | +| Odiglet | "" | nodes | get, list, watch | Needed for virtual device registration. | +| Odiglet | apps | deployments, daemonsets, statefulsets | get, list, watch | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | apps | deployments/status, daemonsets/status, statefulsets/status | get | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | apps | replicasets | get | Needed for language detection (temporary until new detection logic is ready). | +| Odiglet | odigos.io | instrumentedapplications | get, list, watch, create, patch, update | Stores runtime details and language detection (temporary until migration to configs). | +| Odiglet | odigos.io | instrumentedapplications/status | get, patch, update | Updates status of instrumented applications. | +| Odiglet | odigos.io | instrumentationinstances | create, get, list, patch, update, watch, delete | Manages instrumentation instances for process state storage. | +| Odiglet | odigos.io | instrumentationinstances/status | get, patch, update | Updates status of instrumentation instances. | +| Odiglet | odigos.io | instrumentationconfigs | get, list, watch, patch, update | Manages instrumentation configurations (future update for runtime details storage). | +| Odiglet | odigos.io | instrumentationconfigs/status | get, patch, update | Updates status of instrumentation configurations. | +| Odiglet | policy | podsecuritypolicies | use | Allows using privileged PSP (if enabled). | +| Instrumentor | "" | nodes, namespaces | get, list, watch | Tracks runtime detection and resource labels for instrumentation. | +| Instrumentor | apps | daemonsets, deployments, statefulsets | get, list, watch, update, patch | Adjusts pod specifications for instrumentation. | +| Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Reacts to runtime detections in workloads. | +| Instrumentor | odigos.io | instrumentedapplications/status | get, patch, update | Updates application statuses post-injection. | +| Instrumentor | odigos.io | instrumentationconfigs | create, delete, get, list, patch, update, watch | Manages instrumentation configurations. | +| Scheduler | odigos.io | instrumentationconfigs | get, list, watch | Monitors changes in instrumentation configurations for scheduling updates. | +| Autoscaler | odigos.io | instrumentationconfigs | get, list, watch | Reads instrumentation configurations to populate the `data-collector` configmaps. | +| Collector | "" | nodes/stats, nodes/proxy | get, list | Retrieves metrics for telemetry purposes. | +| Collector | "" | pods | get, list | Accesses metadata for resource name processors. | +| Collector | apps | replicasets, deployments, daemonsets, statefulsets | get, list | Fetches application details for instrumentation. | +| Collector | "" | endpoints | get, list, watch | Needed for load balancer. | +| Collector | policy | podsecuritypolicies | use | Supports clients enabling pod security policies (optional). | +| UI | "" | namespaces | get, list, patch | Needed to retrieve and instrument namespaces. | +| UI | "" | services, pods | get, list | Required for identifying and describing sources and potential destinations. | +| UI | apps | deployments, statefulsets, daemonsets | get, list, patch, update | Required for instrumentation of application sources. | +| UI | apps | replicasets | get, list | Needed for describing application and resource relationships. | +| UI | odigos.io | instrumentationconfigs, instrumentationinstances | get, list, watch | Monitors and retrieves configurations for instrumentation. | +| UI | odigos.io | sources | get, list, create, delete | Manages sources for instrumentation and monitoring. | +| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Handles pipeline actions for custom logic. | --- @@ -56,8 +58,9 @@ In this doc, we'll keep track of the permissions requested across different reso | Autoscaler | autoscaling | horizontalpodautoscalers | create, patch, update, delete | Implements autoscaling for gateway collectors. | | Autoscaler | odigos.io | destinations | get, list, watch | Tracks and synchronizes destination configurations. | | Autoscaler | odigos.io | collectorsgroups, destinations/status | get, patch, update | Monitors and updates statuses of collectors groups and destinations. | -| UI | "" | configmaps | get, list | Accesses `odigos-config` for UI configuration settings. | -| UI | "" | secrets | get, list, create, patch, update | Manages destination secrets. | -| UI | odigos.io | instrumentationrules, destinations | get, list, create, patch, update, delete, watch | CRUD operations for destinations and instrumentation rules. | -| UI | odigos.io | collectorsgroups | get, list | Monitors instrumentation groups. | -| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Handles pipeline actions for custom logic. | +| UI | "" | configmaps | get, list | Reads `odigos-config` for UI configuration. | +| UI | "" | secrets | get, list, create, patch, update | Manages destination secrets for configurations. | +| UI | odigos.io | instrumentationrules, destinations | get, list, create, patch, update, delete | CRUD for destinations and instrumentation rules. | +| UI | odigos.io | collectorsgroups | get, list | Monitors groupings of collectors for UI updates. | +| UI | odigos.io | sources | get, list, create, delete | Manages sources for instrumentation and monitoring. | +| UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Pipeline action management. | diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index de9a2ca14..f87d7ee84 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -150,12 +150,12 @@ func NewUIRole(ns string) *rbacv1.Role { Resources: []string{"secrets"}, Verbs: []string{"get", "list", "create", "patch", "update"}, }, - { // Needed for CRUD on Odigos entities + { // Needed for CRUD on instr. rule and destinations APIGroups: []string{"odigos.io"}, Resources: []string{"instrumentationrules", "destinations"}, Verbs: []string{"get", "list", "create", "patch", "update", "delete"}, }, - { // Needed to watch Odigos entities + { // Needed to notify UI about changes with destinations APIGroups: []string{"odigos.io"}, Resources: []string{"destinations"}, Verbs: []string{"watch"}, @@ -165,7 +165,7 @@ func NewUIRole(ns string) *rbacv1.Role { Resources: []string{"collectorsgroups"}, Verbs: []string{"get", "list"}, }, - { // Needed for CRUD on Pipeline Actions + { // Needed for CRUD on pipeline actions APIGroups: []string{"actions.odigos.io"}, Resources: []string{"*"}, Verbs: []string{"get", "list", "create", "patch", "update", "delete"}, @@ -214,7 +214,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Resources: []string{"namespaces"}, Verbs: []string{"get", "list", "patch"}, }, - { // Needed to instrument applications + { // Needed to get and instrument sources APIGroups: []string{"apps"}, Resources: []string{"deployments", "statefulsets", "daemonsets"}, Verbs: []string{"get", "list", "patch", "update"}, @@ -230,14 +230,19 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Resources: []string{"services", "pods"}, Verbs: []string{"get", "list"}, }, - { // Needed to read Odigos entities + { // Needed to get sources APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances", "instrumentationconfigs"}, + Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, Verbs: []string{"get", "list"}, }, - { // Needed to watch Odigos entities + { // Needed to instrument / uninstrument sources + APIGroups: []string{"odigos.io"}, + Resources: []string{"sources"}, + Verbs: []string{"get", "list", "create", "delete"}, + }, + { // Needed to notify UI about changes with sources APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications", "instrumentationinstances"}, + Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, Verbs: []string{"watch"}, }, }, diff --git a/common/consts/consts.go b/common/consts/consts.go index e84a8c08d..a98d34179 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -15,6 +15,9 @@ const ( OdigosInstrumentationLabel = "odigos-instrumentation" InstrumentationEnabled = "enabled" InstrumentationDisabled = "disabled" + OdigosNamespaceAnnotation = "odigos.io/workload-namespace" + OdigosWorkloadKindAnnotation = "odigos.io/workload-kind" + OdigosWorkloadNameAnnotation = "odigos.io/workload-name" OdigosReportedNameAnnotation = "odigos.io/reported-name" // GatewayMaxConnectionAge and GatewayMaxConnectionAgeGrace are the default values for the gateway collector. @@ -26,13 +29,19 @@ const ( // or odigos is uninstalled. // Should only be used for environment variables that are modified by odigos. ManifestEnvOriginalValAnnotation = "odigos.io/manifest-env-original-val" + // Used to label instrumentation instances by the corresponding // instrumented app for better query performance. InstrumentedAppNameLabel = "instrumented-app" + + // CRD types + InstrumentationConfig = "InstrumentationConfig" + InstrumentationInstance = "InstrumentationInstance" + Destination = "Destination" ) var ( - PodsNotFoundErr = errors.New("could not find a ready pod") + ErrorPodsNotFound = errors.New("could not find a ready pod") ) var ( diff --git a/frontend/endpoints/sse/sse.go b/frontend/endpoints/sse/sse.go index afffa9115..4215a8a49 100644 --- a/frontend/endpoints/sse/sse.go +++ b/frontend/endpoints/sse/sse.go @@ -11,22 +11,25 @@ import ( type MessageType string const ( - MessageTypeSuccess MessageType = "success" + MessageTypeWarning MessageType = "warning" MessageTypeError MessageType = "error" + MessageTypeSuccess MessageType = "success" + MessageTypeInfo MessageType = "info" + MessageTypeDefault MessageType = "default" ) type MessageEvent string const ( - MessageEventDeleted MessageEvent = "Deleted" - MessageEventModified MessageEvent = "Modified" MessageEventAdded MessageEvent = "Added" + MessageEventDeleted MessageEvent = "Deleted" + MessageEventModified MessageEvent = "Modified" ) type SSEMessage struct { Type MessageType `json:"type"` - Data string `json:"data"` Event MessageEvent `json:"event"` + Data string `json:"data"` Target string `json:"target"` CRDType string `json:"crdType"` } diff --git a/frontend/gqlgen.yml b/frontend/gqlgen.yml index 562c27730..f5dedfb41 100644 --- a/frontend/gqlgen.yml +++ b/frontend/gqlgen.yml @@ -50,12 +50,12 @@ models: resolver: true k8sActualSource: resolver: true + k8sActualSources: + resolver: true destinations: resolver: true actions: resolver: true - k8sActualSources: - resolver: true instrumentationRules: resolver: true K8sActualNamespace: diff --git a/frontend/graph/conversions.go b/frontend/graph/conversions.go index 67746ccc1..21b91c5ad 100644 --- a/frontend/graph/conversions.go +++ b/frontend/graph/conversions.go @@ -21,31 +21,10 @@ func k8sKindToGql(k8sResourceKind string) model.K8sResourceKind { return "" } -func k8sConditionStatusToGql(status v1.ConditionStatus) model.ConditionStatus { - switch status { - case v1.ConditionTrue: - return model.ConditionStatusTrue - case v1.ConditionFalse: - return model.ConditionStatusFalse - case v1.ConditionUnknown: - return model.ConditionStatusUnknown - } - return model.ConditionStatusUnknown - -} - -func k8sLastTransitionTimeToGql(t v1.Time) *string { - if t.IsZero() { - return nil - } - str := t.UTC().Format(time.RFC3339) - return &str -} - -func instrumentedApplicationToActualSource(instrumentedApp v1alpha1.InstrumentedApplication) *model.K8sActualSource { - // Map the container runtime details +func instrumentationConfigToActualSource(instruConfig v1alpha1.InstrumentationConfig) *model.K8sActualSource { + // Map the containers runtime details var containers []*model.SourceContainerRuntimeDetails - for _, container := range instrumentedApp.Spec.RuntimeDetails { + for _, container := range instruConfig.Status.RuntimeDetailsByContainer { var otherAgentName *string if container.OtherAgent != nil { otherAgentName = &container.OtherAgent.Name @@ -59,30 +38,27 @@ func instrumentedApplicationToActualSource(instrumentedApp v1alpha1.Instrumented }) } - // Map the conditions of the application + // Map the conditions var conditions []*model.Condition - for _, condition := range instrumentedApp.Status.Conditions { - conditions = append(conditions, &model.Condition{ - Type: condition.Type, - Status: k8sConditionStatusToGql(condition.Status), - Reason: &condition.Reason, - LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), - Message: &condition.Message, - }) - } + // for _, condition := range instruConfig.Status.Conditions { + // conditions = append(conditions, &model.Condition{ + // Status: k8sConditionStatusToGql(condition.Status), + // Type: condition.Type, + // Reason: &condition.Reason, + // Message: &condition.Message, + // LastTransitionTime: k8sLastTransitionTimeToGql(condition.LastTransitionTime), + // }) + // } // Return the converted K8sActualSource object return &model.K8sActualSource{ - Namespace: instrumentedApp.Namespace, - Kind: k8sKindToGql(instrumentedApp.OwnerReferences[0].Kind), - Name: instrumentedApp.OwnerReferences[0].Name, - ServiceName: &instrumentedApp.Name, + Namespace: instruConfig.Namespace, + Kind: k8sKindToGql(instruConfig.OwnerReferences[0].Kind), + Name: instruConfig.OwnerReferences[0].Name, NumberOfInstances: nil, - AutoInstrumented: instrumentedApp.Spec.Options != nil, - InstrumentedApplicationDetails: &model.InstrumentedApplicationDetails{ - Containers: containers, - Conditions: conditions, - }, + ReportedName: &instruConfig.Spec.ServiceName, + Containers: containers, + Conditions: conditions, } } diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index caba2e253..b41a613fe 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -85,7 +85,6 @@ type ComplexityRoot struct { InstrumentationRules func(childComplexity int) int K8sActualNamespace func(childComplexity int, name string) int K8sActualNamespaces func(childComplexity int) int - K8sActualSource func(childComplexity int, name *string, namespace *string, kind *string) int K8sActualSources func(childComplexity int) int } @@ -218,6 +217,7 @@ type ComplexityRoot struct { } InstrumentationConfigAnalyze struct { + Containers func(childComplexity int) int CreateTime func(childComplexity int) int Created func(childComplexity int) int } @@ -240,22 +240,12 @@ type ComplexityRoot struct { Workload func(childComplexity int) int } - InstrumentationLibrary struct { - LibraryName func(childComplexity int) int - Options func(childComplexity int) int - } - InstrumentationLibraryGlobalId struct { Language func(childComplexity int) int Name func(childComplexity int) int SpanKind func(childComplexity int) int } - InstrumentationOption struct { - OptionKey func(childComplexity int) int - SpanKind func(childComplexity int) int - } - InstrumentationRule struct { Disabled func(childComplexity int) int InstrumentationLibraries func(childComplexity int) int @@ -266,34 +256,20 @@ type ComplexityRoot struct { Workloads func(childComplexity int) int } - InstrumentedApplicationAnalyze struct { - Containers func(childComplexity int) int - CreateTime func(childComplexity int) int - Created func(childComplexity int) int - } - - InstrumentedApplicationDetails struct { - Conditions func(childComplexity int) int - Containers func(childComplexity int) int - InstrumentationOptions func(childComplexity int) int - } - K8sActualNamespace struct { - InstrumentationLabelEnabled func(childComplexity int) int - K8sActualSources func(childComplexity int, instrumentationLabeled *bool) int - Name func(childComplexity int) int + K8sActualSources func(childComplexity int) int + Name func(childComplexity int) int } K8sActualSource struct { - AutoInstrumented func(childComplexity int) int - AutoInstrumentedDecision func(childComplexity int) int - InstrumentedApplicationDetails func(childComplexity int) int - Kind func(childComplexity int) int - Name func(childComplexity int) int - Namespace func(childComplexity int) int - NumberOfInstances func(childComplexity int) int - ReportedName func(childComplexity int) int - ServiceName func(childComplexity int) int + Conditions func(childComplexity int) int + Containers func(childComplexity int) int + Kind func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + NumberOfInstances func(childComplexity int) int + ReportedName func(childComplexity int) int + Selected func(childComplexity int) int } LatencySamplerAction struct { @@ -453,17 +429,16 @@ type ComplexityRoot struct { } SourceAnalyze struct { - InstrumentationConfig func(childComplexity int) int - InstrumentationDevice func(childComplexity int) int - InstrumentedApplication func(childComplexity int) int - Kind func(childComplexity int) int - Labels func(childComplexity int) int - Name func(childComplexity int) int - Namespace func(childComplexity int) int - Pods func(childComplexity int) int - PodsPhasesCount func(childComplexity int) int - RuntimeInfo func(childComplexity int) int - TotalPods func(childComplexity int) int + InstrumentationConfig func(childComplexity int) int + InstrumentationDevice func(childComplexity int) int + Kind func(childComplexity int) int + Labels func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + Pods func(childComplexity int) int + PodsPhasesCount func(childComplexity int) int + RuntimeInfo func(childComplexity int) int + TotalPods func(childComplexity int) int } SourceContainerRuntimeDetails struct { @@ -489,9 +464,8 @@ type ComplexityRoot struct { } type ComputePlatformResolver interface { - K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) - K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) + K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) @@ -503,7 +477,7 @@ type DestinationResolver interface { Conditions(ctx context.Context, obj *model.Destination) ([]*model.Condition, error) } type K8sActualNamespaceResolver interface { - K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace, instrumentationLabeled *bool) ([]*model.K8sActualSource, error) + K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace) ([]*model.K8sActualSource, error) } type MutationResolver interface { CreateNewDestination(ctx context.Context, destination model.DestinationInput) (*model.Destination, error) @@ -730,18 +704,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ComputePlatform.K8sActualNamespaces(childComplexity), true - case "ComputePlatform.k8sActualSource": - if e.complexity.ComputePlatform.K8sActualSource == nil { - break - } - - args, err := ec.field_ComputePlatform_k8sActualSource_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.ComputePlatform.K8sActualSource(childComplexity, args["name"].(*string), args["namespace"].(*string), args["kind"].(*string)), true - case "ComputePlatform.k8sActualSources": if e.complexity.ComputePlatform.K8sActualSources == nil { break @@ -1246,6 +1208,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.HttpPayloadCollection.MimeTypes(childComplexity), true + case "InstrumentationConfigAnalyze.containers": + if e.complexity.InstrumentationConfigAnalyze.Containers == nil { + break + } + + return e.complexity.InstrumentationConfigAnalyze.Containers(childComplexity), true + case "InstrumentationConfigAnalyze.createTime": if e.complexity.InstrumentationConfigAnalyze.CreateTime == nil { break @@ -1323,20 +1292,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationLabelsAnalyze.Workload(childComplexity), true - case "InstrumentationLibrary.libraryName": - if e.complexity.InstrumentationLibrary.LibraryName == nil { - break - } - - return e.complexity.InstrumentationLibrary.LibraryName(childComplexity), true - - case "InstrumentationLibrary.options": - if e.complexity.InstrumentationLibrary.Options == nil { - break - } - - return e.complexity.InstrumentationLibrary.Options(childComplexity), true - case "InstrumentationLibraryGlobalId.language": if e.complexity.InstrumentationLibraryGlobalId.Language == nil { break @@ -1358,20 +1313,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationLibraryGlobalId.SpanKind(childComplexity), true - case "InstrumentationOption.optionKey": - if e.complexity.InstrumentationOption.OptionKey == nil { - break - } - - return e.complexity.InstrumentationOption.OptionKey(childComplexity), true - - case "InstrumentationOption.spanKind": - if e.complexity.InstrumentationOption.SpanKind == nil { - break - } - - return e.complexity.InstrumentationOption.SpanKind(childComplexity), true - case "InstrumentationRule.disabled": if e.complexity.InstrumentationRule.Disabled == nil { break @@ -1421,66 +1362,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.InstrumentationRule.Workloads(childComplexity), true - case "InstrumentedApplicationAnalyze.containers": - if e.complexity.InstrumentedApplicationAnalyze.Containers == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.Containers(childComplexity), true - - case "InstrumentedApplicationAnalyze.createTime": - if e.complexity.InstrumentedApplicationAnalyze.CreateTime == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.CreateTime(childComplexity), true - - case "InstrumentedApplicationAnalyze.created": - if e.complexity.InstrumentedApplicationAnalyze.Created == nil { - break - } - - return e.complexity.InstrumentedApplicationAnalyze.Created(childComplexity), true - - case "InstrumentedApplicationDetails.conditions": - if e.complexity.InstrumentedApplicationDetails.Conditions == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.Conditions(childComplexity), true - - case "InstrumentedApplicationDetails.containers": - if e.complexity.InstrumentedApplicationDetails.Containers == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.Containers(childComplexity), true - - case "InstrumentedApplicationDetails.instrumentationOptions": - if e.complexity.InstrumentedApplicationDetails.InstrumentationOptions == nil { - break - } - - return e.complexity.InstrumentedApplicationDetails.InstrumentationOptions(childComplexity), true - - case "K8sActualNamespace.instrumentationLabelEnabled": - if e.complexity.K8sActualNamespace.InstrumentationLabelEnabled == nil { - break - } - - return e.complexity.K8sActualNamespace.InstrumentationLabelEnabled(childComplexity), true - case "K8sActualNamespace.k8sActualSources": if e.complexity.K8sActualNamespace.K8sActualSources == nil { break } - args, err := ec.field_K8sActualNamespace_k8sActualSources_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.K8sActualNamespace.K8sActualSources(childComplexity, args["instrumentationLabeled"].(*bool)), true + return e.complexity.K8sActualNamespace.K8sActualSources(childComplexity), true case "K8sActualNamespace.name": if e.complexity.K8sActualNamespace.Name == nil { @@ -1489,26 +1376,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualNamespace.Name(childComplexity), true - case "K8sActualSource.autoInstrumented": - if e.complexity.K8sActualSource.AutoInstrumented == nil { - break - } - - return e.complexity.K8sActualSource.AutoInstrumented(childComplexity), true - - case "K8sActualSource.autoInstrumentedDecision": - if e.complexity.K8sActualSource.AutoInstrumentedDecision == nil { + case "K8sActualSource.conditions": + if e.complexity.K8sActualSource.Conditions == nil { break } - return e.complexity.K8sActualSource.AutoInstrumentedDecision(childComplexity), true + return e.complexity.K8sActualSource.Conditions(childComplexity), true - case "K8sActualSource.instrumentedApplicationDetails": - if e.complexity.K8sActualSource.InstrumentedApplicationDetails == nil { + case "K8sActualSource.containers": + if e.complexity.K8sActualSource.Containers == nil { break } - return e.complexity.K8sActualSource.InstrumentedApplicationDetails(childComplexity), true + return e.complexity.K8sActualSource.Containers(childComplexity), true case "K8sActualSource.kind": if e.complexity.K8sActualSource.Kind == nil { @@ -1545,12 +1425,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualSource.ReportedName(childComplexity), true - case "K8sActualSource.serviceName": - if e.complexity.K8sActualSource.ServiceName == nil { + case "K8sActualSource.selected": + if e.complexity.K8sActualSource.Selected == nil { break } - return e.complexity.K8sActualSource.ServiceName(childComplexity), true + return e.complexity.K8sActualSource.Selected(childComplexity), true case "LatencySamplerAction.details": if e.complexity.LatencySamplerAction.Details == nil { @@ -2334,13 +2214,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SourceAnalyze.InstrumentationDevice(childComplexity), true - case "SourceAnalyze.instrumentedApplication": - if e.complexity.SourceAnalyze.InstrumentedApplication == nil { - break - } - - return e.complexity.SourceAnalyze.InstrumentedApplication(childComplexity), true - case "SourceAnalyze.kind": if e.complexity.SourceAnalyze.Kind == nil { break @@ -2638,54 +2511,6 @@ func (ec *executionContext) field_ComputePlatform_k8sActualNamespace_args(ctx co return args, nil } -func (ec *executionContext) field_ComputePlatform_k8sActualSource_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 *string - if tmp, ok := rawArgs["name"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["name"] = arg0 - var arg1 *string - if tmp, ok := rawArgs["namespace"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) - arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["namespace"] = arg1 - var arg2 *string - if tmp, ok := rawArgs["kind"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("kind")) - arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } - } - args["kind"] = arg2 - return args, nil -} - -func (ec *executionContext) field_K8sActualNamespace_k8sActualSources_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 *bool - if tmp, ok := rawArgs["instrumentationLabeled"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("instrumentationLabeled")) - arg0, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) - if err != nil { - return nil, err - } - } - args["instrumentationLabeled"] = arg0 - return args, nil -} - func (ec *executionContext) field_Mutation_createAction_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -3992,66 +3817,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_computePlatformType(_ c return fc, nil } -func (ec *executionContext) _ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().K8sActualNamespace(rctx, obj, fc.Args["name"].(string)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*model.K8sActualNamespace) - fc.Result = res - return ec.marshalOK8sActualNamespace2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualNamespace(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ComputePlatform", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_K8sActualNamespace_name(ctx, field) - case "instrumentationLabelEnabled": - return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) - case "k8sActualSources": - return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type K8sActualNamespace", field.Name) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_ComputePlatform_k8sActualNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - func (ec *executionContext) _ComputePlatform_k8sActualNamespaces(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) if err != nil { @@ -4093,8 +3858,6 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespaces(_ c switch field.Name { case "name": return ec.fieldContext_K8sActualNamespace_name(ctx, field) - case "instrumentationLabelEnabled": - return ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) case "k8sActualSources": return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } @@ -4104,8 +3867,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespaces(_ c return fc, nil } -func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) +func (ec *executionContext) _ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) if err != nil { return graphql.Null } @@ -4118,7 +3881,7 @@ func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().K8sActualSource(rctx, obj, fc.Args["name"].(*string), fc.Args["namespace"].(*string), fc.Args["kind"].(*string)) + return ec.resolvers.ComputePlatform().K8sActualNamespace(rctx, obj, fc.Args["name"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -4127,12 +3890,12 @@ func (ec *executionContext) _ComputePlatform_k8sActualSource(ctx context.Context if resTmp == nil { return graphql.Null } - res := resTmp.(*model.K8sActualSource) + res := resTmp.(*model.K8sActualNamespace) fc.Result = res - return ec.marshalOK8sActualSource2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualSource(ctx, field.Selections, res) + return ec.marshalOK8sActualNamespace2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualNamespace(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ComputePlatform", Field: field, @@ -4140,26 +3903,12 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx con IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "namespace": - return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": - return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) - case "numberOfInstances": - return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) - case "reportedName": - return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + return ec.fieldContext_K8sActualNamespace_name(ctx, field) + case "k8sActualSources": + return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) + return nil, fmt.Errorf("no field named %q was found under type K8sActualNamespace", field.Name) }, } defer func() { @@ -4169,7 +3918,7 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSource(ctx con } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_ComputePlatform_k8sActualSource_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_ComputePlatform_k8sActualNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -4217,22 +3966,20 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont switch field.Name { case "namespace": return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) + case "kind": + return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "selected": + return ec.fieldContext_K8sActualSource_selected(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + case "containers": + return ec.fieldContext_K8sActualSource_containers(ctx, field) + case "conditions": + return ec.fieldContext_K8sActualSource_conditions(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) }, @@ -7754,6 +7501,60 @@ func (ec *executionContext) fieldContext_InstrumentationConfigAnalyze_createTime return fc, nil } +func (ec *executionContext) _InstrumentationConfigAnalyze_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationConfigAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationConfigAnalyze_containers(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Containers, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ContainerRuntimeInfoAnalyze) + fc.Result = res + return ec.marshalNContainerRuntimeInfoAnalyze2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐContainerRuntimeInfoAnalyzeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_InstrumentationConfigAnalyze_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "InstrumentationConfigAnalyze", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "containerName": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_containerName(ctx, field) + case "language": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_language(ctx, field) + case "runtimeVersion": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_runtimeVersion(ctx, field) + case "envVars": + return ec.fieldContext_ContainerRuntimeInfoAnalyze_envVars(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ContainerRuntimeInfoAnalyze", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _InstrumentationDeviceAnalyze_statusText(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationDeviceAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_InstrumentationDeviceAnalyze_statusText(ctx, field) if err != nil { @@ -8226,8 +8027,8 @@ func (ec *executionContext) fieldContext_InstrumentationLabelsAnalyze_instrument return fc, nil } -func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibrary) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibrary_libraryName(ctx, field) +func (ec *executionContext) _InstrumentationLibraryGlobalId_name(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_name(ctx, field) if err != nil { return graphql.Null } @@ -8240,7 +8041,7 @@ func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Cont }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.LibraryName, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -8257,103 +8058,9 @@ func (ec *executionContext) _InstrumentationLibrary_libraryName(ctx context.Cont return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationLibrary_libraryName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "InstrumentationLibrary", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationLibrary_options(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibrary) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibrary_options(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Options, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.InstrumentationOption) - fc.Result = res - return ec.marshalNInstrumentationOption2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOptionᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationLibrary_options(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationLibrary", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "optionKey": - return ec.fieldContext_InstrumentationOption_optionKey(ctx, field) - case "spanKind": - return ec.fieldContext_InstrumentationOption_spanKind(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationOption", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationLibraryGlobalId_name(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationLibraryGlobalID) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationLibraryGlobalId_name(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Name, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationLibraryGlobalId", + Object: "InstrumentationLibraryGlobalId", Field: field, IsMethod: false, IsResolver: false, @@ -8446,94 +8153,6 @@ func (ec *executionContext) fieldContext_InstrumentationLibraryGlobalId_language return fc, nil } -func (ec *executionContext) _InstrumentationOption_optionKey(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationOption) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationOption_optionKey(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.OptionKey, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationOption_optionKey(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationOption", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentationOption_spanKind(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationOption) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentationOption_spanKind(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.SpanKind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(model.SpanKind) - fc.Result = res - return ec.marshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentationOption_spanKind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationOption", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type SpanKind does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _InstrumentationRule_ruleId(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentationRule) (ret graphql.Marshaler) { fc, err := ec.fieldContext_InstrumentationRule_ruleId(ctx, field) if err != nil { @@ -8739,10 +8358,10 @@ func (ec *executionContext) fieldContext_InstrumentationRule_workloads(_ context switch field.Name { case "namespace": return ec.fieldContext_PodWorkload_namespace(ctx, field) - case "kind": - return ec.fieldContext_PodWorkload_kind(ctx, field) case "name": return ec.fieldContext_PodWorkload_name(ctx, field) + case "kind": + return ec.fieldContext_PodWorkload_kind(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type PodWorkload", field.Name) }, @@ -8827,388 +8446,31 @@ func (ec *executionContext) _InstrumentationRule_payloadCollection(ctx context.C return ec.marshalOPayloadCollection2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPayloadCollection(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_InstrumentationRule_payloadCollection(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentationRule", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "httpRequest": - return ec.fieldContext_PayloadCollection_httpRequest(ctx, field) - case "httpResponse": - return ec.fieldContext_PayloadCollection_httpResponse(ctx, field) - case "dbQuery": - return ec.fieldContext_PayloadCollection_dbQuery(ctx, field) - case "messaging": - return ec.fieldContext_PayloadCollection_messaging(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type PayloadCollection", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationAnalyze_created(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_created(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Created, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.EntityProperty) - fc.Result = res - return ec.marshalNEntityProperty2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐEntityProperty(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_created(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_EntityProperty_name(ctx, field) - case "value": - return ec.fieldContext_EntityProperty_value(ctx, field) - case "status": - return ec.fieldContext_EntityProperty_status(ctx, field) - case "explain": - return ec.fieldContext_EntityProperty_explain(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationAnalyze_createTime(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_createTime(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.CreateTime, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*model.EntityProperty) - fc.Result = res - return ec.marshalOEntityProperty2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐEntityProperty(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_createTime(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_EntityProperty_name(ctx, field) - case "value": - return ec.fieldContext_EntityProperty_value(ctx, field) - case "status": - return ec.fieldContext_EntityProperty_status(ctx, field) - case "explain": - return ec.fieldContext_EntityProperty_explain(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationAnalyze_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationAnalyze_containers(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Containers, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.ContainerRuntimeInfoAnalyze) - fc.Result = res - return ec.marshalNContainerRuntimeInfoAnalyze2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐContainerRuntimeInfoAnalyzeᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationAnalyze_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "containerName": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_containerName(ctx, field) - case "language": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_language(ctx, field) - case "runtimeVersion": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_runtimeVersion(ctx, field) - case "envVars": - return ec.fieldContext_ContainerRuntimeInfoAnalyze_envVars(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ContainerRuntimeInfoAnalyze", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationDetails_containers(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_containers(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Containers, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.SourceContainerRuntimeDetails) - fc.Result = res - return ec.marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "containerName": - return ec.fieldContext_SourceContainerRuntimeDetails_containerName(ctx, field) - case "language": - return ec.fieldContext_SourceContainerRuntimeDetails_language(ctx, field) - case "runtimeVersion": - return ec.fieldContext_SourceContainerRuntimeDetails_runtimeVersion(ctx, field) - case "otherAgent": - return ec.fieldContext_SourceContainerRuntimeDetails_otherAgent(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type SourceContainerRuntimeDetails", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationDetails_conditions(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_conditions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Conditions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]*model.Condition) - fc.Result = res - return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "status": - return ec.fieldContext_Condition_status(ctx, field) - case "type": - return ec.fieldContext_Condition_type(ctx, field) - case "reason": - return ec.fieldContext_Condition_reason(ctx, field) - case "message": - return ec.fieldContext_Condition_message(ctx, field) - case "lastTransitionTime": - return ec.fieldContext_Condition_lastTransitionTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _InstrumentedApplicationDetails_instrumentationOptions(ctx context.Context, field graphql.CollectedField, obj *model.InstrumentedApplicationDetails) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_InstrumentedApplicationDetails_instrumentationOptions(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.InstrumentationOptions, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.InstrumentationLibrary) - fc.Result = res - return ec.marshalNInstrumentationLibrary2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_InstrumentedApplicationDetails_instrumentationOptions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "InstrumentedApplicationDetails", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "libraryName": - return ec.fieldContext_InstrumentationLibrary_libraryName(ctx, field) - case "options": - return ec.fieldContext_InstrumentationLibrary_options(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationLibrary", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _K8sActualNamespace_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualNamespace_name(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Name, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_K8sActualNamespace_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_InstrumentationRule_payloadCollection(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "K8sActualNamespace", + Object: "InstrumentationRule", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "httpRequest": + return ec.fieldContext_PayloadCollection_httpRequest(ctx, field) + case "httpResponse": + return ec.fieldContext_PayloadCollection_httpResponse(ctx, field) + case "dbQuery": + return ec.fieldContext_PayloadCollection_dbQuery(ctx, field) + case "messaging": + return ec.fieldContext_PayloadCollection_messaging(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PayloadCollection", field.Name) }, } return fc, nil } -func (ec *executionContext) _K8sActualNamespace_instrumentationLabelEnabled(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualNamespace_instrumentationLabelEnabled(ctx, field) +func (ec *executionContext) _K8sActualNamespace_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualNamespace_name(ctx, field) if err != nil { return graphql.Null } @@ -9221,28 +8483,31 @@ func (ec *executionContext) _K8sActualNamespace_instrumentationLabelEnabled(ctx }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.InstrumentationLabelEnabled, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*bool) + res := resTmp.(string) fc.Result = res - return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualNamespace_instrumentationLabelEnabled(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualNamespace_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualNamespace", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil @@ -9262,7 +8527,7 @@ func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Con }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.K8sActualNamespace().K8sActualSources(rctx, obj, fc.Args["instrumentationLabeled"].(*bool)) + return ec.resolvers.K8sActualNamespace().K8sActualSources(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -9279,7 +8544,7 @@ func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Con return ec.marshalNK8sActualSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualSource(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualNamespace", Field: field, @@ -9289,37 +8554,24 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_k8sActualSources(ctx switch field.Name { case "namespace": return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) case "name": return ec.fieldContext_K8sActualSource_name(ctx, field) - case "serviceName": - return ec.fieldContext_K8sActualSource_serviceName(ctx, field) + case "kind": + return ec.fieldContext_K8sActualSource_kind(ctx, field) case "numberOfInstances": return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "selected": + return ec.fieldContext_K8sActualSource_selected(ctx, field) case "reportedName": return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "autoInstrumented": - return ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) - case "autoInstrumentedDecision": - return ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) - case "instrumentedApplicationDetails": - return ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) + case "containers": + return ec.fieldContext_K8sActualSource_containers(ctx, field) + case "conditions": + return ec.fieldContext_K8sActualSource_conditions(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_K8sActualNamespace_k8sActualSources_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } @@ -9367,50 +8619,6 @@ func (ec *executionContext) fieldContext_K8sActualSource_namespace(_ context.Con return fc, nil } -func (ec *executionContext) _K8sActualSource_kind(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_kind(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Kind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(model.K8sResourceKind) - fc.Result = res - return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_K8sActualSource_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "K8sActualSource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type K8sResourceKind does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _K8sActualSource_name(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualSource_name(ctx, field) if err != nil { @@ -9455,8 +8663,8 @@ func (ec *executionContext) fieldContext_K8sActualSource_name(_ context.Context, return fc, nil } -func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_serviceName(ctx, field) +func (ec *executionContext) _K8sActualSource_kind(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_kind(ctx, field) if err != nil { return graphql.Null } @@ -9469,28 +8677,31 @@ func (ec *executionContext) _K8sActualSource_serviceName(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ServiceName, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(model.K8sResourceKind) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_serviceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type K8sResourceKind does not have child fields") }, } return fc, nil @@ -9537,8 +8748,8 @@ func (ec *executionContext) fieldContext_K8sActualSource_numberOfInstances(_ con return fc, nil } -func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) +func (ec *executionContext) _K8sActualSource_selected(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_selected(ctx, field) if err != nil { return graphql.Null } @@ -9551,7 +8762,7 @@ func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ReportedName, nil + return obj.Selected, nil }) if err != nil { ec.Error(ctx, err) @@ -9560,26 +8771,26 @@ func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, f if resTmp == nil { return graphql.Null } - res := resTmp.(*string) + res := resTmp.(*bool) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_reportedName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_selected(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Boolean does not have child fields") }, } return fc, nil } -func (ec *executionContext) _K8sActualSource_autoInstrumented(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_autoInstrumented(ctx, field) +func (ec *executionContext) _K8sActualSource_reportedName(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_reportedName(ctx, field) if err != nil { return graphql.Null } @@ -9592,38 +8803,35 @@ func (ec *executionContext) _K8sActualSource_autoInstrumented(ctx context.Contex }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.AutoInstrumented, nil + return obj.ReportedName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(bool) + res := resTmp.(*string) fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_autoInstrumented(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_reportedName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _K8sActualSource_autoInstrumentedDecision(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_autoInstrumentedDecision(ctx, field) +func (ec *executionContext) _K8sActualSource_containers(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_containers(ctx, field) if err != nil { return graphql.Null } @@ -9636,38 +8844,45 @@ func (ec *executionContext) _K8sActualSource_autoInstrumentedDecision(ctx contex }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.AutoInstrumentedDecision, nil + return obj.Containers, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.([]*model.SourceContainerRuntimeDetails) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_autoInstrumentedDecision(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_containers(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "containerName": + return ec.fieldContext_SourceContainerRuntimeDetails_containerName(ctx, field) + case "language": + return ec.fieldContext_SourceContainerRuntimeDetails_language(ctx, field) + case "runtimeVersion": + return ec.fieldContext_SourceContainerRuntimeDetails_runtimeVersion(ctx, field) + case "otherAgent": + return ec.fieldContext_SourceContainerRuntimeDetails_otherAgent(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceContainerRuntimeDetails", field.Name) }, } return fc, nil } -func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_K8sActualSource_instrumentedApplicationDetails(ctx, field) +func (ec *executionContext) _K8sActualSource_conditions(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualSource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualSource_conditions(ctx, field) if err != nil { return graphql.Null } @@ -9680,7 +8895,7 @@ func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.InstrumentedApplicationDetails, nil + return obj.Conditions, nil }) if err != nil { ec.Error(ctx, err) @@ -9689,12 +8904,12 @@ func (ec *executionContext) _K8sActualSource_instrumentedApplicationDetails(ctx if resTmp == nil { return graphql.Null } - res := resTmp.(*model.InstrumentedApplicationDetails) + res := resTmp.([]*model.Condition) fc.Result = res - return ec.marshalOInstrumentedApplicationDetails2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationDetails(ctx, field.Selections, res) + return ec.marshalOCondition2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐConditionᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_K8sActualSource_instrumentedApplicationDetails(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_K8sActualSource_conditions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "K8sActualSource", Field: field, @@ -9702,14 +8917,18 @@ func (ec *executionContext) fieldContext_K8sActualSource_instrumentedApplication IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "containers": - return ec.fieldContext_InstrumentedApplicationDetails_containers(ctx, field) - case "conditions": - return ec.fieldContext_InstrumentedApplicationDetails_conditions(ctx, field) - case "instrumentationOptions": - return ec.fieldContext_InstrumentedApplicationDetails_instrumentationOptions(ctx, field) + case "status": + return ec.fieldContext_Condition_status(ctx, field) + case "type": + return ec.fieldContext_Condition_type(ctx, field) + case "reason": + return ec.fieldContext_Condition_reason(ctx, field) + case "message": + return ec.fieldContext_Condition_message(ctx, field) + case "lastTransitionTime": + return ec.fieldContext_Condition_lastTransitionTime(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type InstrumentedApplicationDetails", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Condition", field.Name) }, } return fc, nil @@ -13017,8 +12236,8 @@ func (ec *executionContext) fieldContext_PodWorkload_namespace(_ context.Context return fc, nil } -func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PodWorkload_kind(ctx, field) +func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PodWorkload_name(ctx, field) if err != nil { return graphql.Null } @@ -13031,7 +12250,7 @@ func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -13043,26 +12262,26 @@ func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(model.K8sResourceKind) + res := resTmp.(string) fc.Result = res - return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PodWorkload_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PodWorkload_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PodWorkload", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type K8sResourceKind does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PodWorkload_name(ctx, field) +func (ec *executionContext) _PodWorkload_kind(ctx context.Context, field graphql.CollectedField, obj *model.PodWorkload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PodWorkload_kind(ctx, field) if err != nil { return graphql.Null } @@ -13075,7 +12294,7 @@ func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -13087,19 +12306,19 @@ func (ec *executionContext) _PodWorkload_name(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(string) + res := resTmp.(model.K8sResourceKind) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNK8sResourceKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sResourceKind(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PodWorkload_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PodWorkload_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PodWorkload", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type K8sResourceKind does not have child fields") }, } return fc, nil @@ -13445,12 +12664,10 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context switch field.Name { case "computePlatformType": return ec.fieldContext_ComputePlatform_computePlatformType(ctx, field) - case "k8sActualNamespace": - return ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) case "k8sActualNamespaces": return ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) - case "k8sActualSource": - return ec.fieldContext_ComputePlatform_k8sActualSource(ctx, field) + case "k8sActualNamespace": + return ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) case "k8sActualSources": return ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) case "destinations": @@ -13821,12 +13038,10 @@ func (ec *executionContext) fieldContext_Query_describeSource(ctx context.Contex return ec.fieldContext_SourceAnalyze_namespace(ctx, field) case "labels": return ec.fieldContext_SourceAnalyze_labels(ctx, field) - case "instrumentationConfig": - return ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) case "runtimeInfo": return ec.fieldContext_SourceAnalyze_runtimeInfo(ctx, field) - case "instrumentedApplication": - return ec.fieldContext_SourceAnalyze_instrumentedApplication(ctx, field) + case "instrumentationConfig": + return ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) case "instrumentationDevice": return ec.fieldContext_SourceAnalyze_instrumentationDevice(ctx, field) case "totalPods": @@ -14894,74 +14109,20 @@ func (ec *executionContext) fieldContext_SourceAnalyze_namespace(_ context.Conte case "name": return ec.fieldContext_EntityProperty_name(ctx, field) case "value": - return ec.fieldContext_EntityProperty_value(ctx, field) - case "status": - return ec.fieldContext_EntityProperty_status(ctx, field) - case "explain": - return ec.fieldContext_EntityProperty_explain(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _SourceAnalyze_labels(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceAnalyze_labels(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Labels, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.InstrumentationLabelsAnalyze) - fc.Result = res - return ec.marshalNInstrumentationLabelsAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLabelsAnalyze(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_SourceAnalyze_labels(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "SourceAnalyze", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "instrumented": - return ec.fieldContext_InstrumentationLabelsAnalyze_instrumented(ctx, field) - case "workload": - return ec.fieldContext_InstrumentationLabelsAnalyze_workload(ctx, field) - case "namespace": - return ec.fieldContext_InstrumentationLabelsAnalyze_namespace(ctx, field) - case "instrumentedText": - return ec.fieldContext_InstrumentationLabelsAnalyze_instrumentedText(ctx, field) + return ec.fieldContext_EntityProperty_value(ctx, field) + case "status": + return ec.fieldContext_EntityProperty_status(ctx, field) + case "explain": + return ec.fieldContext_EntityProperty_explain(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationLabelsAnalyze", field.Name) + return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) }, } return fc, nil } -func (ec *executionContext) _SourceAnalyze_instrumentationConfig(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) +func (ec *executionContext) _SourceAnalyze_labels(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceAnalyze_labels(ctx, field) if err != nil { return graphql.Null } @@ -14974,7 +14135,7 @@ func (ec *executionContext) _SourceAnalyze_instrumentationConfig(ctx context.Con }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.InstrumentationConfig, nil + return obj.Labels, nil }) if err != nil { ec.Error(ctx, err) @@ -14986,12 +14147,12 @@ func (ec *executionContext) _SourceAnalyze_instrumentationConfig(ctx context.Con } return graphql.Null } - res := resTmp.(*model.InstrumentationConfigAnalyze) + res := resTmp.(*model.InstrumentationLabelsAnalyze) fc.Result = res - return ec.marshalNInstrumentationConfigAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationConfigAnalyze(ctx, field.Selections, res) + return ec.marshalNInstrumentationLabelsAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLabelsAnalyze(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SourceAnalyze_instrumentationConfig(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SourceAnalyze_labels(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "SourceAnalyze", Field: field, @@ -14999,12 +14160,16 @@ func (ec *executionContext) fieldContext_SourceAnalyze_instrumentationConfig(_ c IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "created": - return ec.fieldContext_InstrumentationConfigAnalyze_created(ctx, field) - case "createTime": - return ec.fieldContext_InstrumentationConfigAnalyze_createTime(ctx, field) + case "instrumented": + return ec.fieldContext_InstrumentationLabelsAnalyze_instrumented(ctx, field) + case "workload": + return ec.fieldContext_InstrumentationLabelsAnalyze_workload(ctx, field) + case "namespace": + return ec.fieldContext_InstrumentationLabelsAnalyze_namespace(ctx, field) + case "instrumentedText": + return ec.fieldContext_InstrumentationLabelsAnalyze_instrumentedText(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type InstrumentationConfigAnalyze", field.Name) + return nil, fmt.Errorf("no field named %q was found under type InstrumentationLabelsAnalyze", field.Name) }, } return fc, nil @@ -15031,11 +14196,14 @@ func (ec *executionContext) _SourceAnalyze_runtimeInfo(ctx context.Context, fiel return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*model.RuntimeInfoAnalyze) fc.Result = res - return ec.marshalORuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx, field.Selections, res) + return ec.marshalNRuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_SourceAnalyze_runtimeInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -15057,8 +14225,8 @@ func (ec *executionContext) fieldContext_SourceAnalyze_runtimeInfo(_ context.Con return fc, nil } -func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SourceAnalyze_instrumentedApplication(ctx, field) +func (ec *executionContext) _SourceAnalyze_instrumentationConfig(ctx context.Context, field graphql.CollectedField, obj *model.SourceAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceAnalyze_instrumentationConfig(ctx, field) if err != nil { return graphql.Null } @@ -15071,7 +14239,7 @@ func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.InstrumentedApplication, nil + return obj.InstrumentationConfig, nil }) if err != nil { ec.Error(ctx, err) @@ -15083,12 +14251,12 @@ func (ec *executionContext) _SourceAnalyze_instrumentedApplication(ctx context.C } return graphql.Null } - res := resTmp.(*model.InstrumentedApplicationAnalyze) + res := resTmp.(*model.InstrumentationConfigAnalyze) fc.Result = res - return ec.marshalNInstrumentedApplicationAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationAnalyze(ctx, field.Selections, res) + return ec.marshalNInstrumentationConfigAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationConfigAnalyze(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SourceAnalyze_instrumentedApplication(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SourceAnalyze_instrumentationConfig(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "SourceAnalyze", Field: field, @@ -15097,13 +14265,13 @@ func (ec *executionContext) fieldContext_SourceAnalyze_instrumentedApplication(_ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "created": - return ec.fieldContext_InstrumentedApplicationAnalyze_created(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_created(ctx, field) case "createTime": - return ec.fieldContext_InstrumentedApplicationAnalyze_createTime(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_createTime(ctx, field) case "containers": - return ec.fieldContext_InstrumentedApplicationAnalyze_containers(ctx, field) + return ec.fieldContext_InstrumentationConfigAnalyze_containers(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type InstrumentedApplicationAnalyze", field.Name) + return nil, fmt.Errorf("no field named %q was found under type InstrumentationConfigAnalyze", field.Name) }, } return fc, nil @@ -18568,39 +17736,6 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "k8sActualNamespace": - field := field - - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._ComputePlatform_k8sActualNamespace(ctx, field, obj) - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "k8sActualNamespaces": field := field @@ -18637,7 +17772,7 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "k8sActualSource": + case "k8sActualNamespace": field := field innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { @@ -18646,7 +17781,7 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ComputePlatform_k8sActualSource(ctx, field, obj) + res = ec._ComputePlatform_k8sActualNamespace(ctx, field, obj) return res } @@ -19873,6 +19008,11 @@ func (ec *executionContext) _InstrumentationConfigAnalyze(ctx context.Context, s } case "createTime": out.Values[i] = ec._InstrumentationConfigAnalyze_createTime(ctx, field, obj) + case "containers": + out.Values[i] = ec._InstrumentationConfigAnalyze_containers(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -19908,193 +19048,15 @@ func (ec *executionContext) _InstrumentationDeviceAnalyze(ctx context.Context, s case "__typename": out.Values[i] = graphql.MarshalString("InstrumentationDeviceAnalyze") case "statusText": - out.Values[i] = ec._InstrumentationDeviceAnalyze_statusText(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "containers": - out.Values[i] = ec._InstrumentationDeviceAnalyze_containers(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var instrumentationInstanceAnalyzeImplementors = []string{"InstrumentationInstanceAnalyze"} - -func (ec *executionContext) _InstrumentationInstanceAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationInstanceAnalyze) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationInstanceAnalyzeImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationInstanceAnalyze") - case "healthy": - out.Values[i] = ec._InstrumentationInstanceAnalyze_healthy(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "message": - out.Values[i] = ec._InstrumentationInstanceAnalyze_message(ctx, field, obj) - case "identifyingAttributes": - out.Values[i] = ec._InstrumentationInstanceAnalyze_identifyingAttributes(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var instrumentationLabelsAnalyzeImplementors = []string{"InstrumentationLabelsAnalyze"} - -func (ec *executionContext) _InstrumentationLabelsAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLabelsAnalyze) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationLabelsAnalyzeImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationLabelsAnalyze") - case "instrumented": - out.Values[i] = ec._InstrumentationLabelsAnalyze_instrumented(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "workload": - out.Values[i] = ec._InstrumentationLabelsAnalyze_workload(ctx, field, obj) - case "namespace": - out.Values[i] = ec._InstrumentationLabelsAnalyze_namespace(ctx, field, obj) - case "instrumentedText": - out.Values[i] = ec._InstrumentationLabelsAnalyze_instrumentedText(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var instrumentationLibraryImplementors = []string{"InstrumentationLibrary"} - -func (ec *executionContext) _InstrumentationLibrary(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLibrary) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationLibraryImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationLibrary") - case "libraryName": - out.Values[i] = ec._InstrumentationLibrary_libraryName(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "options": - out.Values[i] = ec._InstrumentationLibrary_options(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var instrumentationLibraryGlobalIdImplementors = []string{"InstrumentationLibraryGlobalId"} - -func (ec *executionContext) _InstrumentationLibraryGlobalId(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLibraryGlobalID) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationLibraryGlobalIdImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationLibraryGlobalId") - case "name": - out.Values[i] = ec._InstrumentationLibraryGlobalId_name(ctx, field, obj) + out.Values[i] = ec._InstrumentationDeviceAnalyze_statusText(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "containers": + out.Values[i] = ec._InstrumentationDeviceAnalyze_containers(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "spanKind": - out.Values[i] = ec._InstrumentationLibraryGlobalId_spanKind(ctx, field, obj) - case "language": - out.Values[i] = ec._InstrumentationLibraryGlobalId_language(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -20118,24 +19080,26 @@ func (ec *executionContext) _InstrumentationLibraryGlobalId(ctx context.Context, return out } -var instrumentationOptionImplementors = []string{"InstrumentationOption"} +var instrumentationInstanceAnalyzeImplementors = []string{"InstrumentationInstanceAnalyze"} -func (ec *executionContext) _InstrumentationOption(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationOption) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationOptionImplementors) +func (ec *executionContext) _InstrumentationInstanceAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationInstanceAnalyze) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationInstanceAnalyzeImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationOption") - case "optionKey": - out.Values[i] = ec._InstrumentationOption_optionKey(ctx, field, obj) + out.Values[i] = graphql.MarshalString("InstrumentationInstanceAnalyze") + case "healthy": + out.Values[i] = ec._InstrumentationInstanceAnalyze_healthy(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "spanKind": - out.Values[i] = ec._InstrumentationOption_spanKind(ctx, field, obj) + case "message": + out.Values[i] = ec._InstrumentationInstanceAnalyze_message(ctx, field, obj) + case "identifyingAttributes": + out.Values[i] = ec._InstrumentationInstanceAnalyze_identifyingAttributes(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -20162,34 +19126,28 @@ func (ec *executionContext) _InstrumentationOption(ctx context.Context, sel ast. return out } -var instrumentationRuleImplementors = []string{"InstrumentationRule"} +var instrumentationLabelsAnalyzeImplementors = []string{"InstrumentationLabelsAnalyze"} -func (ec *executionContext) _InstrumentationRule(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationRule) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationRuleImplementors) +func (ec *executionContext) _InstrumentationLabelsAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLabelsAnalyze) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationLabelsAnalyzeImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentationRule") - case "ruleId": - out.Values[i] = ec._InstrumentationRule_ruleId(ctx, field, obj) + out.Values[i] = graphql.MarshalString("InstrumentationLabelsAnalyze") + case "instrumented": + out.Values[i] = ec._InstrumentationLabelsAnalyze_instrumented(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "ruleName": - out.Values[i] = ec._InstrumentationRule_ruleName(ctx, field, obj) - case "notes": - out.Values[i] = ec._InstrumentationRule_notes(ctx, field, obj) - case "disabled": - out.Values[i] = ec._InstrumentationRule_disabled(ctx, field, obj) - case "workloads": - out.Values[i] = ec._InstrumentationRule_workloads(ctx, field, obj) - case "instrumentationLibraries": - out.Values[i] = ec._InstrumentationRule_instrumentationLibraries(ctx, field, obj) - case "payloadCollection": - out.Values[i] = ec._InstrumentationRule_payloadCollection(ctx, field, obj) + case "workload": + out.Values[i] = ec._InstrumentationLabelsAnalyze_workload(ctx, field, obj) + case "namespace": + out.Values[i] = ec._InstrumentationLabelsAnalyze_namespace(ctx, field, obj) + case "instrumentedText": + out.Values[i] = ec._InstrumentationLabelsAnalyze_instrumentedText(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -20213,29 +19171,26 @@ func (ec *executionContext) _InstrumentationRule(ctx context.Context, sel ast.Se return out } -var instrumentedApplicationAnalyzeImplementors = []string{"InstrumentedApplicationAnalyze"} +var instrumentationLibraryGlobalIdImplementors = []string{"InstrumentationLibraryGlobalId"} -func (ec *executionContext) _InstrumentedApplicationAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentedApplicationAnalyze) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentedApplicationAnalyzeImplementors) +func (ec *executionContext) _InstrumentationLibraryGlobalId(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationLibraryGlobalID) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationLibraryGlobalIdImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentedApplicationAnalyze") - case "created": - out.Values[i] = ec._InstrumentedApplicationAnalyze_created(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "createTime": - out.Values[i] = ec._InstrumentedApplicationAnalyze_createTime(ctx, field, obj) - case "containers": - out.Values[i] = ec._InstrumentedApplicationAnalyze_containers(ctx, field, obj) + out.Values[i] = graphql.MarshalString("InstrumentationLibraryGlobalId") + case "name": + out.Values[i] = ec._InstrumentationLibraryGlobalId_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } + case "spanKind": + out.Values[i] = ec._InstrumentationLibraryGlobalId_spanKind(ctx, field, obj) + case "language": + out.Values[i] = ec._InstrumentationLibraryGlobalId_language(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -20259,26 +19214,34 @@ func (ec *executionContext) _InstrumentedApplicationAnalyze(ctx context.Context, return out } -var instrumentedApplicationDetailsImplementors = []string{"InstrumentedApplicationDetails"} +var instrumentationRuleImplementors = []string{"InstrumentationRule"} -func (ec *executionContext) _InstrumentedApplicationDetails(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentedApplicationDetails) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, instrumentedApplicationDetailsImplementors) +func (ec *executionContext) _InstrumentationRule(ctx context.Context, sel ast.SelectionSet, obj *model.InstrumentationRule) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, instrumentationRuleImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("InstrumentedApplicationDetails") - case "containers": - out.Values[i] = ec._InstrumentedApplicationDetails_containers(ctx, field, obj) - case "conditions": - out.Values[i] = ec._InstrumentedApplicationDetails_conditions(ctx, field, obj) - case "instrumentationOptions": - out.Values[i] = ec._InstrumentedApplicationDetails_instrumentationOptions(ctx, field, obj) + out.Values[i] = graphql.MarshalString("InstrumentationRule") + case "ruleId": + out.Values[i] = ec._InstrumentationRule_ruleId(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } + case "ruleName": + out.Values[i] = ec._InstrumentationRule_ruleName(ctx, field, obj) + case "notes": + out.Values[i] = ec._InstrumentationRule_notes(ctx, field, obj) + case "disabled": + out.Values[i] = ec._InstrumentationRule_disabled(ctx, field, obj) + case "workloads": + out.Values[i] = ec._InstrumentationRule_workloads(ctx, field, obj) + case "instrumentationLibraries": + out.Values[i] = ec._InstrumentationRule_instrumentationLibraries(ctx, field, obj) + case "payloadCollection": + out.Values[i] = ec._InstrumentationRule_payloadCollection(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -20318,8 +19281,6 @@ func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.Sel if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "instrumentationLabelEnabled": - out.Values[i] = ec._K8sActualNamespace_instrumentationLabelEnabled(ctx, field, obj) case "k8sActualSources": field := field @@ -20395,34 +19356,26 @@ func (ec *executionContext) _K8sActualSource(ctx context.Context, sel ast.Select if out.Values[i] == graphql.Null { out.Invalids++ } - case "kind": - out.Values[i] = ec._K8sActualSource_kind(ctx, field, obj) + case "name": + out.Values[i] = ec._K8sActualSource_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "name": - out.Values[i] = ec._K8sActualSource_name(ctx, field, obj) + case "kind": + out.Values[i] = ec._K8sActualSource_kind(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "serviceName": - out.Values[i] = ec._K8sActualSource_serviceName(ctx, field, obj) case "numberOfInstances": out.Values[i] = ec._K8sActualSource_numberOfInstances(ctx, field, obj) + case "selected": + out.Values[i] = ec._K8sActualSource_selected(ctx, field, obj) case "reportedName": out.Values[i] = ec._K8sActualSource_reportedName(ctx, field, obj) - case "autoInstrumented": - out.Values[i] = ec._K8sActualSource_autoInstrumented(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "autoInstrumentedDecision": - out.Values[i] = ec._K8sActualSource_autoInstrumentedDecision(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "instrumentedApplicationDetails": - out.Values[i] = ec._K8sActualSource_instrumentedApplicationDetails(ctx, field, obj) + case "containers": + out.Values[i] = ec._K8sActualSource_containers(ctx, field, obj) + case "conditions": + out.Values[i] = ec._K8sActualSource_conditions(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -21167,13 +20120,13 @@ func (ec *executionContext) _PodWorkload(ctx context.Context, sel ast.SelectionS if out.Values[i] == graphql.Null { out.Invalids++ } - case "kind": - out.Values[i] = ec._PodWorkload_kind(ctx, field, obj) + case "name": + out.Values[i] = ec._PodWorkload_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "name": - out.Values[i] = ec._PodWorkload_name(ctx, field, obj) + case "kind": + out.Values[i] = ec._PodWorkload_kind(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -21723,15 +20676,13 @@ func (ec *executionContext) _SourceAnalyze(ctx context.Context, sel ast.Selectio if out.Values[i] == graphql.Null { out.Invalids++ } - case "instrumentationConfig": - out.Values[i] = ec._SourceAnalyze_instrumentationConfig(ctx, field, obj) + case "runtimeInfo": + out.Values[i] = ec._SourceAnalyze_runtimeInfo(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "runtimeInfo": - out.Values[i] = ec._SourceAnalyze_runtimeInfo(ctx, field, obj) - case "instrumentedApplication": - out.Values[i] = ec._SourceAnalyze_instrumentedApplication(ctx, field, obj) + case "instrumentationConfig": + out.Values[i] = ec._SourceAnalyze_instrumentationConfig(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -23001,60 +21952,6 @@ func (ec *executionContext) marshalNInstrumentationLabelsAnalyze2ᚖgithubᚗcom return ec._InstrumentationLabelsAnalyze(ctx, sel, v) } -func (ec *executionContext) marshalNInstrumentationLibrary2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.InstrumentationLibrary) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNInstrumentationLibrary2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibrary(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNInstrumentationLibrary2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibrary(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationLibrary) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentationLibrary(ctx, sel, v) -} - func (ec *executionContext) marshalNInstrumentationLibraryGlobalId2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationLibraryGlobalID(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationLibraryGlobalID) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23070,60 +21967,6 @@ func (ec *executionContext) unmarshalNInstrumentationLibraryGlobalIdInput2ᚖgit return &res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNInstrumentationOption2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOptionᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.InstrumentationOption) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNInstrumentationOption2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOption(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNInstrumentationOption2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationOption(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentationOption) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentationOption(ctx, sel, v) -} - func (ec *executionContext) marshalNInstrumentationRule2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentationRule(ctx context.Context, sel ast.SelectionSet, v model.InstrumentationRule) graphql.Marshaler { return ec._InstrumentationRule(ctx, sel, &v) } @@ -23187,16 +22030,6 @@ func (ec *executionContext) unmarshalNInstrumentationRuleInput2githubᚗcomᚋod return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNInstrumentedApplicationAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentedApplicationAnalyze) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._InstrumentedApplicationAnalyze(ctx, sel, v) -} - func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { res, err := graphql.UnmarshalInt(v) return res, graphql.ErrorOnPath(ctx, err) @@ -23554,6 +22387,16 @@ func (ec *executionContext) unmarshalNPodWorkloadInput2ᚖgithubᚗcomᚋodigos return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNRuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.RuntimeInfoAnalyze) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._RuntimeInfoAnalyze(ctx, sel, v) +} + func (ec *executionContext) unmarshalNSignalType2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSignalType(ctx context.Context, v interface{}) (model.SignalType, error) { var res model.SignalType err := res.UnmarshalGQL(v) @@ -23757,16 +22600,6 @@ func (ec *executionContext) marshalNSourceContainerRuntimeDetails2ᚖgithubᚗco return ec._SourceContainerRuntimeDetails(ctx, sel, v) } -func (ec *executionContext) unmarshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, v interface{}) (model.SpanKind, error) { - var res model.SpanKind - err := res.UnmarshalGQL(v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNSpanKind2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSpanKind(ctx context.Context, sel ast.SelectionSet, v model.SpanKind) graphql.Marshaler { - return v -} - func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -24316,13 +23149,6 @@ func (ec *executionContext) unmarshalOInstrumentationLibraryGlobalIdInput2ᚕᚖ return res, nil } -func (ec *executionContext) marshalOInstrumentedApplicationDetails2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐInstrumentedApplicationDetails(ctx context.Context, sel ast.SelectionSet, v *model.InstrumentedApplicationDetails) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._InstrumentedApplicationDetails(ctx, sel, v) -} - func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v interface{}) (*int, error) { if v == nil { return nil, nil @@ -24466,13 +23292,6 @@ func (ec *executionContext) marshalOProgrammingLanguage2ᚖgithubᚗcomᚋodigos return v } -func (ec *executionContext) marshalORuntimeInfoAnalyze2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐRuntimeInfoAnalyze(ctx context.Context, sel ast.SelectionSet, v *model.RuntimeInfoAnalyze) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._RuntimeInfoAnalyze(ctx, sel, v) -} - func (ec *executionContext) marshalOSourceContainerRuntimeDetails2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐSourceContainerRuntimeDetailsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.SourceContainerRuntimeDetails) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index d5407efa3..bf9bf142b 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -74,9 +74,8 @@ type ClusterInfo struct { type ComputePlatform struct { ComputePlatformType ComputePlatformType `json:"computePlatformType"` - K8sActualNamespace *K8sActualNamespace `json:"k8sActualNamespace,omitempty"` K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` - K8sActualSource *K8sActualSource `json:"k8sActualSource,omitempty"` + K8sActualNamespace *K8sActualNamespace `json:"k8sActualNamespace,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` Destinations []*Destination `json:"destinations"` Actions []*PipelineAction `json:"actions"` @@ -242,8 +241,9 @@ type HTTPPayloadCollectionInput struct { } type InstrumentationConfigAnalyze struct { - Created *EntityProperty `json:"created"` - CreateTime *EntityProperty `json:"createTime,omitempty"` + Created *EntityProperty `json:"created"` + CreateTime *EntityProperty `json:"createTime,omitempty"` + Containers []*ContainerRuntimeInfoAnalyze `json:"containers"` } type InstrumentationDeviceAnalyze struct { @@ -264,11 +264,6 @@ type InstrumentationLabelsAnalyze struct { InstrumentedText *EntityProperty `json:"instrumentedText,omitempty"` } -type InstrumentationLibrary struct { - LibraryName string `json:"libraryName"` - Options []*InstrumentationOption `json:"options"` -} - type InstrumentationLibraryGlobalID struct { Name string `json:"name"` SpanKind *SpanKind `json:"spanKind,omitempty"` @@ -281,11 +276,6 @@ type InstrumentationLibraryGlobalIDInput struct { Language *ProgrammingLanguage `json:"language,omitempty"` } -type InstrumentationOption struct { - OptionKey string `json:"optionKey"` - SpanKind SpanKind `json:"spanKind"` -} - type InstrumentationRule struct { RuleID string `json:"ruleId"` RuleName *string `json:"ruleName,omitempty"` @@ -305,34 +295,20 @@ type InstrumentationRuleInput struct { PayloadCollection *PayloadCollectionInput `json:"payloadCollection,omitempty"` } -type InstrumentedApplicationAnalyze struct { - Created *EntityProperty `json:"created"` - CreateTime *EntityProperty `json:"createTime,omitempty"` - Containers []*ContainerRuntimeInfoAnalyze `json:"containers"` -} - -type InstrumentedApplicationDetails struct { - Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` - Conditions []*Condition `json:"conditions,omitempty"` - InstrumentationOptions []*InstrumentationLibrary `json:"instrumentationOptions"` -} - type K8sActualNamespace struct { - Name string `json:"name"` - InstrumentationLabelEnabled *bool `json:"instrumentationLabelEnabled,omitempty"` - K8sActualSources []*K8sActualSource `json:"k8sActualSources"` + Name string `json:"name"` + K8sActualSources []*K8sActualSource `json:"k8sActualSources"` } type K8sActualSource struct { - Namespace string `json:"namespace"` - Kind K8sResourceKind `json:"kind"` - Name string `json:"name"` - ServiceName *string `json:"serviceName,omitempty"` - NumberOfInstances *int `json:"numberOfInstances,omitempty"` - ReportedName *string `json:"reportedName,omitempty"` - AutoInstrumented bool `json:"autoInstrumented"` - AutoInstrumentedDecision string `json:"autoInstrumentedDecision"` - InstrumentedApplicationDetails *InstrumentedApplicationDetails `json:"instrumentedApplicationDetails,omitempty"` + Namespace string `json:"namespace"` + Name string `json:"name"` + Kind K8sResourceKind `json:"kind"` + NumberOfInstances *int `json:"numberOfInstances,omitempty"` + Selected *bool `json:"selected,omitempty"` + ReportedName *string `json:"reportedName,omitempty"` + Containers []*SourceContainerRuntimeDetails `json:"containers,omitempty"` + Conditions []*Condition `json:"conditions,omitempty"` } type K8sDesiredNamespaceInput struct { @@ -500,8 +476,8 @@ type PodContainerAnalyze struct { type PodWorkload struct { Namespace string `json:"namespace"` - Kind K8sResourceKind `json:"kind"` Name string `json:"name"` + Kind K8sResourceKind `json:"kind"` } type PodWorkloadInput struct { @@ -587,17 +563,16 @@ type SingleSourceMetricsResponse struct { } type SourceAnalyze struct { - Name *EntityProperty `json:"name"` - Kind *EntityProperty `json:"kind"` - Namespace *EntityProperty `json:"namespace"` - Labels *InstrumentationLabelsAnalyze `json:"labels"` - InstrumentationConfig *InstrumentationConfigAnalyze `json:"instrumentationConfig"` - RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo,omitempty"` - InstrumentedApplication *InstrumentedApplicationAnalyze `json:"instrumentedApplication"` - InstrumentationDevice *InstrumentationDeviceAnalyze `json:"instrumentationDevice"` - TotalPods int `json:"totalPods"` - PodsPhasesCount string `json:"podsPhasesCount"` - Pods []*PodAnalyze `json:"pods"` + Name *EntityProperty `json:"name"` + Kind *EntityProperty `json:"kind"` + Namespace *EntityProperty `json:"namespace"` + Labels *InstrumentationLabelsAnalyze `json:"labels"` + RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` + InstrumentationConfig *InstrumentationConfigAnalyze `json:"instrumentationConfig"` + InstrumentationDevice *InstrumentationDeviceAnalyze `json:"instrumentationDevice"` + TotalPods int `json:"totalPods"` + PodsPhasesCount string `json:"podsPhasesCount"` + Pods []*PodAnalyze `json:"pods"` } type SourceContainerRuntimeDetails struct { diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 3fb7b0f4d..602e4440f 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -51,22 +51,6 @@ type SourceContainerRuntimeDetails { otherAgent: String } -type InstrumentationOption { - optionKey: String! - spanKind: SpanKind! -} - -type InstrumentationLibrary { - libraryName: String! - options: [InstrumentationOption!]! -} - -type InstrumentedApplicationDetails { - containers: [SourceContainerRuntimeDetails!] - conditions: [Condition!] - instrumentationOptions: [InstrumentationLibrary!]! -} - type Condition { status: ConditionStatus! type: String! @@ -77,8 +61,7 @@ type Condition { type K8sActualNamespace { name: String! - instrumentationLabelEnabled: Boolean - k8sActualSources(instrumentationLabeled: Boolean): [K8sActualSource]! + k8sActualSources: [K8sActualSource]! } input K8sDesiredNamespaceInput { @@ -91,14 +74,13 @@ input K8sNamespaceId { type K8sActualSource { namespace: String! - kind: K8sResourceKind! name: String! - serviceName: String + kind: K8sResourceKind! numberOfInstances: Int + selected: Boolean reportedName: String - autoInstrumented: Boolean! - autoInstrumentedDecision: String! - instrumentedApplicationDetails: InstrumentedApplicationDetails + containers: [SourceContainerRuntimeDetails!] + conditions: [Condition!] } input K8sDesiredSourceInput { @@ -108,8 +90,8 @@ input K8sDesiredSourceInput { type PodWorkload { namespace: String! - kind: K8sResourceKind! name: String! + kind: K8sResourceKind! } input PodWorkloadInput { @@ -203,9 +185,8 @@ input MessagingPayloadCollectionInput { type ComputePlatform { computePlatformType: ComputePlatformType! - k8sActualNamespace(name: String!): K8sActualNamespace k8sActualNamespaces: [K8sActualNamespace]! - k8sActualSource(name: String, namespace: String, kind: String): K8sActualSource + k8sActualNamespace(name: String!): K8sActualNamespace k8sActualSources: [K8sActualSource]! destinations: [Destination!]! actions: [PipelineAction!]! @@ -466,11 +447,6 @@ type InstrumentationLabelsAnalyze { instrumentedText: EntityProperty } -type InstrumentationConfigAnalyze { - created: EntityProperty! - createTime: EntityProperty -} - type ContainerRuntimeInfoAnalyze { containerName: EntityProperty! language: EntityProperty! @@ -478,14 +454,14 @@ type ContainerRuntimeInfoAnalyze { envVars: [EntityProperty!]! } -type RuntimeInfoAnalyze { - generation: EntityProperty! +type InstrumentationConfigAnalyze { + created: EntityProperty! + createTime: EntityProperty containers: [ContainerRuntimeInfoAnalyze!]! } -type InstrumentedApplicationAnalyze { - created: EntityProperty! - createTime: EntityProperty +type RuntimeInfoAnalyze { + generation: EntityProperty! containers: [ContainerRuntimeInfoAnalyze!]! } @@ -525,9 +501,8 @@ type SourceAnalyze { namespace: EntityProperty! labels: InstrumentationLabelsAnalyze! + runtimeInfo: RuntimeInfoAnalyze! instrumentationConfig: InstrumentationConfigAnalyze! - runtimeInfo: RuntimeInfoAnalyze - instrumentedApplication: InstrumentedApplicationAnalyze! instrumentationDevice: InstrumentationDeviceAnalyze! totalPods: Int! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index ffddced75..5c341760e 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -19,14 +19,32 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/odigos_describe" "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" - "github.com/odigos-io/odigos/k8sutils/pkg/workload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) +// K8sActualNamespaces is the resolver for the k8sActualNamespaces field. +func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) { + namespacesResponse := services.GetK8SNamespaces(ctx) + + K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) + for i, namespace := range namespacesResponse.Namespaces { + K8sActualNamespaces[i] = &model.K8sActualNamespace{ + Name: namespace.Name, + } + } + + return K8sActualNamespaces, nil +} + // K8sActualNamespace is the resolver for the k8sActualNamespace field. func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) { - namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, name, nil) + namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, name) if err != nil { return nil, err } @@ -37,73 +55,26 @@ func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *m namespaceActualSourcesPointers[i] = &source } - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) - return &model.K8sActualNamespace{ - Name: name, - InstrumentationLabelEnabled: nsInstrumented, - K8sActualSources: namespaceActualSourcesPointers, + Name: namespace.Name, + K8sActualSources: namespaceActualSourcesPointers, }, nil } -// K8sActualNamespaces is the resolver for the k8sActualNamespaces field. -func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) { - namespacesResponse := services.GetK8SNamespaces(ctx) - - K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) - for i, namespace := range namespacesResponse.Namespaces { - - namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - nsInstrumented := workload.GetInstrumentationLabelValue(namespace.GetLabels()) - - K8sActualNamespaces[i] = &model.K8sActualNamespace{ - Name: namespace.Name, - InstrumentationLabelEnabled: nsInstrumented, - } - } - - return K8sActualNamespaces, nil -} - -// K8sActualSource is the resolver for the k8sActualSource field. -func (r *computePlatformResolver) K8sActualSource(ctx context.Context, obj *model.ComputePlatform, name *string, namespace *string, kind *string) (*model.K8sActualSource, error) { - return nil, nil -} - // K8sActualSources is the resolver for the k8sActualSources field. func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { - instrumentedApplications, err := kube.DefaultClient.OdigosClient.InstrumentedApplications("").List(ctx, metav1.ListOptions{}) + // Initialize an empty list of K8sActualSource + var actualSources []*model.K8sActualSource + + instrumentationConfigs, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs("").List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } - // Initialize an empty list of K8sActualSource - var actualSources []*model.K8sActualSource - // Convert each instrumented application to the K8sActualSource type - for _, app := range instrumentedApplications.Items { - actualSource := instrumentedApplicationToActualSource(app) - services.AddHealthyInstrumentationInstancesCondition(ctx, &app, actualSource) - owner, _ := services.GetWorkload(ctx, actualSource.Namespace, string(actualSource.Kind), actualSource.Name) - if owner == nil { - - continue - } - ownerAnnotations := owner.GetAnnotations() - var reportedName string - if ownerAnnotations != nil { - reportedName = ownerAnnotations[consts.OdigosReportedNameAnnotation] - } - actualSource.ReportedName = &reportedName + for _, instruConfig := range instrumentationConfigs.Items { + actualSource := instrumentationConfigToActualSource(instruConfig) + services.AddHealthyInstrumentationInstancesCondition(ctx, &instruConfig, actualSource) actualSources = append(actualSources, actualSource) } @@ -284,8 +255,8 @@ func (r *destinationResolver) Conditions(ctx context.Context, obj *model.Destina } // K8sActualSources is the resolver for the k8sActualSources field. -func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace, instrumentationLabeled *bool) ([]*model.K8sActualSource, error) { - namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, obj.Name, instrumentationLabeled) +func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj *model.K8sActualNamespace) ([]*model.K8sActualSource, error) { + namespaceActualSources, err := services.GetWorkloadsInNamespace(ctx, obj.Name) if err != nil { return nil, err } @@ -294,6 +265,14 @@ func (r *k8sActualNamespaceResolver) K8sActualSources(ctx context.Context, obj * namespaceActualSourcesPointers := make([]*model.K8sActualSource, len(namespaceActualSources)) for i, source := range namespaceActualSources { namespaceActualSourcesPointers[i] = &source + + crd, err := services.GetSourceCRD(ctx, obj.Name, source.Name, services.WorkloadKind(source.Kind)) + instrumented := false + if crd != nil && err == nil { + instrumented = true + } + + namespaceActualSourcesPointers[i].Selected = &instrumented } return namespaceActualSourcesPointers, nil diff --git a/frontend/kube/watchers/batcher.go b/frontend/kube/watchers/batcher.go index a0aa74cf2..18e4dac03 100644 --- a/frontend/kube/watchers/batcher.go +++ b/frontend/kube/watchers/batcher.go @@ -32,7 +32,7 @@ type EventBatcher struct { } type EventBatcherConfig struct { - // Event to batch, not configuring this value (empty string) will cause to all events to be batched + // Event to batch, not configuring this value (empty string) will cause all events to be batched Event sse.MessageEvent // Message type to batch, not configuring this value (empty string) will cause all messages to be batched MessageType sse.MessageType @@ -83,11 +83,11 @@ func (eb *EventBatcher) AddEvent(msgType sse.MessageType, data, target string) e defer eb.mu.Unlock() message := sse.SSEMessage{ - Event: eb.config.Event, Type: msgType, - Target: target, + Event: eb.config.Event, Data: data, CRDType: eb.config.CRDType, + Target: target, } eb.batch = append(eb.batch, message) @@ -141,21 +141,21 @@ func (eb *EventBatcher) prepareBatchMessage() []sse.SSEMessage { var result []sse.SSEMessage if successCount > 0 { result = append(result, sse.SSEMessage{ - Event: eb.config.Event, Type: sse.MessageTypeSuccess, - Target: "", + Event: eb.config.Event, Data: eb.config.SuccessBatchMessageFunc(successCount, eb.config.CRDType), CRDType: eb.config.CRDType, + Target: "", }) } if failureCount > 0 { result = append(result, sse.SSEMessage{ - Event: eb.config.Event, Type: sse.MessageTypeError, - Target: "", + Event: eb.config.Event, Data: eb.config.FailureBatchMessageFunc(failureCount, eb.config.CRDType), CRDType: eb.config.CRDType, + Target: "", }) } return result diff --git a/frontend/kube/watchers/common.go b/frontend/kube/watchers/common.go index bf4343c84..ebc70c4cd 100644 --- a/frontend/kube/watchers/common.go +++ b/frontend/kube/watchers/common.go @@ -6,10 +6,10 @@ import ( func genericErrorMessage(event sse.MessageEvent, crd string, data string) { sse.SendMessageToClient(sse.SSEMessage{ - Event: event, - Type: sse.MessageTypeError, - Target: "", - Data: "Something went wrong: " + data, + Type: sse.MessageTypeError, + Event: event, + Data: "Something went wrong: " + data, CRDType: crd, + Target: "", }) -} \ No newline at end of file +} diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index c08058405..b290e275d 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -3,16 +3,66 @@ package watchers import ( "context" "fmt" - "log" + "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/frontend/endpoints/sse" "github.com/odigos-io/odigos/frontend/kube" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/watch" ) +var destinationAddedEventBatcher *EventBatcher +var destinationModifiedEventBatcher *EventBatcher +var destinationDeletedEventBatcher *EventBatcher + func StartDestinationWatcher(ctx context.Context, namespace string) error { + destinationAddedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventAdded, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Successfully created %d destinations", count) + }, + FailureBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Failed to create %d destinations", count) + }, + }, + ) + + destinationModifiedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventModified, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Successfully transformed %d destinations to otelcol configuration", batchSize) + }, + FailureBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Failed to transform %d destinations to otelcol configuration", batchSize) + }, + }, + ) + + destinationDeletedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventDeleted, + CRDType: consts.Destination, + SuccessBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Successfully deleted %d destinations", count) + }, + FailureBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Failed to delete %d destinations", count) + }, + }, + ) + watcher, err := kube.DefaultClient.OdigosClient.Destinations(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { return fmt.Errorf("error creating watcher: %v", err) @@ -24,6 +74,9 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { func handleDestinationWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() + defer destinationAddedEventBatcher.Cancel() + defer destinationModifiedEventBatcher.Cancel() + defer destinationDeletedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -35,50 +88,54 @@ func handleDestinationWatchEvents(ctx context.Context, watcher watch.Interface) } switch event.Type { case watch.Added: - handleAddedDestination(event) + handleAddedDestination(event.Object.(*v1alpha1.Destination)) case watch.Modified: - handleModifiedDestination(event) + handleModifiedDestination(event.Object.(*v1alpha1.Destination)) case watch.Deleted: - handleDeletedDestination(event) - default: - log.Printf("unexpected type: %T", event.Object) + handleDeletedDestination(event.Object.(*v1alpha1.Destination)) } } } } -func handleAddedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventAdded, "Destination", "error type assertion") +func handleAddedDestination(destination *v1alpha1.Destination) { + name := destination.Spec.DestinationName + if name == "" { + name = string(destination.Spec.Type) } - data := fmt.Sprintf("Destination %s created", destination.Spec.DestinationName) - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventAdded, Type: "success", Target: destination.Name, Data: data, CRDType: "Destination"}) + + target := destination.Name + data := fmt.Sprintf(`%s "%s" created`, consts.Destination, name) + destinationAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } -func handleModifiedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventModified, "Destination", "error type assertion") - } - if len(destination.Status.Conditions) == 0 { +func handleModifiedDestination(destination *v1alpha1.Destination) { + length := len(destination.Status.Conditions) + if length == 0 { return } - lastCondition := destination.Status.Conditions[len(destination.Status.Conditions)-1] + target := destination.Name + lastCondition := destination.Status.Conditions[length-1] data := lastCondition.Message - conditionType := sse.MessageTypeSuccess - if lastCondition.Status == "False" { + + conditionType := sse.MessageTypeInfo + if lastCondition.Status == metav1.ConditionTrue { + conditionType = sse.MessageTypeSuccess + } else if lastCondition.Status == metav1.ConditionFalse { conditionType = sse.MessageTypeError } - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventModified, Type: conditionType, Target: destination.Name, Data: data, CRDType: "Destination"}) + + destinationModifiedEventBatcher.AddEvent(conditionType, data, target) } -func handleDeletedDestination(event watch.Event) { - destination, ok := event.Object.(*v1alpha1.Destination) - if !ok { - genericErrorMessage(sse.MessageEventDeleted, "Destination", "error type assertion") +func handleDeletedDestination(destination *v1alpha1.Destination) { + name := destination.Spec.DestinationName + if name == "" { + name = string(destination.Spec.Type) } - data := fmt.Sprintf("Destination %s deleted successfully", destination.Spec.DestinationName) - sse.SendMessageToClient(sse.SSEMessage{Event: sse.MessageEventDeleted, Type: "success", Target: "", Data: data, CRDType: "Destination"}) + + target := destination.Name + data := fmt.Sprintf(`%s "%s" deleted`, consts.Destination, name) + destinationDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go new file mode 100644 index 000000000..ddb67b5b8 --- /dev/null +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -0,0 +1,139 @@ +package watchers + +import ( + "context" + "fmt" + "time" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/common/consts" + "github.com/odigos-io/odigos/frontend/endpoints/sse" + "github.com/odigos-io/odigos/frontend/kube" + commonutils "github.com/odigos-io/odigos/k8sutils/pkg/workload" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" +) + +var instrumentationConfigAddedEventBatcher *EventBatcher +var instrumentationConfigModifiedEventBatcher *EventBatcher +var instrumentationConfigDeletedEventBatcher *EventBatcher + +func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { + instrumentationConfigAddedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventAdded, + CRDType: consts.InstrumentationConfig, + SuccessBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Successfully created %d sources", count) + }, + FailureBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Failed to create %d sources", count) + }, + }, + ) + + instrumentationConfigModifiedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventModified, + CRDType: consts.InstrumentationConfig, + SuccessBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Successfully updated %d sources", batchSize) + }, + FailureBatchMessageFunc: func(batchSize int, crd string) string { + return fmt.Sprintf("Failed to update %d sources", batchSize) + }, + }, + ) + + instrumentationConfigDeletedEventBatcher = NewEventBatcher( + EventBatcherConfig{ + MinBatchSize: 1, + Duration: 10 * time.Second, + Event: sse.MessageEventDeleted, + CRDType: consts.InstrumentationConfig, + SuccessBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Successfully deleted %d sources", count) + }, + FailureBatchMessageFunc: func(count int, crdType string) string { + return fmt.Sprintf("Failed to delete %d sources", count) + }, + }, + ) + + watcher, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs(namespace).Watch(context.Background(), metav1.ListOptions{}) + if err != nil { + return fmt.Errorf("error creating watcher: %w", err) + } + + go handleInstrumentationConfigWatchEvents(ctx, watcher) + return nil +} + +func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) { + ch := watcher.ResultChan() + defer instrumentationConfigAddedEventBatcher.Cancel() + defer instrumentationConfigModifiedEventBatcher.Cancel() + defer instrumentationConfigDeletedEventBatcher.Cancel() + for { + select { + case <-ctx.Done(): + watcher.Stop() + return + case event, ok := <-ch: + if !ok { + return + } + switch event.Type { + case watch.Added: + handleAddedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) + case watch.Modified: + handleModifiedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) + case watch.Deleted: + handleDeletedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) + } + } + } +} + +func handleAddedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) + if err != nil { + genericErrorMessage(sse.MessageEventAdded, consts.InstrumentationConfig, err.Error()) + return + } + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf(`Source "%s" created`, name) + instrumentationConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) +} + +func handleModifiedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) + if err != nil { + genericErrorMessage(sse.MessageEventAdded, consts.InstrumentationConfig, err.Error()) + return + } + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf(`Source "%s" updated`, name) + instrumentationConfigModifiedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) +} + +func handleDeletedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { + namespace := instruConfig.Namespace + name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) + if err != nil { + genericErrorMessage(sse.MessageEventDeleted, consts.InstrumentationConfig, err.Error()) + return + } + + target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) + data := fmt.Sprintf(`Source "%s" deleted`, name) + instrumentationConfigDeletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) +} diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index feb1e7b4d..0992faf63 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -14,20 +14,22 @@ import ( "k8s.io/apimachinery/pkg/watch" ) -var modifiedBatcher *EventBatcher +var instrumentationInstanceModifiedEventBatcher *EventBatcher func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) error { - modifiedBatcher = NewEventBatcher( + instrumentationInstanceModifiedEventBatcher = NewEventBatcher( EventBatcherConfig{ - Event: sse.MessageEventModified, - MessageType: sse.MessageTypeError, - Duration: 10 * time.Second, - CRDType: "InstrumentationInstance", + MinBatchSize: 1, + Duration: 10 * time.Second, + MessageType: sse.MessageTypeError, + Event: sse.MessageEventModified, + CRDType: consts.InstrumentationInstance, FailureBatchMessageFunc: func(batchSize int, crd string) string { return fmt.Sprintf("Failed to instrument %d instances", batchSize) }, }, ) + watcher, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(namespace).Watch(context.Background(), metav1.ListOptions{}) if err != nil { return fmt.Errorf("error creating watcher: %v", err) @@ -39,7 +41,7 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() - defer modifiedBatcher.Cancel() + defer instrumentationInstanceModifiedEventBatcher.Cancel() for { select { case <-ctx.Done(): @@ -51,48 +53,36 @@ func handleInstrumentationInstanceWatchEvents(ctx context.Context, watcher watch } switch event.Type { case watch.Modified: - handleModifiedInstrumentationInstance(event) + handleModifiedInstrumentationInstance(event.Object.(*v1alpha1.InstrumentationInstance)) } } } } -func handleModifiedInstrumentationInstance(event watch.Event) { - instrumentedInstance, ok := event.Object.(*v1alpha1.InstrumentationInstance) - if !ok { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error type assertion") - } - healthy := instrumentedInstance.Status.Healthy - - if healthy == nil { - return - } - - if *healthy { +func handleModifiedInstrumentationInstance(instruInstance *v1alpha1.InstrumentationInstance) { + healthy := instruInstance.Status.Healthy + if healthy == nil || *healthy { // send notification to frontend only if the instance is not healthy return } - labels := instrumentedInstance.GetLabels() + labels := instruInstance.GetLabels() if labels == nil { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting labels") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting labels") } instrumentedAppName, ok := labels[consts.InstrumentedAppNameLabel] if !ok { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting instrumented app name from labels") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, "error getting instrumented app name from labels") } + namespace := instruInstance.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instrumentedAppName) if err != nil { - genericErrorMessage(sse.MessageEventModified, "InstrumentationInstance", "error getting workload info") + genericErrorMessage(sse.MessageEventModified, consts.InstrumentationInstance, err.Error()) } - namespace := instrumentedInstance.Namespace - target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf("%s %s", instrumentedInstance.Status.Reason, instrumentedInstance.Status.Message) - - fmt.Printf("InstrumentationInstance %s modified\n", name) - modifiedBatcher.AddEvent(sse.MessageTypeError, data, target) + data := fmt.Sprintf(`%s "%s" %s: %s`, consts.InstrumentationInstance, name, instruInstance.Status.Reason, instruInstance.Status.Message) + instrumentationInstanceModifiedEventBatcher.AddEvent(sse.MessageTypeError, data, target) } diff --git a/frontend/kube/watchers/instrumented_application_watcher.go b/frontend/kube/watchers/instrumented_application_watcher.go deleted file mode 100644 index b17271289..000000000 --- a/frontend/kube/watchers/instrumented_application_watcher.go +++ /dev/null @@ -1,97 +0,0 @@ -package watchers - -import ( - "context" - "fmt" - - "github.com/odigos-io/odigos/api/odigos/v1alpha1" - "github.com/odigos-io/odigos/frontend/endpoints/sse" - "github.com/odigos-io/odigos/frontend/kube" - commonutils "github.com/odigos-io/odigos/k8sutils/pkg/workload" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" -) - -var addedEventBatcher *EventBatcher -var deletedEventBatcher *EventBatcher - -func StartInstrumentedApplicationWatcher(ctx context.Context, namespace string) error { - addedEventBatcher = NewEventBatcher( - EventBatcherConfig{ - Event: sse.MessageEventAdded, - CRDType: "InstrumentedApplication", - SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("successfully added %d sources", count) - }, - FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("failed to add %d sources", count) - }, - }, - ) - - deletedEventBatcher = NewEventBatcher( - EventBatcherConfig{ - Event: sse.MessageEventDeleted, - CRDType: "InstrumentedApplication", - SuccessBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("successfully deleted %d sources", count) - }, - FailureBatchMessageFunc: func(count int, crdType string) string { - return fmt.Sprintf("failed to delete %d sources", count) - }, - }, - ) - - watcher, err := kube.DefaultClient.OdigosClient.InstrumentedApplications(namespace).Watch(context.Background(), metav1.ListOptions{}) - if err != nil { - return fmt.Errorf("error creating watcher: %v", err) - } - - go handleInstrumentedApplicationWatchEvents(ctx, watcher) - return nil -} - -func handleInstrumentedApplicationWatchEvents(ctx context.Context, watcher watch.Interface) { - ch := watcher.ResultChan() - defer addedEventBatcher.Cancel() - defer deletedEventBatcher.Cancel() - for { - select { - case <-ctx.Done(): - watcher.Stop() - return - case event, ok := <-ch: - if !ok { - return - } - switch event.Type { - case watch.Added: - handleAddedEvent(event.Object.(*v1alpha1.InstrumentedApplication)) - case watch.Deleted: - handleDeletedEvent(event.Object.(*v1alpha1.InstrumentedApplication)) - } - } - } -} - -func handleAddedEvent(app *v1alpha1.InstrumentedApplication) { - name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(app.Name) - if err != nil { - genericErrorMessage(sse.MessageEventAdded, "InstrumentedApplication", "error getting workload info") - return - } - namespace := app.Namespace - target := fmt.Sprintf("name=%s&kind=%s&namespace=%s", name, kind, namespace) - data := fmt.Sprintf("InstrumentedApplication %s created", name) - addedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) -} - -func handleDeletedEvent(app *v1alpha1.InstrumentedApplication) { - name, _, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(app.Name) - if err != nil { - genericErrorMessage(sse.MessageEventDeleted, "InstrumentedApplication", "error getting workload info") - return - } - data := fmt.Sprintf("Source %s deleted successfully", name) - deletedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, "") -} diff --git a/frontend/main.go b/frontend/main.go index ce99dbd7b..5b5bb13a4 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -318,9 +318,9 @@ func main() { } // Start watchers - err = watchers.StartInstrumentedApplicationWatcher(ctx, "") + err = watchers.StartInstrumentationConfigWatcher(ctx, "") if err != nil { - log.Printf("Error starting InstrumentedApplication watcher: %v", err) + log.Printf("Error starting InstrumentationConfig watcher: %v", err) } err = watchers.StartDestinationWatcher(ctx, flags.Namespace) diff --git a/frontend/services/describe/source_describe/source_describe.go b/frontend/services/describe/source_describe/source_describe.go index b422e9836..329ffc76c 100644 --- a/frontend/services/describe/source_describe/source_describe.go +++ b/frontend/services/describe/source_describe/source_describe.go @@ -50,15 +50,11 @@ func ConvertSourceAnalyzeToGQL(analyze *source.SourceAnalyze) *model.SourceAnaly Namespace: describe_utils.ConvertEntityPropertyToGQL(analyze.Labels.Namespace), InstrumentedText: describe_utils.ConvertEntityPropertyToGQL(&analyze.Labels.InstrumentedText), }, + RuntimeInfo: convertRuntimeInfoToGQL(analyze.RuntimeInfo), InstrumentationConfig: &model.InstrumentationConfigAnalyze{ Created: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentationConfig.Created), CreateTime: describe_utils.ConvertEntityPropertyToGQL(analyze.InstrumentationConfig.CreateTime), - }, - RuntimeInfo: convertRuntimeInfoToGQL(analyze.RuntimeInfo), - InstrumentedApplication: &model.InstrumentedApplicationAnalyze{ - Created: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentedApplication.Created), - CreateTime: describe_utils.ConvertEntityPropertyToGQL(analyze.InstrumentedApplication.CreateTime), - Containers: convertRuntimeInfoContainersToGQL(analyze.InstrumentedApplication.Containers), + Containers: convertRuntimeInfoContainersToGQL(analyze.InstrumentationConfig.Containers), }, InstrumentationDevice: &model.InstrumentationDeviceAnalyze{ StatusText: describe_utils.ConvertEntityPropertyToGQL(&analyze.InstrumentationDevice.StatusText), diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 534249c90..7b4e34a62 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -179,12 +179,13 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo for _, workload := range workloads { currWorkload := workload g.Go(func() error { - // Only label selected sources, ignore the rest + // Only instrument/uninstrument selected workloads, ignore the rest if currWorkload.Selected != nil { - return setWorkloadInstrumentationLabel(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) + return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) } return nil }) } + return g.Wait() } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 9549fba7a..4167548d2 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -7,19 +7,17 @@ import ( "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" + "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" - + "github.com/odigos-io/odigos/k8sutils/pkg/client" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" appsv1 "k8s.io/api/apps/v1" - - "github.com/odigos-io/odigos/frontend/graph/model" - - "github.com/odigos-io/odigos/k8sutils/pkg/client" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" "golang.org/x/sync/errgroup" - corev1 "k8s.io/api/core/v1" ) type WorkloadKind string @@ -30,38 +28,6 @@ const ( WorkloadKindDaemonSet WorkloadKind = "DaemonSet" ) -type SourceLanguage struct { - ContainerName string `json:"container_name"` - Language string `json:"language"` -} - -type InstrumentedApplicationDetails struct { - Languages []SourceLanguage `json:"languages,omitempty"` - Conditions []metav1.Condition `json:"conditions,omitempty"` -} -type SourceID struct { - // combination of namespace, kind and name is unique - Name string `json:"name"` - Kind string `json:"kind"` - Namespace string `json:"namespace"` -} - -type Source struct { - ThinSource - ReportedName string `json:"reported_name,omitempty"` -} - -type PatchSourceRequest struct { - ReportedName *string `json:"reported_name"` -} - -// this object contains only part of the source fields. It is used to display the sources in the frontend -type ThinSource struct { - SourceID - NumberOfRunningInstances int `json:"number_of_running_instances"` - IaDetails *InstrumentedApplicationDetails `json:"instrumented_application_details"` -} - func GetWorkload(c context.Context, ns string, kind string, name string) (metav1.Object, int) { switch kind { case "Deployment": @@ -87,9 +53,9 @@ func GetWorkload(c context.Context, ns string, kind string, name string) (metav1 } } -func AddHealthyInstrumentationInstancesCondition(ctx context.Context, app *v1alpha1.InstrumentedApplication, source *model.K8sActualSource) error { - labelSelector := fmt.Sprintf("%s=%s", consts.InstrumentedAppNameLabel, app.Name) - instancesList, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(app.Namespace).List(ctx, metav1.ListOptions{ +func AddHealthyInstrumentationInstancesCondition(ctx context.Context, instruConfig *v1alpha1.InstrumentationConfig, source *model.K8sActualSource) error { + labelSelector := fmt.Sprintf("%s=%s", consts.InstrumentedAppNameLabel, instruConfig.Name) + instancesList, err := kube.DefaultClient.OdigosClient.InstrumentationInstances(instruConfig.Namespace).List(ctx, metav1.ListOptions{ LabelSelector: labelSelector, }) @@ -121,7 +87,7 @@ func AddHealthyInstrumentationInstancesCondition(ctx context.Context, app *v1alp message := fmt.Sprintf("%d/%d instances are healthy", healthyInstances, totalInstances) lastTransitionTime := Metav1TimeToString(latestStatusTime) - source.InstrumentedApplicationDetails.Conditions = append(source.InstrumentedApplicationDetails.Conditions, &model.Condition{ + source.Conditions = append(source.Conditions, &model.Condition{ Type: "HealthyInstrumentationInstances", Status: status, LastTransitionTime: &lastTransitionTime, @@ -131,8 +97,7 @@ func AddHealthyInstrumentationInstancesCondition(ctx context.Context, app *v1alp return nil } -func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { - +func GetWorkloadsInNamespace(ctx context.Context, nsName string) ([]model.K8sActualSource, error) { namespace, err := kube.DefaultClient.CoreV1().Namespaces().Get(ctx, nsName, metav1.GetOptions{}) if err != nil { return nil, err @@ -147,19 +112,19 @@ func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentation g.Go(func() error { var err error - deps, err = getDeployments(ctx, *namespace, instrumentationLabeled) + deps, err = getDeployments(ctx, *namespace) return err }) g.Go(func() error { var err error - ss, err = getStatefulSets(ctx, *namespace, instrumentationLabeled) + ss, err = getStatefulSets(ctx, *namespace) return err }) g.Go(func() error { var err error - dss, err = getDaemonSets(ctx, *namespace, instrumentationLabeled) + dss, err = getDaemonSets(ctx, *namespace) return err }) @@ -175,23 +140,16 @@ func GetWorkloadsInNamespace(ctx context.Context, nsName string, instrumentation return items, nil } -func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getDeployments(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().Deployments(namespace.Name).List, ctx, metav1.ListOptions{}, func(deps *appsv1.DeploymentList) error { for _, dep := range deps.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(dep.GetLabels(), string(WorkloadKindDeployment), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } numberOfInstances := int(dep.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ - Namespace: dep.Namespace, - Name: dep.Name, - Kind: k8sKindToGql(string(WorkloadKindDeployment)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: dep.Namespace, + Name: dep.Name, + Kind: k8sKindToGql(string(WorkloadKindDeployment)), + NumberOfInstances: &numberOfInstances, }) } return nil @@ -204,23 +162,16 @@ func getDeployments(ctx context.Context, namespace corev1.Namespace, instrumenta return response, nil } -func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getDaemonSets(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().DaemonSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(dss *appsv1.DaemonSetList) error { for _, ds := range dss.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ds.GetLabels(), string(WorkloadKindDaemonSet), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } numberOfInstances := int(ds.Status.NumberReady) response = append(response, model.K8sActualSource{ - Namespace: ds.Namespace, - Name: ds.Name, - Kind: k8sKindToGql(string(WorkloadKindDaemonSet)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: ds.Namespace, + Name: ds.Name, + Kind: k8sKindToGql(string(WorkloadKindDaemonSet)), + NumberOfInstances: &numberOfInstances, }) } return nil @@ -233,23 +184,16 @@ func getDaemonSets(ctx context.Context, namespace corev1.Namespace, instrumentat return response, nil } -func getStatefulSets(ctx context.Context, namespace corev1.Namespace, instrumentationLabeled *bool) ([]model.K8sActualSource, error) { +func getStatefulSets(ctx context.Context, namespace corev1.Namespace) ([]model.K8sActualSource, error) { var response []model.K8sActualSource err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.AppsV1().StatefulSets(namespace.Name).List, ctx, metav1.ListOptions{}, func(sss *appsv1.StatefulSetList) error { for _, ss := range sss.Items { - _, _, decisionText, autoInstrumented := workload.GetInstrumentationLabelTexts(ss.GetLabels(), string(WorkloadKindStatefulSet), namespace.GetLabels()) - if instrumentationLabeled != nil && *instrumentationLabeled != autoInstrumented { - continue - } numberOfInstances := int(ss.Status.ReadyReplicas) response = append(response, model.K8sActualSource{ - Namespace: ss.Namespace, - Name: ss.Name, - Kind: k8sKindToGql(string(WorkloadKindStatefulSet)), - NumberOfInstances: &numberOfInstances, - AutoInstrumented: autoInstrumented, - AutoInstrumentedDecision: decisionText, - InstrumentedApplicationDetails: nil, // TODO: fill this + Namespace: ss.Namespace, + Name: ss.Name, + Kind: k8sKindToGql(string(WorkloadKindStatefulSet)), + NumberOfInstances: &numberOfInstances, }) } return nil @@ -319,3 +263,83 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s } return annotations } + +func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { + source, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ + consts.OdigosNamespaceAnnotation: nsName, + consts.OdigosWorkloadNameAnnotation: workloadName, + consts.OdigosWorkloadKindAnnotation: string(workloadKind), + }).String()}) + + if err != nil { + return nil, err + } + if len(source.Items) == 0 { + return nil, fmt.Errorf(`source "%s" not found`, workloadName) + } + if len(source.Items) > 1 { + return nil, fmt.Errorf(`expected to get 1 source "%s", got %d`, workloadName, len(source.Items)) + } + + crdName := source.Items[0].Name + crd, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) + + return crd, err +} + +func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + err := CheckWorkloadKind(workloadKind) + if err != nil { + return err + } + + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) + if source != nil && err == nil { + // we don't want to throw error, because we want to allow the UI to send an array of all sources even if instrumented + return nil + } + + newSource := &v1alpha1.Source{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "source-", + }, + Spec: v1alpha1.SourceSpec{ + Workload: workload.PodWorkload{ + Namespace: nsName, + Name: workloadName, + Kind: workload.WorkloadKind(workloadKind), + }, + }, + } + + _, err = kube.DefaultClient.OdigosClient.Sources(nsName).Create(ctx, newSource, metav1.CreateOptions{}) + return err +} + +func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { + err := CheckWorkloadKind(workloadKind) + if err != nil { + return err + } + + source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) + if err != nil { + // we don't want to throw error, because we want to allow the UI to send an array of all sources even if not instrumented + return nil + } + + err = kube.DefaultClient.OdigosClient.Sources(nsName).Delete(ctx, source.Name, metav1.DeleteOptions{}) + return err +} + +func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { + if enabled == nil { + return fmt.Errorf("enabled must be provided") + } + + if *enabled { + return createSourceCRD(ctx, nsName, workloadName, workloadKind) + } else { + return deleteSourceCRD(ctx, nsName, workloadName, workloadKind) + } +} diff --git a/frontend/services/utils.go b/frontend/services/utils.go index 71218b665..c092e532e 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -1,18 +1,14 @@ package services import ( - "context" "errors" "fmt" "path" - "strings" "time" "github.com/odigos-io/odigos/common" "github.com/odigos-io/odigos/frontend/graph/model" - "github.com/odigos-io/odigos/frontend/kube" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" ) const cdnUrl = "https://d15jtxgb40qetw.cloudfront.net" @@ -21,37 +17,6 @@ func GetImageURL(image string) string { return path.Join(cdnUrl, image) } -func setWorkloadInstrumentationLabel(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { - jsonMergePatchData := GetJsonMergePatchForInstrumentationLabel(enabled) - - switch workloadKind { - case WorkloadKindDeployment: - _, err := kube.DefaultClient.AppsV1().Deployments(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindStatefulSet: - _, err := kube.DefaultClient.AppsV1().StatefulSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - case WorkloadKindDaemonSet: - _, err := kube.DefaultClient.AppsV1().DaemonSets(nsName).Patch(ctx, workloadName, types.MergePatchType, jsonMergePatchData, metav1.PatchOptions{}) - return err - default: - return errors.New("unsupported workload kind " + string(workloadKind)) - } -} - -func ConvertFieldsToString(fields map[string]string) string { - if len(fields) == 0 { - return "" - } - - var parts []string - for key, value := range fields { - parts = append(parts, fmt.Sprintf("%s: %s", key, value)) - } - - return strings.Join(parts, ", ") -} - func ConvertSignals(signals []model.SignalType) ([]common.ObservabilitySignal, error) { var result []common.ObservabilitySignal for _, s := range signals { @@ -86,3 +51,12 @@ func Metav1TimeToString(latestStatusTime metav1.Time) string { } return latestStatusTime.Time.Format(time.RFC3339) } + +func CheckWorkloadKind(kind WorkloadKind) error { + switch kind { + case WorkloadKindDeployment, WorkloadKindStatefulSet, WorkloadKindDaemonSet: + return nil + default: + return errors.New("unsupported workload kind: " + string(kind)) + } +} diff --git a/frontend/webapp/app/page.tsx b/frontend/webapp/app/page.tsx index 9df649d3c..2dcaf764a 100644 --- a/frontend/webapp/app/page.tsx +++ b/frontend/webapp/app/page.tsx @@ -2,10 +2,10 @@ import { useEffect } from 'react'; import { useConfig } from '@/hooks'; import { CenterThis } from '@/styles'; -import { ROUTES, CONFIG } from '@/utils'; import { NOTIFICATION_TYPE } from '@/types'; import { useRouter } from 'next/navigation'; import { useNotificationStore } from '@/store'; +import { ROUTES, CONFIG, ACTION } from '@/utils'; import { FadeLoader } from '@/reuseable-components'; export default function App() { @@ -17,8 +17,8 @@ export default function App() { if (error) { addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: error.name, - message: error.message, + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, }); } else if (data) { const { installation } = data; diff --git a/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx b/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx index 571fc3bcd..5c687db28 100644 --- a/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx +++ b/frontend/webapp/components/common/dropdowns/error-dropdown/index.tsx @@ -20,8 +20,8 @@ export const ErrorDropdown: React.FC = ({ title = 'Error Message', value, const options = useMemo(() => { const payload: DropdownOption[] = []; - sources.forEach(({ instrumentedApplicationDetails: { conditions } }) => { - conditions.forEach(({ type, status, message }) => { + sources.forEach(({ conditions }) => { + conditions.forEach(({ status, message }) => { if (status === BACKEND_BOOLEAN.FALSE && !payload.find((opt) => opt.id === message)) { payload.push({ id: message, value: message }); } diff --git a/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx b/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx index 31c53ac9a..92fbfc011 100644 --- a/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx +++ b/frontend/webapp/components/common/dropdowns/language-dropdown/index.tsx @@ -19,7 +19,7 @@ export const LanguageDropdown: React.FC = ({ title = 'Programming Languag const options = useMemo(() => { const payload: DropdownOption[] = []; - sources.forEach(({ instrumentedApplicationDetails: { containers } }) => { + sources.forEach(({ containers }) => { containers.forEach(({ language }) => { if (!payload.find((opt) => opt.id === language)) { payload.push({ id: language, value: language }); diff --git a/frontend/webapp/components/notification/notification-manager.tsx b/frontend/webapp/components/notification/notification-manager.tsx index 0bc4791b1..d90188bdf 100644 --- a/frontend/webapp/components/notification/notification-manager.tsx +++ b/frontend/webapp/components/notification/notification-manager.tsx @@ -64,7 +64,8 @@ const NewCount = styled(Text)` `; export const NotificationManager = () => { - const { notifications, markAsSeen } = useNotificationStore(); + const { notifications: n, markAsSeen } = useNotificationStore(); + const notifications = n.filter(({ hideFromHistory }) => !hideFromHistory); const unseen = notifications.filter(({ seen }) => !seen); const unseenCount = unseen.length; diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index 9720c7bb2..99b01179a 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -41,7 +41,7 @@ const StyledAddDestinationButton = styled(Button)` export function AddDestinationContainer() { const router = useRouter(); - const { createSources } = useSourceCRUD(); + const { persistSources } = useSourceCRUD(); const { createDestination } = useDestinationCRUD(); const { configuredSources, configuredFutureApps, configuredDestinations, resetState } = useAppStore((state) => state); @@ -58,15 +58,20 @@ export function AddDestinationContainer() { const clickDone = async () => { setIsLoading(true); - await createSources(configuredSources, configuredFutureApps); + const payload: typeof configuredSources = {}; + + Object.entries(configuredSources).forEach(([namespace, sources]) => { + payload[namespace] = sources.map((source) => ({ + ...source, + selected: true, + })); + }); + + await persistSources(payload, configuredFutureApps); await Promise.all(configuredDestinations.map(async ({ form }) => await createDestination(form))); - // Delay redirect by 3 seconds to allow the sources to be created on the backend 1st, - // otherwise we would have to apply polling on the overview page on every mount. - setTimeout(() => { - resetState(); - router.push(ROUTES.OVERVIEW); - }, 3000); + resetState(); + router.push(ROUTES.OVERVIEW); }; const isSourcesListEmpty = () => !Object.values(configuredSources).some((sources) => !!sources.length); diff --git a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx index 5dd44ce68..ae906d20d 100644 --- a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx @@ -55,7 +55,7 @@ export const DestinationsList: React.FC = ({ items, setSe title={destinationItem.displayName} iconSrc={destinationItem.imageUrl} hoverText='Select' - monitors={Object.keys(destinationItem.supportedSignals).filter((signal) => destinationItem.supportedSignals[signal].supported)} + monitors={Object.keys(destinationItem.supportedSignals || {}).filter((signal) => destinationItem.supportedSignals[signal].supported)} monitorsWithLabels onClick={() => setSelectedItems(destinationItem)} /> diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx index cdcbfeda9..1a1b02573 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx @@ -72,7 +72,14 @@ export const RuleDrawer: React.FC = () => { const handleEdit = (bool?: boolean) => { if (item.type === InstrumentationRuleType.UNKNOWN_TYPE && (bool || bool === undefined)) { - addNotification({ type: NOTIFICATION_TYPE.WARNING, title: FORM_ALERTS.FORBIDDEN, message: FORM_ALERTS.CANNOT_EDIT_RULE, crdType: OVERVIEW_ENTITY_TYPES.RULE, target: id }); + addNotification({ + type: NOTIFICATION_TYPE.WARNING, + title: FORM_ALERTS.FORBIDDEN, + message: FORM_ALERTS.CANNOT_EDIT_RULE, + crdType: OVERVIEW_ENTITY_TYPES.RULE, + target: id, + hideFromHistory: true, + }); } else { setIsEditing(typeof bool === 'boolean' ? bool : true); } diff --git a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx index 4181dda72..ebe13b893 100644 --- a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx +++ b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx @@ -31,7 +31,7 @@ export const MultiSourceControl = () => { animateOut: slide.out['center'], }); - const { sources, deleteSources } = useSourceCRUD(); + const { sources, persistSources } = useSourceCRUD(); const { configuredSources, setConfiguredSources } = useAppStore(); const [isWarnModalOpen, setIsWarnModalOpen] = useState(false); @@ -50,7 +50,13 @@ export const MultiSourceControl = () => { }; const onDelete = () => { - deleteSources(configuredSources); + const payload: typeof configuredSources = {}; + + Object.entries(configuredSources).forEach(([namespace, selectedSources]) => { + payload[namespace] = selectedSources.map(({ selected, ...rest }) => ({ ...rest, selected: false })); + }); + + persistSources(payload, {}); onDeselect(); setIsWarnModalOpen(false); }; diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx index 9fe3201ab..d3c02e349 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx @@ -12,12 +12,20 @@ export const AddSourceModal: React.FC = ({ isOpen, onClose }) => { useKeyDown({ key: 'Enter', active: isOpen }, () => handleSubmit()); const menuState = useSourceFormData(); - const { createSources } = useSourceCRUD({ onSuccess: onClose }); + const { persistSources } = useSourceCRUD({ onSuccess: onClose }); const handleSubmit = async () => { - const { selectedSources, selectedFutureApps } = menuState; + const { availableSources, selectedSources, selectedFutureApps } = menuState; + const payload: typeof availableSources = {}; - await createSources(selectedSources, selectedFutureApps); + Object.entries(availableSources).forEach(([namespace, sources]) => { + payload[namespace] = sources.map((source) => ({ + ...source, + selected: !!selectedSources[namespace].find(({ kind, name }) => kind === source.kind && name === source.name), + })); + }); + + await persistSources(payload, selectedFutureApps); }; return ( diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx index 97231f57f..f38e35d4a 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import theme from '@/styles/theme'; import styled from 'styled-components'; import { UseSourceFormDataResponse } from '@/hooks'; -import { Checkbox, Divider, ExtendIcon, NoDataFound, Text, Toggle } from '@/reuseable-components'; +import { Checkbox, Divider, ExtendIcon, FadeLoader, NoDataFound, Text, Toggle } from '@/reuseable-components'; interface Props extends UseSourceFormDataResponse { isModal?: boolean; @@ -89,6 +89,7 @@ const NoDataFoundWrapper = styled.div` export const SourcesList: React.FC = ({ isModal = false, + namespacesLoading, selectedNamespace, onSelectNamespace, @@ -101,18 +102,13 @@ export const SourcesList: React.FC = ({ searchText, selectAllForNamespace, onSelectAll, - showSelectedOnly, filterSources, }) => { const namespaces = Object.entries(availableSources); if (!namespaces.length) { - return ( - - - - ); + return {namespacesLoading ? : }; } return ( diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx index 8bc147a45..76f4a163a 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { FolderIcon } from '@/assets'; import styled from 'styled-components'; import { type UseSourceFormDataResponse } from '@/hooks'; -import { Checkbox, NoDataFound, Text } from '@/reuseable-components'; +import { Checkbox, FadeLoader, NoDataFound, Text } from '@/reuseable-components'; interface Props extends UseSourceFormDataResponse { isModal?: boolean; @@ -74,6 +74,7 @@ const NoDataFoundWrapper = styled.div` export const SourcesList: React.FC = ({ isModal = false, + namespacesLoading, selectedNamespace, availableSources, @@ -85,11 +86,7 @@ export const SourcesList: React.FC = ({ const sources = availableSources[selectedNamespace] || []; if (!sources.length) { - return ( - - - - ); + return {namespacesLoading ? : }; } return ( diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts index e72c56c6b..1ed993fec 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts +++ b/frontend/webapp/containers/main/sources/source-drawer-container/build-drawer-item.ts @@ -3,16 +3,17 @@ import type { K8sActualSource, WorkloadId } from '@/types'; const buildDrawerItem = (id: WorkloadId, formData: { reportedName: string }, drawerItem: K8sActualSource): K8sActualSource => { const { namespace, name, kind } = id; const { reportedName } = formData; - const { selected, numberOfInstances, instrumentedApplicationDetails } = drawerItem; + const { numberOfInstances, conditions, containers, selected } = drawerItem; return { namespace, name, kind, + numberOfInstances, reportedName, + conditions, + containers, selected, - numberOfInstances, - instrumentedApplicationDetails, }; }; diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx index 750696632..5e23efabb 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -33,7 +33,7 @@ const DataContainer = styled.div` export const SourceDrawer: React.FC = () => { const { selectedItem, setSelectedItem } = useDrawerStore(); - const { deleteSources, updateSource } = useSourceCRUD({ + const { persistSources, updateSource } = useSourceCRUD({ onSuccess: (type) => { setIsEditing(false); setIsFormDirty(false); @@ -79,12 +79,11 @@ export const SourceDrawer: React.FC = () => { const { item } = selectedItem as { item: K8sActualSource }; const hasPresenceOfOtherAgent = - item?.instrumentedApplicationDetails?.conditions?.some( - (condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent'), - ) || false; + item?.conditions?.some((condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent')) || + false; return ( - item?.instrumentedApplicationDetails?.containers?.map( + item?.containers?.map( (container) => ({ type: DataCardFieldTypes.SOURCE_CONTAINER, @@ -113,7 +112,8 @@ export const SourceDrawer: React.FC = () => { const handleDelete = async () => { const { namespace } = item; - await deleteSources({ [namespace]: [item] }); + + await persistSources({ [namespace]: [{ ...item, selected: false }] }, {}); }; const handleSave = async () => { @@ -146,7 +146,7 @@ export const SourceDrawer: React.FC = () => { ) : ( - + { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { - cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); - cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { + cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); + cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); + }); }); }); }); @@ -70,8 +73,11 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { - cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); - cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); + // Wait for 10 seconds to allow the backend to batch an SSE notification + cy.wait(10000).then(() => { + cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); + cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); + }); }); }); }); diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index 674c61246..c02fd80dd 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -3,26 +3,28 @@ import { gql } from '@apollo/client'; export const GET_COMPUTE_PLATFORM = gql` query GetComputePlatform { computePlatform { + k8sActualNamespaces { + name + } k8sActualSources { + namespace name kind - namespace numberOfInstances + selected reportedName - instrumentedApplicationDetails { - containers { - containerName - language - runtimeVersion - otherAgent - } - conditions { - status - type - reason - message - lastTransitionTime - } + containers { + containerName + language + runtimeVersion + otherAgent + } + conditions { + status + type + reason + message + lastTransitionTime } } destinations { @@ -96,23 +98,20 @@ export const GET_COMPUTE_PLATFORM = gql` } } } - k8sActualNamespaces { - name - } } } `; export const GET_NAMESPACES = gql` - query GetK8sActualNamespace($namespaceName: String!, $instrumentationLabeled: Boolean) { + query GetK8sActualNamespace($namespaceName: String!) { computePlatform { k8sActualNamespace(name: $namespaceName) { name - instrumentationLabelEnabled - k8sActualSources(instrumentationLabeled: $instrumentationLabeled) { + k8sActualSources { kind name numberOfInstances + selected } } } diff --git a/frontend/webapp/graphql/queries/describe.ts b/frontend/webapp/graphql/queries/describe.ts index 1280a312e..cfd09670e 100644 --- a/frontend/webapp/graphql/queries/describe.ts +++ b/frontend/webapp/graphql/queries/describe.ts @@ -188,20 +188,6 @@ export const DESCRIBE_SOURCE = gql` explain } } - instrumentationConfig { - created { - name - value - status - explain - } - createTime { - name - value - status - explain - } - } runtimeInfo { generation { name @@ -236,7 +222,7 @@ export const DESCRIBE_SOURCE = gql` } } } - instrumentedApplication { + instrumentationConfig { created { name value diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index f261ee47e..9cadebd79 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -15,45 +15,40 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.ACTION, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.ACTION) : undefined, + hideFromHistory, }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + const handleComplete = (actionType: string, message: string, id?: string) => { + notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); refetch(); - params?.onSuccess?.(title); + params?.onSuccess?.(actionType); }; const [createAction, cState] = useMutation<{ createAction: { id: string } }>(CREATE_ACTION, { onError: (error) => handleError(ACTION.CREATE, error.message), onCompleted: (res, req) => { - const id = res.createAction.id; - const type = req?.variables?.action.type; - const name = req?.variables?.action.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `action "${label}" was created`, id); + const id = res?.createAction?.id; + handleComplete(ACTION.CREATE, `Action "${id}" created`, id); }, }); const [updateAction, uState] = useMutation<{ updateAction: { id: string } }>(UPDATE_ACTION, { onError: (error) => handleError(ACTION.UPDATE, error.message), onCompleted: (res, req) => { - const id = res.updateAction.id; - const type = req?.variables?.action.type; - const name = req?.variables?.action.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `action "${label}" was updated`, id); + const id = res?.updateAction?.id; + handleComplete(ACTION.UPDATE, `Action "${id}" updated`, id); }, }); const [deleteAction, dState] = useMutation<{ deleteAction: boolean }>(DELETE_ACTION, { @@ -61,16 +56,22 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { onCompleted: (res, req) => { const id = req?.variables?.id; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.ACTION)); - handleComplete(ACTION.DELETE, `action "${id}" was deleted`); + handleComplete(ACTION.DELETE, `Action "${id}" deleted`, id); }, }); return { loading: cState.loading || uState.loading || dState.loading, - actions: data?.computePlatform.actions || [], + actions: data?.computePlatform?.actions || [], - createAction: (action: ActionInput) => createAction({ variables: { action } }), - updateAction: (id: string, action: ActionInput) => updateAction({ variables: { id, action } }), - deleteAction: (id: string, actionType: ActionsType) => deleteAction({ variables: { id, actionType } }), + createAction: (action: ActionInput) => { + createAction({ variables: { action } }); + }, + updateAction: (id: string, action: ActionInput) => { + updateAction({ variables: { id, action } }); + }, + deleteAction: (id: string, actionType: ActionsType) => { + deleteAction({ variables: { id, actionType } }); + }, }; }; diff --git a/frontend/webapp/hooks/actions/useActionFormData.ts b/frontend/webapp/hooks/actions/useActionFormData.ts index 55b880056..5f6aed157 100644 --- a/frontend/webapp/hooks/actions/useActionFormData.ts +++ b/frontend/webapp/hooks/actions/useActionFormData.ts @@ -55,6 +55,7 @@ export function useActionFormData() { type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts index da943fe0c..be53749ad 100644 --- a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts +++ b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts @@ -1,9 +1,9 @@ -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { useQuery } from '@apollo/client'; import { useNotificationStore } from '@/store'; import { GET_COMPUTE_PLATFORM } from '@/graphql'; import { useFilterStore } from '@/store/useFilterStore'; -import { BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils'; +import { ACTION, BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils'; import { NOTIFICATION_TYPE, type ActionItem, type ComputePlatform, type ComputePlatformMapped } from '@/types'; type UseComputePlatformHook = { @@ -15,19 +15,17 @@ type UseComputePlatformHook = { }; export const useComputePlatform = (): UseComputePlatformHook => { - const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM); const { addNotification } = useNotificationStore(); const filters = useFilterStore(); - useEffect(() => { - if (error) { + const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM, { + onError: (error) => addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: error.name, - message: error.cause?.message, - }); - } - }, [error]); + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, + }), + }); const mappedData = useMemo(() => { if (!data) return undefined; @@ -84,15 +82,13 @@ export const useComputePlatform = (): UseComputePlatformHook => { k8sActualSources = k8sActualSources.filter((source) => !!filters.types.find((type) => type.id === source.kind)); } if (!!filters.onlyErrors) { - k8sActualSources = k8sActualSources.filter((source) => !!source.instrumentedApplicationDetails?.conditions?.find((cond) => cond.status === BACKEND_BOOLEAN.FALSE)); + k8sActualSources = k8sActualSources.filter((source) => !!source.conditions?.find((cond) => cond.status === BACKEND_BOOLEAN.FALSE)); } if (!!filters.errors.length) { - k8sActualSources = k8sActualSources.filter((source) => !!filters.errors.find((error) => !!source.instrumentedApplicationDetails?.conditions?.find((cond) => cond.message === error.id))); + k8sActualSources = k8sActualSources.filter((source) => !!filters.errors.find((error) => !!source.conditions?.find((cond) => cond.message === error.id))); } if (!!filters.languages.length) { - k8sActualSources = k8sActualSources.filter( - (source) => !!filters.languages.find((language) => !!source.instrumentedApplicationDetails?.containers?.find((cont) => cont.language === language.id)), - ); + k8sActualSources = k8sActualSources.filter((source) => !!filters.languages.find((language) => !!source.containers?.find((cont) => cont.language === language.id))); } if (!!filters.monitors.length) { destinations = destinations.filter((destination) => !!filters.monitors.find((metric) => destination.exportedSignals[metric.id])); diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index fe95b9ea5..33d5a5c1d 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -1,37 +1,38 @@ +import { ACTION } from '@/utils'; import { useNotificationStore } from '@/store'; import { useMutation, useQuery } from '@apollo/client'; import { useComputePlatform } from './useComputePlatform'; import { GET_NAMESPACES, PERSIST_NAMESPACE } from '@/graphql'; import { type ComputePlatform, NOTIFICATION_TYPE, type PersistNamespaceItemInput } from '@/types'; -export const useNamespace = (namespaceName?: string, instrumentationLabeled = null as boolean | null) => { +export const useNamespace = (namespaceName?: string) => { const { addNotification } = useNotificationStore(); const cp = useComputePlatform(); - const handleError = (title: string, message: string) => { - addNotification({ type: NOTIFICATION_TYPE.ERROR, title, message }); - }; - - const handleComplete = (title: string, message: string) => { - addNotification({ type: NOTIFICATION_TYPE.SUCCESS, title, message }); - }; - - const { data, loading, error } = useQuery(GET_NAMESPACES, { + const { data, loading } = useQuery(GET_NAMESPACES, { skip: !namespaceName, - fetchPolicy: 'cache-first', - variables: { namespaceName, instrumentationLabeled }, + variables: { namespaceName }, + onError: (error) => + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, + }), }); const [persistNamespaceMutation] = useMutation(PERSIST_NAMESPACE, { - onError: (error) => handleError('', error.message), - onCompleted: (res, req) => {}, + onError: (error) => + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: error.name || ACTION.UPDATE, + message: error.cause?.message || error.message, + }), }); return { + loading, + data: data?.computePlatform.k8sActualNamespace, allNamespaces: cp.data?.computePlatform.k8sActualNamespaces, persistNamespace: async (namespace: PersistNamespaceItemInput) => await persistNamespaceMutation({ variables: { namespace } }), - data: data?.computePlatform.k8sActualNamespace, - loading, - error, }; }; diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 4870b6617..93ea5b701 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -12,76 +12,60 @@ interface Params { export const useDestinationCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); - const { data, refetch } = useComputePlatform(); + const { data } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.DESTINATION) : undefined, + hideFromHistory, }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); - refetch(); - params?.onSuccess?.(title); + const handleComplete = (actionType: string) => { + params?.onSuccess?.(actionType); }; - const [createDestination, cState] = useMutation<{ - createNewDestination: { id: string }; - }>(CREATE_DESTINATION, { + const [createDestination, cState] = useMutation<{ createNewDestination: { id: string } }>(CREATE_DESTINATION, { onError: (error) => handleError(ACTION.CREATE, error.message), - onCompleted: (res, req) => { - const id = res.createNewDestination.id; - const type = req?.variables?.destination.type; - const name = req?.variables?.destination.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `destination "${label}" was created`, id); - }, + onCompleted: () => handleComplete(ACTION.CREATE), }); - const [updateDestination, uState] = useMutation<{ - updateDestination: { id: string }; - }>(UPDATE_DESTINATION, { + const [updateDestination, uState] = useMutation<{ updateDestination: { id: string } }>(UPDATE_DESTINATION, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = res.updateDestination.id; - const type = req?.variables?.destination.type; - const name = req?.variables?.destination.name; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `destination "${label}" was updated`, id); - }, + onCompleted: () => handleComplete(ACTION.UPDATE), }); - const [deleteDestination, dState] = useMutation<{ - deleteDestination: boolean; - }>(DELETE_DESTINATION, { + const [deleteDestination, dState] = useMutation<{ deleteDestination: boolean }>(DELETE_DESTINATION, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.id; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.DESTINATION)); - handleComplete(ACTION.DELETE, `destination "${id}" was deleted`); + handleComplete(ACTION.DELETE); }, }); return { loading: cState.loading || uState.loading || dState.loading, - destinations: data?.computePlatform.destinations || [], + destinations: data?.computePlatform?.destinations || [], createDestination: (destination: DestinationInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Creating destination...', undefined, true); createDestination({ variables: { destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, updateDestination: (id: string, destination: DestinationInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating destination...', undefined, true); updateDestination({ variables: { id, destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, deleteDestination: (id: string) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Deleting destination...', undefined, true); deleteDestination({ variables: { id } }); }, }; diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 8fbb73bb9..916bece0d 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -104,8 +104,8 @@ export function useDestinationFormData(params?: { destinationType?: string; supp onError: (error) => addNotification({ type: NOTIFICATION_TYPE.ERROR, - title: ACTION.FETCH, - message: error.message, + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, }), }); @@ -171,6 +171,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 407c8961b..99b61bf17 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -1,7 +1,7 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; +import { ACTION, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; -import { ACTION, deriveTypeFromRule, getSseTargetFromId } from '@/utils'; import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput } from '@/types'; import { CREATE_INSTRUMENTATION_RULE, UPDATE_INSTRUMENTATION_RULE, DELETE_INSTRUMENTATION_RULE } from '@/graphql/mutations'; @@ -15,68 +15,63 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.RULE, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.RULE) : undefined, + hideFromHistory, }); }; - const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + const handleComplete = (actionType: string, message: string, id?: string) => { + notifyUser(NOTIFICATION_TYPE.SUCCESS, actionType, message, id); refetch(); - params?.onSuccess?.(title); + params?.onSuccess?.(actionType); }; - const [createInstrumentationRule, cState] = useMutation<{ - createInstrumentationRule: { ruleId: string }; - }>(CREATE_INSTRUMENTATION_RULE, { + const [createInstrumentationRule, cState] = useMutation<{ createInstrumentationRule: { ruleId: string } }>(CREATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.CREATE, error.message), onCompleted: (res, req) => { - const id = res.createInstrumentationRule.ruleId; - const type = deriveTypeFromRule(req?.variables?.instrumentationRule); - const name = req?.variables?.instrumentationRule.ruleName; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.CREATE, `instrumentation rule "${label}" was created`, id); + const id = res?.createInstrumentationRule?.ruleId; + handleComplete(ACTION.CREATE, `Rule "${id}" created`, id); }, }); - const [updateInstrumentationRule, uState] = useMutation<{ - updateInstrumentationRule: { ruleId: string }; - }>(UPDATE_INSTRUMENTATION_RULE, { + const [updateInstrumentationRule, uState] = useMutation<{ updateInstrumentationRule: { ruleId: string } }>(UPDATE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.UPDATE, error.message), onCompleted: (res, req) => { - const id = res.updateInstrumentationRule.ruleId; - const type = deriveTypeFromRule(req?.variables?.instrumentationRule); - const name = req?.variables?.instrumentationRule.ruleName; - const label = `${type}${!!name ? ` (${name})` : ''}`; - handleComplete(ACTION.UPDATE, `instrumentation rule "${label}" was updated`, id); + const id = res?.updateInstrumentationRule?.ruleId; + handleComplete(ACTION.UPDATE, `Rule "${id}" updated`, id); }, }); - const [deleteInstrumentationRule, dState] = useMutation<{ - deleteInstrumentationRule: boolean; - }>(DELETE_INSTRUMENTATION_RULE, { + const [deleteInstrumentationRule, dState] = useMutation<{ deleteInstrumentationRule: boolean }>(DELETE_INSTRUMENTATION_RULE, { onError: (error) => handleError(ACTION.DELETE, error.message), onCompleted: (res, req) => { const id = req?.variables?.ruleId; removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.RULE)); - handleComplete(ACTION.DELETE, `instrumentation rule "${id}" was deleted`); + handleComplete(ACTION.DELETE, `Rule "${id}" deleted`, id); }, }); return { loading: cState.loading || uState.loading || dState.loading, - instrumentationRules: data?.computePlatform.instrumentationRules || [], + instrumentationRules: data?.computePlatform?.instrumentationRules || [], - createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => createInstrumentationRule({ variables: { instrumentationRule } }), - updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }), - deleteInstrumentationRule: (ruleId: string) => deleteInstrumentationRule({ variables: { ruleId } }), + createInstrumentationRule: (instrumentationRule: InstrumentationRuleInput) => { + createInstrumentationRule({ variables: { instrumentationRule } }); + }, + updateInstrumentationRule: (ruleId: string, instrumentationRule: InstrumentationRuleInput) => { + updateInstrumentationRule({ variables: { ruleId, instrumentationRule } }); + }, + deleteInstrumentationRule: (ruleId: string) => { + deleteInstrumentationRule({ variables: { ruleId } }); + }, }; }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts index 20d14e7df..e430d7ff7 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts @@ -45,6 +45,7 @@ export function useInstrumentationRuleFormData() { type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, + hideFromHistory: true, }); } diff --git a/frontend/webapp/hooks/notification/useClickNotif.ts b/frontend/webapp/hooks/notification/useClickNotif.ts index a9ba5a970..0be9bc130 100644 --- a/frontend/webapp/hooks/notification/useClickNotif.ts +++ b/frontend/webapp/hooks/notification/useClickNotif.ts @@ -29,7 +29,7 @@ export const useClickNotif = () => { break; case OVERVIEW_ENTITY_TYPES.SOURCE: - case 'InstrumentedApplication': + case 'InstrumentationConfig': case 'InstrumentationInstance': drawerItem['type'] = OVERVIEW_ENTITY_TYPES.SOURCE; drawerItem['id'] = getIdFromSseTarget(target, OVERVIEW_ENTITY_TYPES.SOURCE); diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts index 4fc506a42..55e9e24be 100644 --- a/frontend/webapp/hooks/notification/useSSE.ts +++ b/frontend/webapp/hooks/notification/useSSE.ts @@ -4,18 +4,6 @@ import { NOTIFICATION_TYPE } from '@/types'; import { useComputePlatform } from '../compute-platform'; import { type NotifyPayload, useConnectionStore, useNotificationStore } from '@/store'; -const modifyType = (notification: NotifyPayload) => { - if (notification.title === 'Modified') { - if (notification.message?.indexOf('ProcessTerminated') === 0 || notification.message?.indexOf('NoHeartbeat') === 0 || notification.message?.indexOf('Failed') === 0) { - return NOTIFICATION_TYPE.ERROR; - } else { - return NOTIFICATION_TYPE.INFO; - } - } - - return notification.type; -}; - export const useSSE = () => { const { addNotification } = useNotificationStore(); const { setConnectionStore } = useConnectionStore(); @@ -40,8 +28,6 @@ export const useSSE = () => { target: data.target, }; - notification.type = modifyType(notification); - // Dispatch the notification to the store addNotification(notification); refetchComputePlatform(); diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index f70c0d706..5744ea9db 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -1,4 +1,3 @@ -import { useCallback } from 'react'; import { useMutation } from '@apollo/client'; import { ACTION, getSseTargetFromId } from '@/utils'; import { useAppStore, useNotificationStore } from '@/store'; @@ -15,76 +14,52 @@ export const useSourceCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { configuredSources, setConfiguredSources } = useAppStore(); + const { data } = useComputePlatform(); const { persistNamespace } = useNamespace(); - const { data, refetch } = useComputePlatform(); const { addNotification } = useNotificationStore(); - const startPolling = useCallback(async () => { - let retries = 0; - const maxRetries = 5; - const retryInterval = 1 * 1000; // time in milliseconds - - while (retries < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, retryInterval)); - refetch(); - retries++; - } - }, [refetch]); - - const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId) => { + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { addNotification({ type, title, message, crdType: OVERVIEW_ENTITY_TYPES.SOURCE, target: id ? getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE) : undefined, + hideFromHistory, }); }; - const handleError = (title: string, message: string, id?: WorkloadId) => { - notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); - params?.onError?.(title); + const handleError = (actionType: string, message: string) => { + notifyUser(NOTIFICATION_TYPE.ERROR, actionType, message); + params?.onError?.(actionType); }; - const handleComplete = (title: string, message: string, id?: WorkloadId) => { - notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); - startPolling(); - params?.onSuccess?.(title); + const handleComplete = (actionType: string) => { + params?.onSuccess?.(actionType); }; const [createOrDeleteSources, cdState] = useMutation<{ persistK8sSources: boolean }>(PERSIST_SOURCE, { - onError: (error, req) => { - const { selected } = req?.variables?.sources?.[0] || {}; - const action = selected ? ACTION.CREATE : ACTION.DELETE; - - handleError(action, error.message); - }, + onError: (error, req) => handleError('', error.message), onCompleted: (res, req) => { - const namespace = req?.variables?.namespace; - const { name, kind, selected } = req?.variables?.sources?.[0] || {}; - const count = req?.variables?.sources.length; - const action = selected ? ACTION.CREATE : ACTION.DELETE; - const fromOrIn = selected ? 'in' : 'from'; - if (count > 1) { - handleComplete(action, `${count} sources were ${action.toLowerCase()}d ${fromOrIn} "${namespace}"`); - } else { - const id = { kind, name, namespace }; + if (count === 1) { + const namespace = req?.variables?.namespace; + const { name, kind, selected } = req?.variables?.sources?.[0] || {}; + const id = { namespace, name, kind }; + if (!selected) removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE)); if (!selected) setConfiguredSources({ ...configuredSources, [namespace]: configuredSources[namespace]?.filter((source) => source.name !== name) || [] }); - handleComplete(action, `source "${name}" was ${action.toLowerCase()}d ${fromOrIn} "${namespace}"`, selected ? id : undefined); + handleComplete(selected ? ACTION.CREATE : ACTION.DELETE); + } else { + handleComplete(''); } }, }); const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => { - const id = req?.variables?.sourceId; - const name = id?.name; - handleComplete(ACTION.UPDATE, `source "${name}" was updated`, id); - }, + onCompleted: (res, req) => handleComplete(ACTION.UPDATE), }); const persistNamespaces = async (items: { [key: string]: boolean }) => { @@ -93,7 +68,7 @@ export const useSourceCRUD = (params?: Params) => { } }; - const persistSources = async (items: { [key: string]: K8sActualSource[] }, selected: boolean) => { + const persistSources = async (items: { [key: string]: K8sActualSource[] }) => { for (const [namespace, sources] of Object.entries(items)) { await createOrDeleteSources({ variables: { @@ -101,7 +76,7 @@ export const useSourceCRUD = (params?: Params) => { sources: sources.map((source) => ({ kind: source.kind, name: source.name, - selected, + selected: source.selected, })), }, }); @@ -112,11 +87,14 @@ export const useSourceCRUD = (params?: Params) => { loading: cdState.loading || uState.loading, sources: data?.computePlatform.k8sActualSources || [], - createSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { + persistSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Persisting sources...', undefined, true); await persistNamespaces(futureSelectAppsList); - await persistSources(selectAppsList, true); + await persistSources(selectAppsList); + }, + updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating sources...', undefined, true); + await updateSource({ variables: { sourceId, patchSourceRequest } }); }, - updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => await updateSource({ variables: { sourceId, patchSourceRequest } }), - deleteSources: async (selectAppsList: { [key: string]: K8sActualSource[] }) => await persistSources(selectAppsList, false), }; }; diff --git a/frontend/webapp/hooks/sources/useSourceFormData.ts b/frontend/webapp/hooks/sources/useSourceFormData.ts index 8e16d1399..7f31a3ff9 100644 --- a/frontend/webapp/hooks/sources/useSourceFormData.ts +++ b/frontend/webapp/hooks/sources/useSourceFormData.ts @@ -18,6 +18,8 @@ interface UseSourceFormDataParams { } export interface UseSourceFormDataResponse { + namespacesLoading: boolean; + selectedNamespace: SelectedNamespace; availableSources: SourcesByNamespace; selectedSources: SourcesByNamespace; @@ -50,7 +52,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo const [availableSources, setAvailableSources] = useState(appStore.availableSources); const [selectedSources, setSelectedSources] = useState(appStore.configuredSources); const [selectedFutureApps, setSelectedFutureApps] = useState(appStore.configuredFutureApps); - const { allNamespaces, data: namespacesData } = useNamespace(selectedNamespace, false); + const { allNamespaces, data: namespacesData, loading: namespacesLoading } = useNamespace(selectedNamespace); useEffect(() => { if (!!allNamespaces?.length) { @@ -76,7 +78,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo // set available sources for current selected namespace const { name, k8sActualSources = [] } = namespacesData; setAvailableSources((prev) => ({ ...prev, [name]: k8sActualSources })); - setSelectedSources((prev) => ({ ...prev, [name]: prev[name] || [] })); + setSelectedSources((prev) => ({ ...prev, [name]: prev[name] || k8sActualSources.filter(({ selected }) => selected) })); } }, [namespacesData]); @@ -197,6 +199,8 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo }; return { + namespacesLoading, + selectedNamespace, availableSources, selectedSources, diff --git a/frontend/webapp/types/common.ts b/frontend/webapp/types/common.ts index 5fa10761a..c0254c292 100644 --- a/frontend/webapp/types/common.ts +++ b/frontend/webapp/types/common.ts @@ -29,6 +29,7 @@ export interface Notification { target?: string; dismissed: boolean; seen: boolean; + hideFromHistory?: boolean; time: string; } diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index 16fea4d76..9808d3545 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -12,11 +12,11 @@ interface ComputePlatformData { id: string; name: string; computePlatformType: string; - k8sActualNamespace?: K8sActualNamespace; k8sActualNamespaces: K8sActualNamespace[]; - actions: ActionData[]; + k8sActualNamespace: K8sActualNamespace; k8sActualSources: K8sActualSource[]; destinations: ActualDestination[]; + actions: ActionData[]; instrumentationRules: InstrumentationRuleSpec[]; } diff --git a/frontend/webapp/types/describe.ts b/frontend/webapp/types/describe.ts index ead5c51e0..556bd5f54 100644 --- a/frontend/webapp/types/describe.ts +++ b/frontend/webapp/types/describe.ts @@ -12,11 +12,6 @@ interface InstrumentationLabelsAnalyze { instrumentedText?: EntityProperty; } -interface InstrumentationConfigAnalyze { - created: EntityProperty; - createTime?: EntityProperty; -} - interface ContainerRuntimeInfoAnalyze { containerName: EntityProperty; language: EntityProperty; @@ -29,7 +24,7 @@ interface RuntimeInfoAnalyze { containers: ContainerRuntimeInfoAnalyze[]; } -interface InstrumentedApplicationAnalyze { +interface InstrumentationConfigAnalyze { created: EntityProperty; createTime?: EntityProperty; containers: ContainerRuntimeInfoAnalyze[]; @@ -71,9 +66,8 @@ interface SourceAnalyze { namespace: EntityProperty; labels: InstrumentationLabelsAnalyze; - instrumentationConfig: InstrumentationConfigAnalyze; runtimeInfo?: RuntimeInfoAnalyze; - instrumentedApplication: InstrumentedApplicationAnalyze; + instrumentationConfig: InstrumentationConfigAnalyze; instrumentationDevice: InstrumentationDeviceAnalyze; totalPods: number; diff --git a/frontend/webapp/types/sources.ts b/frontend/webapp/types/sources.ts index f2ce90015..945b8d88a 100644 --- a/frontend/webapp/types/sources.ts +++ b/frontend/webapp/types/sources.ts @@ -9,16 +9,14 @@ export type SourceContainer = { }; export type K8sActualSource = { + namespace: string; name: string; kind: string; - namespace: string; - reportedName: string; numberOfInstances: number; - selected?: boolean; - instrumentedApplicationDetails: { - containers: Array; - conditions: Array; - }; + selected: boolean; + reportedName: string; + containers: Array; + conditions: Array; }; export type WorkloadId = { diff --git a/frontend/webapp/utils/constants/programming-languages.ts b/frontend/webapp/utils/constants/programming-languages.ts index 994a90879..33b09af38 100644 --- a/frontend/webapp/utils/constants/programming-languages.ts +++ b/frontend/webapp/utils/constants/programming-languages.ts @@ -19,31 +19,24 @@ export enum WORKLOAD_PROGRAMMING_LANGUAGES { } export const getMainContainerLanguage = (source: K8sActualSource): WORKLOAD_PROGRAMMING_LANGUAGES => { - const ia = source?.instrumentedApplicationDetails; + const { numberOfInstances, containers } = source; - if (!ia) { - if (source?.numberOfInstances > 0) { + if (!containers) { + if (numberOfInstances > 0) { return WORKLOAD_PROGRAMMING_LANGUAGES.PROCESSING; } else { return WORKLOAD_PROGRAMMING_LANGUAGES.NO_RUNNING_PODS; } } - const containers = ia.containers; - if (!containers) { - return WORKLOAD_PROGRAMMING_LANGUAGES.PROCESSING; - } - // we will filter out the ignored languages as we don't want to account them in the main language - const noneIgnoredLanguages = containers.filter((container) => container.language !== 'ignored'); - if (noneIgnoredLanguages.length === 0) { - return WORKLOAD_PROGRAMMING_LANGUAGES.NO_CONTAINERS; - } + const noneIgnoredLanguages = containers.filter((container) => container.language !== WORKLOAD_PROGRAMMING_LANGUAGES.IGNORED); + if (!noneIgnoredLanguages.length) return WORKLOAD_PROGRAMMING_LANGUAGES.NO_CONTAINERS; // find the first container with valid language - const mainContainer = noneIgnoredLanguages.find((container) => container.language !== 'unknown'); - if (!mainContainer) { - return WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN; // no valid language found, return the first one - } - return mainContainer.language as WORKLOAD_PROGRAMMING_LANGUAGES; + const mainContainer = noneIgnoredLanguages.find((container) => container.language !== WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN); + // no valid language found, return the first one + if (!mainContainer) return WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN; + + return mainContainer.language; }; diff --git a/frontend/webapp/utils/functions/strings/get-entity-label/index.ts b/frontend/webapp/utils/functions/strings/get-entity-label/index.ts index 930bad81e..ff6c0cae5 100644 --- a/frontend/webapp/utils/functions/strings/get-entity-label/index.ts +++ b/frontend/webapp/utils/functions/strings/get-entity-label/index.ts @@ -39,7 +39,7 @@ export const getEntityLabel = ( break; } - if (extended) return type + (name ? ` (${name})` : ''); + if (extended) return type + (name && name !== type ? ` (${name})` : ''); else if (prioritizeDisplayName) return name || type; else return type; }; diff --git a/frontend/webapp/utils/functions/strings/get-health-status/index.ts b/frontend/webapp/utils/functions/strings/get-health-status/index.ts index 247fc09d1..ff0169151 100644 --- a/frontend/webapp/utils/functions/strings/get-health-status/index.ts +++ b/frontend/webapp/utils/functions/strings/get-health-status/index.ts @@ -2,8 +2,8 @@ import { BACKEND_BOOLEAN } from '@/utils'; import { STATUSES, type ActualDestination, type K8sActualSource } from '@/types'; export const getHealthStatus = (item: K8sActualSource | ActualDestination) => { - const conditions = (item as K8sActualSource)?.instrumentedApplicationDetails?.conditions || (item as ActualDestination)?.conditions || []; - const isUnhealthy = !conditions.length || !!conditions.find(({ status }) => status === BACKEND_BOOLEAN.FALSE); + const conditions = item?.conditions || []; + const isUnhealthy = !!conditions.find(({ status }) => status === BACKEND_BOOLEAN.FALSE); return isUnhealthy ? STATUSES.UNHEALTHY : STATUSES.HEALTHY; }; diff --git a/helm/odigos/templates/ui/clusterrole.yaml b/helm/odigos/templates/ui/clusterrole.yaml index db40122df..75eaf295a 100644 --- a/helm/odigos/templates/ui/clusterrole.yaml +++ b/helm/odigos/templates/ui/clusterrole.yaml @@ -40,16 +40,24 @@ rules: - apiGroups: - odigos.io resources: - - instrumentedapplications - - instrumentationinstances - instrumentationconfigs + - instrumentationinstances verbs: - get - list - apiGroups: - odigos.io resources: - - instrumentedapplications + - sources + verbs: + - get + - list + - create + - delete + - apiGroups: + - odigos.io + resources: + - instrumentationconfigs - instrumentationinstances verbs: - watch diff --git a/instrumentor/controllers/deleteinstrumentedapplication/manager.go b/instrumentor/controllers/deleteinstrumentedapplication/manager.go index da00e9827..e401c2354 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/manager.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/manager.go @@ -77,17 +77,17 @@ func SetupWithManager(mgr ctrl.Manager) error { } err = builder. - ControllerManagedBy(mgr). - Named("deleteinstrumentedapplication-source"). - WithEventFilter(&k8sutils.OnlyUpdatesPredicate{}). - For(&odigosv1.Source{}). - Complete(&SourceReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - }) -if err != nil { - return err -} + ControllerManagedBy(mgr). + Named("deleteinstrumentedapplication-source"). + WithEventFilter(&k8sutils.OnlyUpdatesPredicate{}). + For(&odigosv1.Source{}). + Complete(&SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) + if err != nil { + return err + } return nil diff --git a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go index a60656046..acd8aba31 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go +++ b/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go @@ -20,8 +20,8 @@ import ( "context" "github.com/odigos-io/odigos/api/odigos/v1alpha1" - "github.com/odigos-io/odigos/k8sutils/pkg/workload" "github.com/odigos-io/odigos/k8sutils/pkg/consts" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" diff --git a/k8sutils/pkg/describe/source.go b/k8sutils/pkg/describe/source.go index d5bf6a75e..38bd055bd 100644 --- a/k8sutils/pkg/describe/source.go +++ b/k8sutils/pkg/describe/source.go @@ -23,12 +23,6 @@ func printWorkloadManifestInfo(analyze *source.SourceAnalyze, sb *strings.Builde printProperty(sb, 1, &analyze.Labels.InstrumentedText) } -func printInstrumentationConfigInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { - describeText(sb, 0, "\nInstrumentation Config:") - printProperty(sb, 1, &analyze.InstrumentationConfig.Created) - printProperty(sb, 1, analyze.InstrumentationConfig.CreateTime) -} - func printRuntimeDetails(analyze *source.SourceAnalyze, sb *strings.Builder) { describeText(sb, 0, "\nRuntime Inspection Details (new):") @@ -52,14 +46,13 @@ func printRuntimeDetails(analyze *source.SourceAnalyze, sb *strings.Builder) { } } -func printInstrumentedApplicationInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { - - describeText(sb, 0, "\nRuntime Inspection Details (old):") - printProperty(sb, 1, &analyze.InstrumentedApplication.Created) - printProperty(sb, 1, analyze.InstrumentedApplication.CreateTime) +func printInstrumentationConfigInfo(analyze *source.SourceAnalyze, sb *strings.Builder) { + describeText(sb, 0, "\nInstrumentation Config:") + printProperty(sb, 1, &analyze.InstrumentationConfig.Created) + printProperty(sb, 1, analyze.InstrumentationConfig.CreateTime) describeText(sb, 1, "Detected Containers:") - for _, container := range analyze.InstrumentedApplication.Containers { + for _, container := range analyze.InstrumentationConfig.Containers { printProperty(sb, 2, &container.ContainerName) printProperty(sb, 3, &container.Language) printProperty(sb, 3, &container.RuntimeVersion) @@ -122,9 +115,8 @@ func DescribeSourceToText(analyze *source.SourceAnalyze) string { var sb strings.Builder printWorkloadManifestInfo(analyze, &sb) - printInstrumentationConfigInfo(analyze, &sb) printRuntimeDetails(analyze, &sb) - printInstrumentedApplicationInfo(analyze, &sb) + printInstrumentationConfigInfo(analyze, &sb) printAppliedInstrumentationDeviceInfo(analyze, &sb) printPodsInfo(analyze, &sb) diff --git a/k8sutils/pkg/describe/source/analyze.go b/k8sutils/pkg/describe/source/analyze.go index a825b3929..0b03fc5a1 100644 --- a/k8sutils/pkg/describe/source/analyze.go +++ b/k8sutils/pkg/describe/source/analyze.go @@ -21,11 +21,6 @@ type InstrumentationLabelsAnalyze struct { InstrumentedText properties.EntityProperty `json:"instrumentedText"` } -type InstrumentationConfigAnalyze struct { - Created properties.EntityProperty `json:"created"` - CreateTime *properties.EntityProperty `json:"createTime"` -} - type ContainerRuntimeInfoAnalyze struct { ContainerName properties.EntityProperty `json:"containerName"` Language properties.EntityProperty `json:"language"` @@ -38,7 +33,7 @@ type RuntimeInfoAnalyze struct { Containers []ContainerRuntimeInfoAnalyze `json:"containers"` } -type InstrumentedApplicationAnalyze struct { +type InstrumentationConfigAnalyze struct { Created properties.EntityProperty `json:"created"` CreateTime *properties.EntityProperty `json:"createTime"` Containers []ContainerRuntimeInfoAnalyze `json:"containers"` @@ -80,10 +75,9 @@ type SourceAnalyze struct { Namespace properties.EntityProperty `json:"namespace"` Labels InstrumentationLabelsAnalyze `json:"labels"` - InstrumentationConfig InstrumentationConfigAnalyze `json:"instrumentationConfig"` - RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` - InstrumentedApplication InstrumentedApplicationAnalyze `json:"instrumentedApplication"` - InstrumentationDevice InstrumentationDeviceAnalyze `json:"instrumentationDevice"` + RuntimeInfo *RuntimeInfoAnalyze `json:"runtimeInfo"` + InstrumentationConfig InstrumentationConfigAnalyze `json:"instrumentationConfig"` + InstrumentationDevice InstrumentationDeviceAnalyze `json:"instrumentationDevice"` TotalPods int `json:"totalPods"` PodsPhasesCount string `json:"podsPhasesCount"` @@ -167,9 +161,15 @@ func analyzeInstrumentationConfig(resources *OdigosSourceResources, instrumented } } + containers := make([]ContainerRuntimeInfoAnalyze, 0) + if resources.InstrumentedApplication != nil { + containers = analyzeRuntimeDetails(resources.InstrumentedApplication.Spec.RuntimeDetails) + } + return InstrumentationConfigAnalyze{ Created: created, CreateTime: createdTime, + Containers: containers, } } @@ -229,37 +229,6 @@ func analyzeRuntimeInfo(resources *OdigosSourceResources) *RuntimeInfoAnalyze { } } -func analyzeInstrumentedApplication(resources *OdigosSourceResources) InstrumentedApplicationAnalyze { - instrumentedApplicationCreated := resources.InstrumentedApplication != nil - - created := properties.EntityProperty{ - Name: "Created", - Value: properties.GetTextCreated(instrumentedApplicationCreated), - Status: properties.GetSuccessOrTransitioning(instrumentedApplicationCreated), - Explain: "whether the instrumented application object exists in the cluster. When a workload is labeled for instrumentation, an instrumented application object is created", - } - - var createdTime *properties.EntityProperty - if instrumentedApplicationCreated { - createdTime = &properties.EntityProperty{ - Name: "create time", - Value: resources.InstrumentedApplication.GetCreationTimestamp().String(), - Explain: "the time when the instrumented application object was created", - } - } - - containers := make([]ContainerRuntimeInfoAnalyze, 0) - if resources.InstrumentedApplication != nil { - containers = analyzeRuntimeDetails(resources.InstrumentedApplication.Spec.RuntimeDetails) - } - - return InstrumentedApplicationAnalyze{ - Created: created, - CreateTime: createdTime, - Containers: containers, - } -} - func analyzeInstrumentationDevice(resources *OdigosSourceResources, workloadObj *K8sSourceObject, instrumented bool) InstrumentationDeviceAnalyze { instrumentedApplication := resources.InstrumentedApplication @@ -520,9 +489,8 @@ func analyzePods(resources *OdigosSourceResources, expectedDevices Instrumentati func AnalyzeSource(resources *OdigosSourceResources, workloadObj *K8sSourceObject) *SourceAnalyze { labelsAnalysis, instrumented := analyzeInstrumentationLabels(resources, workloadObj) - icAnalysis := analyzeInstrumentationConfig(resources, instrumented) runtimeAnalysis := analyzeRuntimeInfo(resources) - instrumentedApplication := analyzeInstrumentedApplication(resources) + icAnalysis := analyzeInstrumentationConfig(resources, instrumented) device := analyzeInstrumentationDevice(resources, workloadObj, instrumented) pods, podsText := analyzePods(resources, device) @@ -532,10 +500,9 @@ func AnalyzeSource(resources *OdigosSourceResources, workloadObj *K8sSourceObjec Namespace: properties.EntityProperty{Name: "Namespace", Value: workloadObj.GetNamespace(), Explain: "the namespace of the k8s workload object that this source describes"}, Labels: labelsAnalysis, - InstrumentationConfig: icAnalysis, - RuntimeInfo: runtimeAnalysis, - InstrumentedApplication: instrumentedApplication, - InstrumentationDevice: device, + RuntimeInfo: runtimeAnalysis, + InstrumentationConfig: icAnalysis, + InstrumentationDevice: device, TotalPods: len(pods), PodsPhasesCount: podsText, diff --git a/k8sutils/pkg/describe/source/resources.go b/k8sutils/pkg/describe/source/resources.go index 3f585f51f..caaaa4d89 100644 --- a/k8sutils/pkg/describe/source/resources.go +++ b/k8sutils/pkg/describe/source/resources.go @@ -43,13 +43,6 @@ func GetRelevantSourceResources(ctx context.Context, kubeClient kubernetes.Inter return nil, err } - ia, err := odigosClient.InstrumentedApplications(workloadNs).Get(ctx, runtimeObjectName, metav1.GetOptions{}) - if err == nil { - sourceResources.InstrumentedApplication = ia - } else if !apierrors.IsNotFound(err) { - return nil, err - } - instrumentedAppSelector := labels.SelectorFromSet(labels.Set{ "instrumented-app": runtimeObjectName, }) diff --git a/k8sutils/pkg/predicate/creation.go b/k8sutils/pkg/predicate/creation.go index 921a62795..481418370 100644 --- a/k8sutils/pkg/predicate/creation.go +++ b/k8sutils/pkg/predicate/creation.go @@ -24,4 +24,4 @@ func (i CreationPredicate) Generic(e event.GenericEvent) bool { return false } -var _ predicate.Predicate = &DeletionPredicate{} \ No newline at end of file +var _ predicate.Predicate = &DeletionPredicate{} diff --git a/k8sutils/pkg/workload/workload.go b/k8sutils/pkg/workload/workload.go index d8b6369a8..16fd5e699 100644 --- a/k8sutils/pkg/workload/workload.go +++ b/k8sutils/pkg/workload/workload.go @@ -111,54 +111,6 @@ func IsInstrumentationDisabledExplicitly(obj client.Object) bool { return false } -func GetInstrumentationLabelValue(labels map[string]string) *bool { - if val, exists := labels[consts.OdigosInstrumentationLabel]; exists { - enabled := val == consts.InstrumentationEnabled - return &enabled - } - - return nil -} - -func GetInstrumentationLabelTexts(workloadLabels map[string]string, workloadKind string, nsLabels map[string]string) (workloadText, nsText, decisionText string, sourceInstrumented bool) { - workloadLabel, workloadFound := workloadLabels[consts.OdigosInstrumentationLabel] - nsLabel, nsFound := nsLabels[consts.OdigosInstrumentationLabel] - - if workloadFound { - workloadText = consts.OdigosInstrumentationLabel + "=" + workloadLabel - } else { - workloadText = consts.OdigosInstrumentationLabel + " label not set" - } - - if nsFound { - nsText = consts.OdigosInstrumentationLabel + "=" + nsLabel - } else { - nsText = consts.OdigosInstrumentationLabel + " label not set" - } - - if workloadFound { - sourceInstrumented = workloadLabel == consts.InstrumentationEnabled - if sourceInstrumented { - decisionText = "Workload is instrumented because the " + workloadKind + " contains the label '" + consts.OdigosInstrumentationLabel + "=" + workloadLabel + "'" - } else { - decisionText = "Workload is NOT instrumented because the " + workloadKind + " contains the label '" + consts.OdigosInstrumentationLabel + "=" + workloadLabel + "'" - } - } else { - sourceInstrumented = nsLabel == consts.InstrumentationEnabled - if sourceInstrumented { - decisionText = "Workload is instrumented because the " + workloadKind + " is not labeled, and the namespace is labeled with '" + consts.OdigosInstrumentationLabel + "=" + nsLabel + "'" - } else { - if nsFound { - decisionText = "Workload is NOT instrumented because the " + workloadKind + " is not labeled, and the namespace is labeled with '" + consts.OdigosInstrumentationLabel + "=" + nsLabel + "'" - } else { - decisionText = "Workload is NOT instrumented because neither the workload nor the namespace has the '" + consts.OdigosInstrumentationLabel + "' label set" - } - } - } - - return -} - func GetWorkloadObject(ctx context.Context, objectKey client.ObjectKey, kind WorkloadKind, kubeClient client.Client) (metav1.Object, error) { switch kind { case WorkloadKindDeployment: From 3f1837cc1ae635124b5284904425c0218773280d Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Tue, 31 Dec 2024 13:39:26 +0200 Subject: [PATCH 179/259] chore: filter data base on groups and namespaces --- autoscaler/controllers/common/processors.go | 22 ++++++++++++------- .../processor.go | 3 ++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/autoscaler/controllers/common/processors.go b/autoscaler/controllers/common/processors.go index af2a27087..7a4481b5c 100644 --- a/autoscaler/controllers/common/processors.go +++ b/autoscaler/controllers/common/processors.go @@ -87,13 +87,13 @@ func AddFilterProcessors(allProcessors *odigosv1.ProcessorList, dests *odigosv1. //TODO: remove this log matchedSources := filterSources(sources.Items, dest.Spec.SourceSelector) - if len(matchedSources) == 0 { - //TODO: remove this log - logger.Info("No matching sources found for destination. Skipping processor creation.") + // if len(matchedSources) == 0 { + // //TODO: remove this log + // logger.Info("No matching sources found for destination. Skipping processor creation.") - //TODO: remove this log - continue - } + // //TODO: remove this log + // continue + // } //TODO: remove this log logger.Info("Matched sources for destination", "matchedSources", matchedSources) //TODO: remove this log @@ -145,8 +145,14 @@ func filterSources(sources []odigosv1.Source, selector *odigosv1.SourceSelector) for _, source := range sources { if selector.Mode == "namespaces" && contains(selector.Namespaces, source.Spec.Workload.Namespace) { filtered = append(filtered, source) - } else if selector.Mode == "groups" && containsAny(selector.Groups, source.Spec.Groups) { - filtered = append(filtered, source) + } else if selector.Mode == "groups" { + // Handle nil or empty groups gracefully + if source.Spec.Groups == nil || len(source.Spec.Groups) == 0 { + continue + } + if containsAny(selector.Groups, source.Spec.Groups) { + filtered = append(filtered, source) + } } } return filtered diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/processor.go b/collector/processors/odigossourcetodestinationfilterprocessor/processor.go index dec48003d..404098051 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/processor.go +++ b/collector/processors/odigossourcetodestinationfilterprocessor/processor.go @@ -111,6 +111,7 @@ func (fp *filterProcessor) logMatches(logAttributes, resourceAttributes pcommon. } func (fp *filterProcessor) matches(span ptrace.Span, resourceSpan ptrace.ResourceSpans) bool { + attributes := resourceSpan.Resource().Attributes() namespace := getAttribute(attributes, "k8s.namespace.name") @@ -140,7 +141,7 @@ func getDynamicNameAndKind(attributes pcommon.Map) (name string, kind string) { kind string key string }{ - {"deployment", "k8s.deployment.name"}, + {"Deployment", "k8s.deployment.name"}, {"statefulSet", "k8s.statefulset.name"}, {"daemonSet", "k8s.daemonset.name"}, } From 10d35d677a7b65ca3cff8d3072aa9e97626150bd Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Tue, 31 Dec 2024 17:01:16 +0200 Subject: [PATCH 180/259] [GEN-2140]: improve UX for "pending" states (#2106) This pull request includes multiple changes to improve the handling of pending states and notifications in the frontend web application. The most important changes include the addition of pending state management, updates to notification handling, and minor UI improvements. ### Pending State Management: * [`frontend/webapp/hooks/destinations/useDestinationCRUD.ts`](diffhunk://#diff-c7f19ca063b62568e37726473e1b8c265d3309def0d157772951023c66e055b8R61-R71): Added `addPendingItems` calls to manage pending states for create, update, and delete destination actions. * [`frontend/webapp/hooks/sources/useSourceCRUD.ts`](diffhunk://#diff-1be86445dc0ed6bfdc08f7525c5800ef39bdcd70318a3c130f83f36f1ae9f5c8R37-R52): Added `addPendingItems` calls to manage pending states for persisting and updating sources. ### Notification Handling: * [`frontend/webapp/hooks/actions/useActionCRUD.ts`](diffhunk://#diff-97e103ce27156651b4a989f874cbddcdc9a3c2583c0a14e36d036bdd4f66933eL14-R15): Updated to include `removeNotifications` in the notification store. * [`frontend/webapp/hooks/notification/useSSE.ts`](diffhunk://#diff-db9ebe0ce8cdabc0ede2f45de12661d55fb7aa50a5ecfc0787f5898c55b044d6L31-R39): Added `setPendingItems` to clear pending states upon successful SSE connection. ### UI Improvements: * [`frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts`](diffhunk://#diff-aa30a6b7893a9d1393f196b9846b1fdc19956c7c07f4099570cb25ff2f77fa43L76-R76): Fixed a typo in the node title from 'ADD DESTIONATION' to 'ADD DESTINATION'. * [`frontend/webapp/containers/main/overview/overview-drawer/index.tsx`](diffhunk://#diff-732721edf7c32d883ffefa52d6e43339c6ee328c88c3cb5f0d87f6760347b599R106-R123): Added `useMemo` for pending state checks and integrated notification handling for pending actions like edit and delete. [[1]](diffhunk://#diff-732721edf7c32d883ffefa52d6e43339c6ee328c88c3cb5f0d87f6760347b599R106-R123) [[2]](diffhunk://#diff-732721edf7c32d883ffefa52d6e43339c6ee328c88c3cb5f0d87f6760347b599L115-R137) These changes enhance the application's user experience by providing better feedback and handling for pending operations and notifications. --------- Co-authored-by: Mike Dame Co-authored-by: Ron Federman --- .../build-destination-nodes.ts | 4 +- .../overview-data-flow/build-source-nodes.ts | 2 +- .../main/overview/overview-drawer/index.tsx | 30 ++++++-- .../webapp/hooks/actions/useActionCRUD.ts | 3 +- .../hooks/destinations/useDestinationCRUD.ts | 9 ++- .../useInstrumentationRuleCRUD.ts | 3 +- frontend/webapp/hooks/notification/useSSE.ts | 9 ++- .../hooks/overview/useNodeDataFlowHandlers.ts | 13 ++-- .../webapp/hooks/sources/useSourceCRUD.ts | 68 +++++++++---------- .../nodes-data-flow/nodes/add-node.tsx | 51 +++++++++----- .../nodes-data-flow/nodes/base-node.tsx | 22 +++--- .../nodes-data-flow/nodes/header-node.tsx | 21 +++--- .../nodes-data-flow/nodes/scroll-node.tsx | 7 +- frontend/webapp/store/index.ts | 1 + frontend/webapp/store/usePendingStore.ts | 59 ++++++++++++++++ frontend/webapp/types/common.ts | 2 +- frontend/webapp/utils/constants/string.tsx | 14 ++-- 17 files changed, 212 insertions(+), 106 deletions(-) create mode 100644 frontend/webapp/store/usePendingStore.ts diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts b/frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts index 4b1b448b6..148ab7e73 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts @@ -71,9 +71,9 @@ export const buildDestinationNodes = ({ loading, entities, positions, unfiltered }, data: { nodeWidth, - type: OVERVIEW_NODE_TYPES.ADD_DESTIONATION, + type: OVERVIEW_NODE_TYPES.ADD_DESTINATION, status: STATUSES.HEALTHY, - title: 'ADD DESTIONATION', + title: 'ADD DESTINATION', subTitle: `Add ${!!unfilteredCount ? 'a new' : 'first'} destination to monitor the OpenTelemetry data`, }, }); diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts b/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts index 8bb12e1e1..3695d7d7b 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts @@ -30,7 +30,7 @@ const mapToNodeData = (entity: Params['entities'][0]) => { type: OVERVIEW_ENTITY_TYPES.SOURCE, status: getHealthStatus(entity), title: getEntityLabel(entity, OVERVIEW_ENTITY_TYPES.SOURCE, { extended: true }), - subTitle: entity.kind, + subTitle: `${entity.namespace} • ${entity.kind}`, iconSrc: getProgrammingLanguageIcon(getMainContainerLanguage(entity)), raw: entity, }; diff --git a/frontend/webapp/containers/main/overview/overview-drawer/index.tsx b/frontend/webapp/containers/main/overview/overview-drawer/index.tsx index 5b78d667e..e9986aafd 100644 --- a/frontend/webapp/containers/main/overview/overview-drawer/index.tsx +++ b/frontend/webapp/containers/main/overview/overview-drawer/index.tsx @@ -1,13 +1,13 @@ -import { PropsWithChildren, useRef, useState } from 'react'; +import { PropsWithChildren, useMemo, useRef, useState } from 'react'; import { SVG } from '@/assets'; import styled from 'styled-components'; -import { useDrawerStore } from '@/store'; import DrawerFooter from './drawer-footer'; import { Drawer } from '@/reuseable-components'; -import { OVERVIEW_ENTITY_TYPES } from '@/types'; import DrawerHeader, { DrawerHeaderRef } from './drawer-header'; import { CancelWarning, DeleteWarning } from '@/components/modals'; import { useDestinationCRUD, useKeyDown, useSourceCRUD } from '@/hooks'; +import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, WorkloadId } from '@/types'; +import { useDrawerStore, useNotificationStore, usePendingStore } from '@/store'; const DRAWER_WIDTH = `${640 + 64}px`; // +64 because of "ContentArea" padding @@ -37,6 +37,8 @@ const ContentArea = styled.div` `; const OverviewDrawer: React.FC = ({ children, title, titleTooltip, icon, iconSrc, isEdit = false, isFormDirty = false, onEdit, onSave, onDelete, onCancel }) => { + const { isThisPending } = usePendingStore(); + const { addNotification } = useNotificationStore(); const { selectedItem, setSelectedItem } = useDrawerStore(); useKeyDown({ key: 'Enter', active: !!selectedItem }, () => (isEdit ? clickSave() : closeDrawer())); @@ -101,6 +103,24 @@ const OverviewDrawer: React.FC = ({ children, title, return isLast; }; + const isPending = useMemo(() => { + if (!selectedItem?.type) return false; + + return isThisPending({ + entityType: selectedItem.type as OVERVIEW_ENTITY_TYPES, + entityId: selectedItem.id, + }); + }, [selectedItem]); + + const handlePending = (action: string) => { + addNotification({ + type: NOTIFICATION_TYPE.WARNING, + title: 'Pending', + message: `Cannot click ${action}, ${selectedItem?.type} is pending`, + hideFromHistory: true, + }); + }; + return ( <> @@ -112,9 +132,9 @@ const OverviewDrawer: React.FC = ({ children, title, icon={icon} iconSrc={iconSrc} isEdit={isEdit} - onEdit={onEdit ? () => onEdit(true) : undefined} + onEdit={isPending ? () => handlePending('edit') : onEdit ? () => onEdit(true) : undefined} onClose={isEdit ? clickCancel : closeDrawer} - onDelete={onEdit ? clickDelete : undefined} + onDelete={isPending ? () => handlePending(isSource ? 'uninstrument' : 'delete') : onEdit ? clickDelete : undefined} deleteLabel={isSource ? 'Uninstrument' : undefined} /> {children} diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index 9cadebd79..af326980e 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -11,9 +11,8 @@ interface UseActionCrudParams { } export const useActionCRUD = (params?: UseActionCrudParams) => { - const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { data, refetch } = useComputePlatform(); - const { addNotification } = useNotificationStore(); + const { addNotification, removeNotifications } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 93ea5b701..e94c9bc54 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -1,7 +1,7 @@ import { useMutation } from '@apollo/client'; -import { useNotificationStore } from '@/store'; import { ACTION, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; +import { useNotificationStore, usePendingStore } from '@/store'; import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type DestinationInput } from '@/types'; import { CREATE_DESTINATION, DELETE_DESTINATION, UPDATE_DESTINATION } from '@/graphql/mutations'; @@ -11,9 +11,9 @@ interface Params { } export const useDestinationCRUD = (params?: Params) => { - const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { data } = useComputePlatform(); - const { addNotification } = useNotificationStore(); + const { addPendingItems } = usePendingStore(); + const { addNotification, removeNotifications } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ @@ -58,14 +58,17 @@ export const useDestinationCRUD = (params?: Params) => { createDestination: (destination: DestinationInput) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Creating destination...', undefined, true); + addPendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.DESTINATION, entityId: undefined }]); createDestination({ variables: { destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, updateDestination: (id: string, destination: DestinationInput) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating destination...', undefined, true); + addPendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.DESTINATION, entityId: id }]); updateDestination({ variables: { id, destination: { ...destination, fields: destination.fields.filter(({ value }) => value !== undefined) } } }); }, deleteDestination: (id: string) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Deleting destination...', undefined, true); + addPendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.DESTINATION, entityId: id }]); deleteDestination({ variables: { id } }); }, }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 99b61bf17..2d317dd72 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -11,9 +11,8 @@ interface Params { } export const useInstrumentationRuleCRUD = (params?: Params) => { - const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { data, refetch } = useComputePlatform(); - const { addNotification } = useNotificationStore(); + const { addNotification, removeNotifications } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string, hideFromHistory?: boolean) => { addNotification({ diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts index 55e9e24be..463e89d9d 100644 --- a/frontend/webapp/hooks/notification/useSSE.ts +++ b/frontend/webapp/hooks/notification/useSSE.ts @@ -2,9 +2,10 @@ import { useEffect, useRef } from 'react'; import { API } from '@/utils'; import { NOTIFICATION_TYPE } from '@/types'; import { useComputePlatform } from '../compute-platform'; -import { type NotifyPayload, useConnectionStore, useNotificationStore } from '@/store'; +import { type NotifyPayload, useConnectionStore, useNotificationStore, usePendingStore } from '@/store'; export const useSSE = () => { + const { setPendingItems } = usePendingStore(); const { addNotification } = useNotificationStore(); const { setConnectionStore } = useConnectionStore(); const { refetch: refetchComputePlatform } = useComputePlatform(); @@ -28,10 +29,14 @@ export const useSSE = () => { target: data.target, }; - // Dispatch the notification to the store addNotification(notification); refetchComputePlatform(); + // This works for now, + // but in the future we might have to change this to "removePendingItems", + // and remove the specific pending items based on their entityType and entityId + setPendingItems([]); + // Reset retry count on successful connection retryCount.current = 0; }; diff --git a/frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts b/frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts index 821730559..59bd4ed80 100644 --- a/frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts +++ b/frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts @@ -1,13 +1,13 @@ import { useCallback } from 'react'; -import { type Node } from '@xyflow/react'; import { useSourceCRUD } from '../sources'; import { useActionCRUD } from '../actions'; import { useDestinationCRUD } from '../destinations'; import { useDrawerStore, useModalStore } from '@/store'; import { useInstrumentationRuleCRUD } from '../instrumentation-rules'; import { OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, WorkloadId } from '@/types'; +import { Node } from '@xyflow/react'; -export function useNodeDataFlowHandlers() { +export const useNodeDataFlowHandlers = () => { const { sources } = useSourceCRUD(); const { actions } = useActionCRUD(); const { destinations } = useDestinationCRUD(); @@ -24,7 +24,7 @@ export function useNodeDataFlowHandlers() { id: string | WorkloadId; type: OVERVIEW_ENTITY_TYPES | OVERVIEW_NODE_TYPES; }, - 'id' + 'any-node' >, ) => { const { @@ -40,8 +40,8 @@ export function useNodeDataFlowHandlers() { if (type === OVERVIEW_ENTITY_TYPES.SOURCE) { const { kind, name, namespace } = id as WorkloadId; - const selectedDrawerItem = entities['sources'].find((item) => item.kind === kind && item.name === name && item.namespace === namespace); + const selectedDrawerItem = entities['sources'].find((item) => item.kind === kind && item.name === name && item.namespace === namespace); if (!selectedDrawerItem) { console.warn('Selected item not found', { id, [`${type}sCount`]: entities[`${type}s`].length }); return; @@ -54,7 +54,6 @@ export function useNodeDataFlowHandlers() { }); } else if ([OVERVIEW_ENTITY_TYPES.RULE, OVERVIEW_ENTITY_TYPES.ACTION, OVERVIEW_ENTITY_TYPES.DESTINATION].includes(type as OVERVIEW_ENTITY_TYPES)) { const selectedDrawerItem = entities[`${type}s`].find((item) => id && [item.id, item.ruleId].includes(id)); - if (!selectedDrawerItem) { console.warn('Selected item not found', { id, [`${type}sCount`]: entities[`${type}s`].length }); return; @@ -71,7 +70,7 @@ export function useNodeDataFlowHandlers() { setCurrentModal(OVERVIEW_ENTITY_TYPES.SOURCE); } else if (type === OVERVIEW_NODE_TYPES.ADD_ACTION) { setCurrentModal(OVERVIEW_ENTITY_TYPES.ACTION); - } else if (type === OVERVIEW_NODE_TYPES.ADD_DESTIONATION) { + } else if (type === OVERVIEW_NODE_TYPES.ADD_DESTINATION) { setCurrentModal(OVERVIEW_ENTITY_TYPES.DESTINATION); } else { console.warn('Unhandled node click', object); @@ -83,4 +82,4 @@ export function useNodeDataFlowHandlers() { return { handleNodeClick, }; -} +}; diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 5744ea9db..61dcb8559 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -1,8 +1,8 @@ import { useMutation } from '@apollo/client'; import { ACTION, getSseTargetFromId } from '@/utils'; -import { useAppStore, useNotificationStore } from '@/store'; import { PERSIST_SOURCE, UPDATE_K8S_ACTUAL_SOURCE } from '@/graphql'; import { useComputePlatform, useNamespace } from '../compute-platform'; +import { type PendingItem, useAppStore, useNotificationStore, usePendingStore } from '@/store'; import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type PatchSourceRequestInput, type K8sActualSource, NOTIFICATION_TYPE } from '@/types'; interface Params { @@ -11,12 +11,11 @@ interface Params { } export const useSourceCRUD = (params?: Params) => { - const removeNotifications = useNotificationStore((store) => store.removeNotifications); - const { configuredSources, setConfiguredSources } = useAppStore(); - const { data } = useComputePlatform(); const { persistNamespace } = useNamespace(); - const { addNotification } = useNotificationStore(); + const { addPendingItems } = usePendingStore(); + const { setConfiguredSources } = useAppStore(); + const { addNotification, removeNotifications } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { addNotification({ @@ -35,21 +34,22 @@ export const useSourceCRUD = (params?: Params) => { }; const handleComplete = (actionType: string) => { + setConfiguredSources({}); params?.onSuccess?.(actionType); }; const [createOrDeleteSources, cdState] = useMutation<{ persistK8sSources: boolean }>(PERSIST_SOURCE, { - onError: (error, req) => handleError('', error.message), + onError: (error) => handleError('', error.message), onCompleted: (res, req) => { + const namespace = req?.variables?.namespace; const count = req?.variables?.sources.length; - if (count === 1) { - const namespace = req?.variables?.namespace; - const { name, kind, selected } = req?.variables?.sources?.[0] || {}; - const id = { namespace, name, kind }; + req?.variables?.sources.forEach(({ name, kind, selected }) => { + if (!selected) removeNotifications(getSseTargetFromId({ namespace, name, kind }, OVERVIEW_ENTITY_TYPES.SOURCE)); + }); - if (!selected) removeNotifications(getSseTargetFromId(id, OVERVIEW_ENTITY_TYPES.SOURCE)); - if (!selected) setConfiguredSources({ ...configuredSources, [namespace]: configuredSources[namespace]?.filter((source) => source.name !== name) || [] }); + if (count === 1) { + const { selected } = req?.variables?.sources?.[0] || {}; handleComplete(selected ? ACTION.CREATE : ACTION.DELETE); } else { handleComplete(''); @@ -59,41 +59,37 @@ export const useSourceCRUD = (params?: Params) => { const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: (res, req) => handleComplete(ACTION.UPDATE), + onCompleted: () => handleComplete(ACTION.UPDATE), }); - const persistNamespaces = async (items: { [key: string]: boolean }) => { - for (const [namespace, futureSelected] of Object.entries(items)) { - await persistNamespace({ name: namespace, futureSelected }); - } - }; - - const persistSources = async (items: { [key: string]: K8sActualSource[] }) => { - for (const [namespace, sources] of Object.entries(items)) { - await createOrDeleteSources({ - variables: { - namespace, - sources: sources.map((source) => ({ - kind: source.kind, - name: source.name, - selected: source.selected, - })), - }, - }); - } - }; - return { loading: cdState.loading || uState.loading, sources: data?.computePlatform.k8sActualSources || [], persistSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Persisting sources...', undefined, true); - await persistNamespaces(futureSelectAppsList); - await persistSources(selectAppsList); + + for (const [namespace, sources] of Object.entries(selectAppsList)) { + const addToPendingStore: PendingItem[] = []; + const sendToGql: { name: string; kind: string; selected: boolean }[] = []; + + sources.forEach(({ name, kind, selected }) => { + addToPendingStore.push({ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: { namespace, name, kind } }); + sendToGql.push({ name, kind, selected }); + }); + + addPendingItems(addToPendingStore); + await createOrDeleteSources({ variables: { namespace, sources: sendToGql } }); + } + + for (const [namespace, futureSelected] of Object.entries(futureSelectAppsList)) { + await persistNamespace({ name: namespace, futureSelected }); + } }, + updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating sources...', undefined, true); + addPendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: sourceId }]); await updateSource({ variables: { sourceId, patchSourceRequest } }); }, }; diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/add-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/add-node.tsx index 81c57be84..64f139d25 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/add-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/add-node.tsx @@ -1,9 +1,11 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import { PlusIcon } from '@/assets'; import styled from 'styled-components'; -import { Text } from '@/reuseable-components'; -import { NODE_TYPES, OVERVIEW_NODE_TYPES, STATUSES } from '@/types'; +import { usePendingStore } from '@/store'; +import { FlexColumn, FlexRow } from '@/styles'; +import { FadeLoader, Text } from '@/reuseable-components'; import { Handle, type Node, type NodeProps, Position } from '@xyflow/react'; +import { NODE_TYPES, OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES } from '@/types'; interface Props extends NodeProps< @@ -20,14 +22,13 @@ interface Props > > {} -const Container = styled.div<{ $nodeWidth: Props['data']['nodeWidth'] }>` +const Container = styled(FlexColumn)<{ $nodeWidth: Props['data']['nodeWidth'] }>` + min-height: 69px; // negative width applied here because of the padding left&right width: ${({ $nodeWidth }) => `${$nodeWidth - 40}px`}; padding: 16px 24px 16px 16px; - display: flex; - flex-direction: column; - justify-content: center; align-items: center; + justify-content: center; gap: 4px; align-self: stretch; cursor: pointer; @@ -40,17 +41,13 @@ const Container = styled.div<{ $nodeWidth: Props['data']['nodeWidth'] }>` } `; -const TitleWrapper = styled.div` - display: flex; +const TitleWrapper = styled(FlexRow)` gap: 4px; - align-items: center; `; const Title = styled(Text)` font-size: 14px; font-weight: 600; - font-family: ${({ theme }) => theme.font_family.secondary}; - text-decoration-line: underline; `; const SubTitle = styled(Text)` @@ -59,16 +56,34 @@ const SubTitle = styled(Text)` text-align: center; `; -const AddNode: React.FC = ({ data }) => { +const AddNode: React.FC = ({ id: nodeId, data }) => { const { nodeWidth, title, subTitle } = data; + const { isThisPending } = usePendingStore(); + const entity = nodeId.split('-')[0] as OVERVIEW_ENTITY_TYPES; + const isPending = isThisPending({ entityType: entity }); + return ( - - - {title} - - {subTitle} + {isPending ? ( + + + + adding {entity}s + + Just a few more seconds... + + ) : ( + + + + + {title} + + + {subTitle} + + )} diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx index 318102bc4..cfb27aaaf 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useAppStore } from '@/store'; import styled from 'styled-components'; import { ErrorTriangleIcon, SVG } from '@/assets'; -import { Checkbox, DataTab } from '@/reuseable-components'; +import { useAppStore, usePendingStore } from '@/store'; +import { Checkbox, DataTab, FadeLoader } from '@/reuseable-components'; import { Handle, type Node, type NodeProps, Position } from '@xyflow/react'; -import { type ActionDataParsed, type ActualDestination, type InstrumentationRuleSpec, type K8sActualSource, NODE_TYPES, NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, STATUSES, WorkloadId } from '@/types'; +import { type ActionDataParsed, type ActualDestination, type InstrumentationRuleSpec, type K8sActualSource, NODE_TYPES, OVERVIEW_ENTITY_TYPES, STATUSES, WorkloadId } from '@/types'; interface Props extends NodeProps< @@ -31,10 +31,12 @@ const Container = styled.div<{ $nodeWidth: Props['data']['nodeWidth'] }>` `; const BaseNode: React.FC = ({ id: nodeId, data }) => { - const { nodeWidth, type, status, title, subTitle, icon, iconSrc, monitors, isActive, raw } = data; + const { nodeWidth, id: entityId, type: entityType, status, title, subTitle, icon, iconSrc, monitors, isActive, raw } = data; const isError = status === STATUSES.UNHEALTHY; - const { configuredSources, setConfiguredSources } = useAppStore((state) => state); + const { configuredSources, setConfiguredSources } = useAppStore(); + const { isThisPending } = usePendingStore(); + const isPending = isThisPending({ entityType, entityId }); const renderActions = () => { const getSourceLocation = () => { @@ -60,13 +62,9 @@ const BaseNode: React.FC = ({ id: nodeId, data }) => { return ( <> - {/* TODO: handle instrumentation rules for sources */} - {isError ? ( - - ) : // : type === 'source' && SOME_INDICATOR_THAT_THIS_IS_INSTRUMENTED ? ( ) - null} - - {type === 'source' ? : null} + {/* TODO: handle action/icon to apply instrumentation-rules for individual sources (@Notion GEN-1650) */} + {isPending ? : isError ? : null} + {entityType === 'source' ? : null} ); }; diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/header-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/header-node.tsx index 34f74ad92..5a9b1c1b4 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/header-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/header-node.tsx @@ -1,10 +1,9 @@ import React, { useMemo } from 'react'; -import Image from 'next/image'; -import { NODE_TYPES } from '@/types'; -import { useAppStore } from '@/store'; import styled from 'styled-components'; import { useSourceCRUD } from '@/hooks'; import type { Node, NodeProps } from '@xyflow/react'; +import { useAppStore, usePendingStore } from '@/store'; +import { NODE_TYPES, OVERVIEW_ENTITY_TYPES } from '@/types'; import { Badge, Checkbox, Text } from '@/reuseable-components'; interface Props @@ -42,7 +41,8 @@ const HeaderNode: React.FC = ({ data }) => { const { nodeWidth, title, icon: Icon, tagValue } = data; const isSources = title === 'Sources'; - const { configuredSources, setConfiguredSources } = useAppStore((state) => state); + const { configuredSources, setConfiguredSources } = useAppStore(); + const { isThisPending } = usePendingStore(); const { sources } = useSourceCRUD(); const totalSelectedSources = useMemo(() => { @@ -63,10 +63,15 @@ const HeaderNode: React.FC = ({ data }) => { const payload = {}; sources.forEach((source) => { - if (!payload[source.namespace]) { - payload[source.namespace] = [source]; - } else { - payload[source.namespace].push(source); + const id = { namespace: source.namespace, name: source.name, kind: source.kind }; + const isPending = isThisPending({ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: id }); + + if (!isPending) { + if (!payload[source.namespace]) { + payload[source.namespace] = [source]; + } else { + payload[source.namespace].push(source); + } } }); diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/scroll-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/scroll-node.tsx index fe46069f4..1d21add70 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/scroll-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/scroll-node.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react'; import { SVG } from '@/assets'; import BaseNode from './base-node'; import styled from 'styled-components'; -import { useDrawerStore } from '@/store'; +import { useNodeDataFlowHandlers } from '@/hooks'; import { type Node, type NodeProps } from '@xyflow/react'; import { type K8sActualSource, NODE_TYPES, OVERVIEW_ENTITY_TYPES, STATUSES, type WorkloadId } from '@/types'; @@ -50,7 +50,7 @@ const BaseNodeWrapper = styled.div<{ $framePadding: number }>` const ScrollNode: React.FC = ({ data, ...rest }) => { const { nodeWidth, nodeHeight, items, onScroll } = data; - const { setSelectedItem } = useDrawerStore(); + const { handleNodeClick } = useNodeDataFlowHandlers(); const containerRef = useRef(null); useEffect(() => { @@ -86,7 +86,8 @@ const ScrollNode: React.FC = ({ data, ...rest }) => { $framePadding={item.data.framePadding} onClick={(e) => { e.stopPropagation(); - setSelectedItem({ id: item.data.id, type: item.data.type, item: item.data.raw }); + // @ts-ignore + handleNodeClick(e, item); }} > diff --git a/frontend/webapp/store/index.ts b/frontend/webapp/store/index.ts index 017c41823..19c00c77b 100644 --- a/frontend/webapp/store/index.ts +++ b/frontend/webapp/store/index.ts @@ -3,3 +3,4 @@ export * from './useConnectionStore'; export * from './useDrawerStore'; export * from './useModalStore'; export * from './useNotificationStore'; +export * from './usePendingStore'; diff --git a/frontend/webapp/store/usePendingStore.ts b/frontend/webapp/store/usePendingStore.ts new file mode 100644 index 000000000..34302f36d --- /dev/null +++ b/frontend/webapp/store/usePendingStore.ts @@ -0,0 +1,59 @@ +import { create } from 'zustand'; +import { OVERVIEW_ENTITY_TYPES, WorkloadId } from '@/types'; + +export interface PendingItem { + entityType: OVERVIEW_ENTITY_TYPES; + entityId?: string | WorkloadId; +} + +interface StoreState { + pendingItems: PendingItem[]; + setPendingItems: (arr: PendingItem[]) => void; + addPendingItems: (arr: PendingItem[]) => void; + removePendingItems: (arr: PendingItem[]) => void; + isThisPending: (item: PendingItem) => boolean; +} + +const itemsAreEqual = (item1: PendingItem, item2: PendingItem) => { + const entityTypesEqual = item1.entityType === item2.entityType; + const idsEqual = + typeof item1.entityId === 'string' && typeof item2.entityId === 'string' + ? item1.entityId === item2.entityId + : typeof item1.entityId === 'object' && typeof item2.entityId === 'object' + ? item1.entityId.namespace === item2.entityId.namespace && item1.entityId.name === item2.entityId.name && item1.entityId.kind === item2.entityId.kind + : !item1.entityId && !item2.entityId; + + return entityTypesEqual && idsEqual; +}; + +export const usePendingStore = create((set, get) => ({ + pendingItems: [], + setPendingItems: (arr) => set({ pendingItems: arr }), + addPendingItems: (arr) => set((state) => ({ pendingItems: state.pendingItems.concat(arr.filter((addItem) => !state.pendingItems.some((existingItem) => itemsAreEqual(existingItem, addItem)))) })), + removePendingItems: (arr) => set((state) => ({ pendingItems: state.pendingItems.filter((existingItem) => !arr.find((removeItem) => itemsAreEqual(existingItem, removeItem))) })), + + isThisPending: (item) => { + const { pendingItems } = get(); + let bool = false; + + for (let i = 0; i < pendingItems.length; i++) { + const pendingItem = pendingItems[i]; + if ( + pendingItem.entityType === item.entityType && + (!item.entityId || + (pendingItem.entityType === OVERVIEW_ENTITY_TYPES.SOURCE + ? !!pendingItem.entityId && + !!item.entityId && + (pendingItem.entityId as WorkloadId).namespace === (item.entityId as WorkloadId).namespace && + (pendingItem.entityId as WorkloadId).name === (item.entityId as WorkloadId).name && + (pendingItem.entityId as WorkloadId).kind === (item.entityId as WorkloadId).kind + : pendingItem.entityId === item.entityId)) + ) { + bool = true; + break; + } + } + + return bool; + }, +})); diff --git a/frontend/webapp/types/common.ts b/frontend/webapp/types/common.ts index c0254c292..f1c404b3c 100644 --- a/frontend/webapp/types/common.ts +++ b/frontend/webapp/types/common.ts @@ -62,7 +62,7 @@ export enum OVERVIEW_NODE_TYPES { ADD_RULE = 'addRule', ADD_SOURCE = 'addSource', ADD_ACTION = 'addAction', - ADD_DESTIONATION = 'addDestination', + ADD_DESTINATION = 'addDestination', } export enum STATUSES { diff --git a/frontend/webapp/utils/constants/string.tsx b/frontend/webapp/utils/constants/string.tsx index b0326a27b..927f79e09 100644 --- a/frontend/webapp/utils/constants/string.tsx +++ b/frontend/webapp/utils/constants/string.tsx @@ -15,6 +15,12 @@ export const INPUT_TYPES = { CHECKBOX: 'checkbox', }; +export enum CRUD { + CREATE = 'Create', + UPDATE = 'Update', + DELETE = 'Delete', +} + export const ACTION = { SAVE: 'Save', CONTACT_US: 'Contact Us', @@ -24,11 +30,11 @@ export const ACTION = { DISABLE: 'Disable', RUNNING: 'Running', APPLIED: 'Applied', - DELETE_ALL: 'Delete All', - CREATE: 'Create', - UPDATE: 'Update', - DELETE: 'Delete', FETCH: 'Fetch', + CREATE: CRUD.CREATE, + UPDATE: CRUD.UPDATE, + DELETE: CRUD.DELETE, + DELETE_ALL: 'Delete All', }; export const FORM_ALERTS = { From 6d3d447af4adb24afa150c55ddc3831316ea29bc Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Tue, 31 Dec 2024 17:44:14 +0200 Subject: [PATCH 181/259] [GEN-2141]: remove instrumentation config modified-event batcher; update source-update logic (#2108) --- .../instrumentation_config_watcher.go | 32 ------------------- .../webapp/hooks/sources/useSourceCRUD.ts | 23 ++++++++++--- frontend/webapp/store/usePendingStore.ts | 11 +++++++ 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index ddb67b5b8..e3c0514ce 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -15,7 +15,6 @@ import ( ) var instrumentationConfigAddedEventBatcher *EventBatcher -var instrumentationConfigModifiedEventBatcher *EventBatcher var instrumentationConfigDeletedEventBatcher *EventBatcher func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error { @@ -34,21 +33,6 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er }, ) - instrumentationConfigModifiedEventBatcher = NewEventBatcher( - EventBatcherConfig{ - MinBatchSize: 1, - Duration: 10 * time.Second, - Event: sse.MessageEventModified, - CRDType: consts.InstrumentationConfig, - SuccessBatchMessageFunc: func(batchSize int, crd string) string { - return fmt.Sprintf("Successfully updated %d sources", batchSize) - }, - FailureBatchMessageFunc: func(batchSize int, crd string) string { - return fmt.Sprintf("Failed to update %d sources", batchSize) - }, - }, - ) - instrumentationConfigDeletedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, @@ -76,7 +60,6 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) { ch := watcher.ResultChan() defer instrumentationConfigAddedEventBatcher.Cancel() - defer instrumentationConfigModifiedEventBatcher.Cancel() defer instrumentationConfigDeletedEventBatcher.Cancel() for { select { @@ -90,8 +73,6 @@ func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.I switch event.Type { case watch.Added: handleAddedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) - case watch.Modified: - handleModifiedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) case watch.Deleted: handleDeletedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig)) } @@ -112,19 +93,6 @@ func handleAddedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConf instrumentationConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) } -func handleModifiedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { - namespace := instruConfig.Namespace - name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) - if err != nil { - genericErrorMessage(sse.MessageEventAdded, consts.InstrumentationConfig, err.Error()) - return - } - - target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind) - data := fmt.Sprintf(`Source "%s" updated`, name) - instrumentationConfigModifiedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target) -} - func handleDeletedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) { namespace := instruConfig.Namespace name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name) diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 61dcb8559..3f44a5286 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -11,10 +11,10 @@ interface Params { } export const useSourceCRUD = (params?: Params) => { - const { data } = useComputePlatform(); const { persistNamespace } = useNamespace(); - const { addPendingItems } = usePendingStore(); + const { data, refetch } = useComputePlatform(); const { setConfiguredSources } = useAppStore(); + const { addPendingItems, removePendingItems } = usePendingStore(); const { addNotification, removeNotifications } = useNotificationStore(); const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => { @@ -59,7 +59,22 @@ export const useSourceCRUD = (params?: Params) => { const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { onError: (error) => handleError(ACTION.UPDATE, error.message), - onCompleted: () => handleComplete(ACTION.UPDATE), + onCompleted: (res, req) => { + handleComplete(ACTION.UPDATE); + + // This is instead of using a k8s modified-event watcher... + // If we do use a watcher, we can't guarantee an SSE will be sent for this update alone. + // It will definitely include SSE for all updates, that can be instrument/uninstrument, conditions changed etc. + // Not that there's anything about a watcher that would break the UI, it's just that we would receive unexpected events with ridiculous amounts, + // (example: instrument 5 apps, update the name of 2, then uninstrument the other 3, we would get an SSE with minimum 10 updated sources, when we expect it to show only 2 due to name change). + setTimeout(() => { + const id = req?.variables?.sourceId; + + refetch(); + notifyUser(NOTIFICATION_TYPE.SUCCESS, ACTION.UPDATE, 'Successfully updated 1 source', id); + removePendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: id }]); + }, 2000); + }, }); return { @@ -88,7 +103,7 @@ export const useSourceCRUD = (params?: Params) => { }, updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { - notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating sources...', undefined, true); + notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating source...', undefined, true); addPendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: sourceId }]); await updateSource({ variables: { sourceId, patchSourceRequest } }); }, diff --git a/frontend/webapp/store/usePendingStore.ts b/frontend/webapp/store/usePendingStore.ts index 34302f36d..0bc4a7f21 100644 --- a/frontend/webapp/store/usePendingStore.ts +++ b/frontend/webapp/store/usePendingStore.ts @@ -1,6 +1,15 @@ import { create } from 'zustand'; import { OVERVIEW_ENTITY_TYPES, WorkloadId } from '@/types'; +// This store is used to keep track of pending items that are being created, updated, or deleted. +// This is used for entities that require an SSE event to be sent from the backend after a CRUD action. +// --- +// Imagine a user instruments a few sources, we want to show loading spinners, toasts etc. +// The CLI will finish processing the CRDs and send an SSE event to the frontend. +// The frontend will then remove the pending item from the store and update the UI by refetching the data. +// --- +// This can be used for non-SSE entities (like actions & rules), but it's not necessary as we refetch-instantly in those cases. + export interface PendingItem { entityType: OVERVIEW_ENTITY_TYPES; entityId?: string | WorkloadId; @@ -32,6 +41,8 @@ export const usePendingStore = create((set, get) => ({ addPendingItems: (arr) => set((state) => ({ pendingItems: state.pendingItems.concat(arr.filter((addItem) => !state.pendingItems.some((existingItem) => itemsAreEqual(existingItem, addItem)))) })), removePendingItems: (arr) => set((state) => ({ pendingItems: state.pendingItems.filter((existingItem) => !arr.find((removeItem) => itemsAreEqual(existingItem, removeItem))) })), + // Pass an item to check if it's in the pending items array. + // This is used to show loading spinners, toasts etc. isThisPending: (item) => { const { pendingItems } = get(); let bool = false; From 9725d7d2b70410fad72c0661bc7d453209a33180 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 09:24:34 +0200 Subject: [PATCH 182/259] chore: use labels and change mode to array --- .../crd/bases/odigos.io_destinations.yaml | 10 +- api/config/crd/bases/odigos.io_sources.yaml | 248 +++++++++--------- .../odigos/v1alpha1/sourceselector.go | 14 +- .../odigos/v1alpha1/sourcespec.go | 11 - api/odigos/v1alpha1/destination_types.go | 4 +- api/odigos/v1alpha1/source_types.go | 4 - api/odigos/v1alpha1/zz_generated.deepcopy.go | 12 +- autoscaler/controllers/common/processors.go | 116 ++++---- .../controllers/destination_controller.go | 96 +------ autoscaler/controllers/gateway/configmap.go | 4 +- autoscaler/controllers/gateway/root.go | 12 +- .../crds/odigos.io_destinations.yaml | 10 +- .../templates/crds/odigos.io_sources.yaml | 248 +++++++++--------- 13 files changed, 330 insertions(+), 459 deletions(-) diff --git a/api/config/crd/bases/odigos.io_destinations.yaml b/api/config/crd/bases/odigos.io_destinations.yaml index 892fe8e9c..7b6205e35 100644 --- a/api/config/crd/bases/odigos.io_destinations.yaml +++ b/api/config/crd/bases/odigos.io_destinations.yaml @@ -82,23 +82,23 @@ spec: items: type: string type: array - mode: + modes: description: |- - Mode can be "all", "namespaces", or "groups". + Mode can contain a combination of "all", "namespaces", or "groups". Determines how sources are selected for this destination. enum: - all - namespaces - groups - type: string + items: + type: string + type: array namespaces: description: Namespaces specifies the namespaces for "namespaces" mode. items: type: string type: array - required: - - mode type: object type: type: string diff --git a/api/config/crd/bases/odigos.io_sources.yaml b/api/config/crd/bases/odigos.io_sources.yaml index 48b67831f..c59f98ae2 100644 --- a/api/config/crd/bases/odigos.io_sources.yaml +++ b/api/config/crd/bases/odigos.io_sources.yaml @@ -14,139 +14,131 @@ spec: singular: source scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.workload.name - name: Workload - type: string - - jsonPath: .spec.workload.kind - name: Kind - type: string - - jsonPath: .spec.workload.namespace - name: Namespace - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Source configures an application for auto-instrumentation. - 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: - groups: - description: - Groups represents the logical group(s) this source belongs - to. - items: + - additionalPrinterColumns: + - jsonPath: .spec.workload.name + name: Workload + type: string + - jsonPath: .spec.workload.kind + name: Kind + type: string + - jsonPath: .spec.workload.namespace + name: Namespace + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Source configures an application for auto-instrumentation. + 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: + workload: + description: |- + Workload represents the workload or namespace to be instrumented. + This field is required upon creation and cannot be modified. + 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 - type: array - workload: - description: |- - Workload represents the workload or namespace to be instrumented. - This field is required upon creation and cannot be modified. + name: + type: string + namespace: + type: string + required: + - kind + - name + - namespace + type: object + required: + - workload + type: object + status: + properties: + conditions: + description: |- + Represents the observations of a source'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: - kind: + 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: |- - 1. the pascal case representation of the workload kind - it is used in k8s api objects as the `Kind` field. + 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 - name: + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown type: string - namespace: + 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: - - kind - - name - - namespace + - lastTransitionTime + - message + - reason + - status + - type type: object - required: - - workload - type: object - status: - properties: - conditions: - description: |- - Represents the observations of a source'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 - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourceselector.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourceselector.go index ab40e136d..f00752536 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourceselector.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourceselector.go @@ -20,7 +20,7 @@ package v1alpha1 // SourceSelectorApplyConfiguration represents a declarative configuration of the SourceSelector type for use // with apply. type SourceSelectorApplyConfiguration struct { - Mode *string `json:"mode,omitempty"` + Modes []string `json:"modes,omitempty"` Namespaces []string `json:"namespaces,omitempty"` Groups []string `json:"groups,omitempty"` } @@ -31,11 +31,13 @@ func SourceSelector() *SourceSelectorApplyConfiguration { return &SourceSelectorApplyConfiguration{} } -// WithMode sets the Mode 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 Mode field is set to the value of the last call. -func (b *SourceSelectorApplyConfiguration) WithMode(value string) *SourceSelectorApplyConfiguration { - b.Mode = &value +// WithModes adds the given value to the Modes 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 Modes field. +func (b *SourceSelectorApplyConfiguration) WithModes(values ...string) *SourceSelectorApplyConfiguration { + for i := range values { + b.Modes = append(b.Modes, values[i]) + } return b } diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go index 467f9a483..ae7774ba8 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go @@ -25,7 +25,6 @@ import ( // with apply. type SourceSpecApplyConfiguration struct { Workload *workload.PodWorkload `json:"workload,omitempty"` - Groups []string `json:"groups,omitempty"` } // SourceSpecApplyConfiguration constructs a declarative configuration of the SourceSpec type for use with @@ -41,13 +40,3 @@ func (b *SourceSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) b.Workload = &value return b } - -// WithGroups adds the given value to the Groups 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 Groups field. -func (b *SourceSpecApplyConfiguration) WithGroups(values ...string) *SourceSpecApplyConfiguration { - for i := range values { - b.Groups = append(b.Groups, values[i]) - } - return b -} diff --git a/api/odigos/v1alpha1/destination_types.go b/api/odigos/v1alpha1/destination_types.go index 2dfbd64b9..7eabe0557 100644 --- a/api/odigos/v1alpha1/destination_types.go +++ b/api/odigos/v1alpha1/destination_types.go @@ -39,10 +39,10 @@ type DestinationSpec struct { // SourceSelector defines the criteria for selecting sources. type SourceSelector struct { - // Mode can be "all", "namespaces", or "groups". + // Mode can contain a combination of "all", "namespaces", or "groups". // Determines how sources are selected for this destination. // +kubebuilder:validation:Enum=all;namespaces;groups - Mode string `json:"mode"` + Modes []string `json:"modes,omitempty"` // Namespaces specifies the namespaces for "namespaces" mode. // +optional diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index abb64b72c..ae4ab1799 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -47,10 +47,6 @@ type SourceSpec struct { // This field is required upon creation and cannot be modified. // +kubebuilder:validation:Required Workload workload.PodWorkload `json:"workload"` - - // Groups represents the logical group(s) this source belongs to. - // +optional - Groups []string `json:"groups,omitempty"` } type SourceStatus struct { diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index a1d6d747d..1889d66da 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -1296,7 +1296,7 @@ func (in *Source) DeepCopyInto(out *Source) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) + out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) } @@ -1353,6 +1353,11 @@ func (in *SourceList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SourceSelector) DeepCopyInto(out *SourceSelector) { *out = *in + if in.Modes != nil { + in, out := &in.Modes, &out.Modes + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Namespaces != nil { in, out := &in.Namespaces, &out.Namespaces *out = make([]string, len(*in)) @@ -1379,11 +1384,6 @@ func (in *SourceSelector) DeepCopy() *SourceSelector { func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { *out = *in out.Workload = in.Workload - if in.Groups != nil { - in, out := &in.Groups, &out.Groups - *out = make([]string, len(*in)) - copy(*out, *in) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. diff --git a/autoscaler/controllers/common/processors.go b/autoscaler/controllers/common/processors.go index 7a4481b5c..70f4d3ac0 100644 --- a/autoscaler/controllers/common/processors.go +++ b/autoscaler/controllers/common/processors.go @@ -1,14 +1,19 @@ package common import ( + "context" "encoding/json" "fmt" "sort" + "strings" + "github.com/go-logr/logr" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -79,29 +84,29 @@ type MatchCondition struct { Kind string `mapstructure:"kind"` } -func AddFilterProcessors(allProcessors *odigosv1.ProcessorList, dests *odigosv1.DestinationList, sources *odigosv1.SourceList) { +func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProcessors *odigosv1.ProcessorList, dests *odigosv1.DestinationList) { for _, dest := range dests.Items { - //TODO: remove this log logger := log.Log.WithValues("destination", dest.Name) logger.Info("Processing destination for filter processor") - //TODO: remove this log - matchedSources := filterSources(sources.Items, dest.Spec.SourceSelector) - // if len(matchedSources) == 0 { - // //TODO: remove this log - // logger.Info("No matching sources found for destination. Skipping processor creation.") + if dest.Spec.SourceSelector == nil || contains(dest.Spec.SourceSelector.Modes, "all") { + logger.Info("Skipping destination as SourceSelector is nil or set to 'all'") + continue + } + + var matchedSources []odigosv1.Source + if contains(dest.Spec.SourceSelector.Modes, "namespaces") { + matchedSources = append(matchedSources, fetchSourcesByNamespaces(ctx, kubeClient, dest.Spec.SourceSelector.Namespaces, logger)...) + } + if contains(dest.Spec.SourceSelector.Modes, "groups") { + matchedSources = append(matchedSources, fetchSourcesByGroups(ctx, kubeClient, dest.Spec.SourceSelector.Groups, logger)...) + } - // //TODO: remove this log - // continue - // } - //TODO: remove this log logger.Info("Matched sources for destination", "matchedSources", matchedSources) - //TODO: remove this log + var matchConditions []map[string]string for _, source := range matchedSources { - //TODO: remove this log logger.Info("Adding match condition for source", "sourceName", source.Spec.Workload.Name, "namespace", source.Spec.Workload.Namespace, "kind", source.Spec.Workload.Kind) - //TODO: remove this log matchCondition := map[string]string{ "name": source.Spec.Workload.Name, @@ -111,6 +116,11 @@ func AddFilterProcessors(allProcessors *odigosv1.ProcessorList, dests *odigosv1. matchConditions = append(matchConditions, matchCondition) } + if len(matchConditions) == 0 { + logger.Info("No matched sources for destination. Skipping processor creation.") + continue + } + filterConfig := map[string]interface{}{ "match_conditions": matchConditions, } @@ -129,61 +139,42 @@ func AddFilterProcessors(allProcessors *odigosv1.ProcessorList, dests *odigosv1. Signals: dest.Spec.Signals, }, }) - //TODO: remove this log - logger.Info("Filter processor added successfully", "processorName", fmt.Sprintf("odigossourcetodestinationfilter-%s", dest.Name)) - //TODO: remove this log - - } -} -func filterSources(sources []odigosv1.Source, selector *odigosv1.SourceSelector) []odigosv1.Source { - if selector == nil || selector.Mode == "all" { - return sources - } - - var filtered []odigosv1.Source - for _, source := range sources { - if selector.Mode == "namespaces" && contains(selector.Namespaces, source.Spec.Workload.Namespace) { - filtered = append(filtered, source) - } else if selector.Mode == "groups" { - // Handle nil or empty groups gracefully - if source.Spec.Groups == nil || len(source.Spec.Groups) == 0 { - continue - } - if containsAny(selector.Groups, source.Spec.Groups) { - filtered = append(filtered, source) - } - } } - return filtered } -func contains(arr []string, val string) bool { - for _, item := range arr { - if item == val { - return true +func fetchSourcesByNamespaces(ctx context.Context, kubeClient client.Client, namespaces []string, logger logr.Logger) []odigosv1.Source { + var sources []odigosv1.Source + for _, ns := range namespaces { + sourceList := &odigosv1.SourceList{} + err := kubeClient.List(ctx, sourceList, &client.ListOptions{Namespace: ns}) + if err != nil { + logger.Error(err, "Failed to fetch sources by namespace", "namespace", ns) + continue } + sources = append(sources, sourceList.Items...) } - return false + return sources } -func containsAny(arr1, arr2 []string) bool { - for _, item1 := range arr1 { - for _, item2 := range arr2 { - if item1 == item2 { - return true - } - } +func fetchSourcesByGroups(ctx context.Context, kubeClient client.Client, groups []string, logger logr.Logger) []odigosv1.Source { + selectors := make([]string, len(groups)) + for i, group := range groups { + selectors[i] = fmt.Sprintf("odigos.io/group-%s=true", group) } - return false -} + labelSelector := labels.SelectorFromSet(labels.Set{ + strings.Join(selectors, ","): "", + }) -func buildFilterConditions(sources []odigosv1.Source) []string { - var conditions []string - for _, source := range sources { - conditions = append(conditions, source.Name) + sourceList := &odigosv1.SourceList{} + err := kubeClient.List(ctx, sourceList, &client.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + logger.Error(err, "Failed to fetch sources by groups", "groups", groups) + return nil } - return conditions + return sourceList.Items } func marshalConfig(config map[string]interface{}) []byte { @@ -193,3 +184,12 @@ func marshalConfig(config map[string]interface{}) []byte { } return data } + +func contains(arr []string, val string) bool { + for _, item := range arr { + if item == val { + return true + } + } + return false +} diff --git a/autoscaler/controllers/destination_controller.go b/autoscaler/controllers/destination_controller.go index d7ca38fd9..c0459e022 100644 --- a/autoscaler/controllers/destination_controller.go +++ b/autoscaler/controllers/destination_controller.go @@ -42,30 +42,7 @@ func (r *DestinationReconciler) Reconcile(ctx context.Context, req ctrl.Request) logger := log.FromContext(ctx) logger.V(0).Info("Reconciling Destination") - var destination v1.Destination - if err := r.Client.Get(ctx, req.NamespacedName, &destination); err != nil { - logger.Error(err, "Failed to get Destination") - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - var sources v1.SourceList - if err := r.Client.List(ctx, &sources); err != nil { - logger.Error(err, "Failed to list Sources") - return ctrl.Result{}, err - } - logger.V(1).Info("Sources", "sources", sources.Items) - logger.V(1).Info("Destination", "destination", destination) - logger.V(1).Info("SourceSelector", "sourceSelector", destination.Spec.SourceSelector) - filteredSources := filterSources(sources.Items, destination.Spec.SourceSelector) - logger.V(1).Info("Filtered Sources", "filteredSources", filteredSources) - // Generate route configuration - err := generateRouteConfig(ctx, r.Client, destination, filteredSources) - if err != nil { - logger.Error(err, "Failed to generate route configuration") - return ctrl.Result{}, err - } - - err = gateway.Sync(ctx, r.Client, r.Scheme, r.ImagePullSecrets, r.OdigosVersion, r.Config) + err := gateway.Sync(ctx, r.Client, r.Scheme, r.ImagePullSecrets, r.OdigosVersion, r.Config) if err != nil { return ctrl.Result{}, err } @@ -82,74 +59,3 @@ func (r *DestinationReconciler) SetupWithManager(mgr ctrl.Manager) error { WithEventFilter(&predicate.GenerationChangedPredicate{}). Complete(r) } - -func generateRouteConfig(ctx context.Context, client client.Client, destination v1.Destination, sources []v1.Source) error { - // Build route configuration based on filtered sources and destination signals - routeConfig := buildRouteConfig(destination, sources) - - // Apply the route configuration to the OpenTelemetry collector - // This could involve updating a ConfigMap or another custom resource - // that the collector watches - configMapName := "otelcol-route-config" - configMapNamespace := "odigos-system" // Replace with your namespace - err := updateConfigMap(ctx, client, configMapName, configMapNamespace, routeConfig) - if err != nil { - return err - } - - return nil -} - -func buildRouteConfig(destination v1.Destination, sources []v1.Source) map[string]interface{} { - // Example structure of the route configuration - routeConfig := map[string]interface{}{ - "destination": destination.Spec.DestinationName, - "signals": destination.Spec.Signals, - "sources": []string{}, - } - - for _, source := range sources { - routeConfig["sources"] = append(routeConfig["sources"].([]string), source.Name) - } - - return routeConfig -} - -func updateConfigMap(ctx context.Context, client client.Client, name, namespace string, data map[string]interface{}) error { - - // Update the ConfigMap with the new data - // This could involve creating a new ConfigMap or updating an existing one - // based on the name and namespace provided - return nil -} - -func filterSources(sources []v1.Source, selector *v1.SourceSelector) []v1.Source { - if selector == nil || selector.Mode == "all" { - // Return all sources if selector is nil or mode is "all" - return sources - } - - var filtered []v1.Source - for _, source := range sources { - switch selector.Mode { - case "namespaces": - for _, ns := range selector.Namespaces { - if source.Spec.Workload.Namespace == ns { - filtered = append(filtered, source) - break - } - } - case "groups": - for _, group := range selector.Groups { - for _, srcGroup := range source.Spec.Groups { - if group == srcGroup { - filtered = append(filtered, source) - break - } - } - } - } - } - - return filtered -} diff --git a/autoscaler/controllers/gateway/configmap.go b/autoscaler/controllers/gateway/configmap.go index c600e06e2..43936026f 100644 --- a/autoscaler/controllers/gateway/configmap.go +++ b/autoscaler/controllers/gateway/configmap.go @@ -111,10 +111,10 @@ func addSelfTelemetryPipeline(c *config.Config, ownTelemetryPort int32) error { return nil } -func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.ProcessorList, gateway *odigosv1.CollectorsGroup, ctx context.Context, c client.Client, scheme *runtime.Scheme, sources *odigosv1.SourceList) ([]odigoscommon.ObservabilitySignal, error) { +func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.ProcessorList, gateway *odigosv1.CollectorsGroup, ctx context.Context, c client.Client, scheme *runtime.Scheme) ([]odigoscommon.ObservabilitySignal, error) { logger := log.FromContext(ctx) memoryLimiterConfiguration := common.GetMemoryLimiterConfig(gateway.Spec.ResourcesSettings) - common.AddFilterProcessors(allProcessors, dests, sources) + common.AddFilterProcessors(ctx, c, allProcessors, dests) processors := common.FilterAndSortProcessorsByOrderHint(allProcessors, odigosv1.CollectorsGroupRoleClusterGateway) desiredData, err, status, signals := config.Calculate( diff --git a/autoscaler/controllers/gateway/root.go b/autoscaler/controllers/gateway/root.go index a41bd685a..1869e35fe 100644 --- a/autoscaler/controllers/gateway/root.go +++ b/autoscaler/controllers/gateway/root.go @@ -39,12 +39,6 @@ func Sync(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, return err } - var sources odigosv1.SourceList - if err := k8sClient.List(ctx, &sources); err != nil { - logger.Error(err, "Failed to list sources") - return err - } - var processors odigosv1.ProcessorList if err := k8sClient.List(ctx, &processors); err != nil { logger.Error(err, "Failed to list processors") @@ -53,7 +47,7 @@ func Sync(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, // Add the generic batch processor to the list of processors processors.Items = append(processors.Items, commonconf.GetGenericBatchProcessor()) - err = syncGateway(&dests, &processors, &gatewayCollectorGroup, ctx, k8sClient, scheme, imagePullSecrets, odigosVersion, config, &sources) + err = syncGateway(&dests, &processors, &gatewayCollectorGroup, ctx, k8sClient, scheme, imagePullSecrets, odigosVersion, config) statusPatchString := commonconf.GetCollectorsGroupDeployedConditionsPatch(err) statusErr := k8sClient.Status().Patch(ctx, &gatewayCollectorGroup, client.RawPatch(types.MergePatchType, []byte(statusPatchString))) if statusErr != nil { @@ -66,11 +60,11 @@ func Sync(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, func syncGateway(dests *odigosv1.DestinationList, processors *odigosv1.ProcessorList, gateway *odigosv1.CollectorsGroup, ctx context.Context, c client.Client, scheme *runtime.Scheme, imagePullSecrets []string, odigosVersion string, - config *controllerconfig.ControllerConfig, sources *odigosv1.SourceList) error { + config *controllerconfig.ControllerConfig) error { logger := log.FromContext(ctx) logger.V(0).Info("Syncing gateway") - signals, err := syncConfigMap(dests, processors, gateway, ctx, c, scheme, sources) + signals, err := syncConfigMap(dests, processors, gateway, ctx, c, scheme) if err != nil { logger.Error(err, "Failed to sync config map") return err diff --git a/helm/odigos/templates/crds/odigos.io_destinations.yaml b/helm/odigos/templates/crds/odigos.io_destinations.yaml index 892fe8e9c..7b6205e35 100644 --- a/helm/odigos/templates/crds/odigos.io_destinations.yaml +++ b/helm/odigos/templates/crds/odigos.io_destinations.yaml @@ -82,23 +82,23 @@ spec: items: type: string type: array - mode: + modes: description: |- - Mode can be "all", "namespaces", or "groups". + Mode can contain a combination of "all", "namespaces", or "groups". Determines how sources are selected for this destination. enum: - all - namespaces - groups - type: string + items: + type: string + type: array namespaces: description: Namespaces specifies the namespaces for "namespaces" mode. items: type: string type: array - required: - - mode type: object type: type: string diff --git a/helm/odigos/templates/crds/odigos.io_sources.yaml b/helm/odigos/templates/crds/odigos.io_sources.yaml index 48b67831f..c59f98ae2 100644 --- a/helm/odigos/templates/crds/odigos.io_sources.yaml +++ b/helm/odigos/templates/crds/odigos.io_sources.yaml @@ -14,139 +14,131 @@ spec: singular: source scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.workload.name - name: Workload - type: string - - jsonPath: .spec.workload.kind - name: Kind - type: string - - jsonPath: .spec.workload.namespace - name: Namespace - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Source configures an application for auto-instrumentation. - 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: - groups: - description: - Groups represents the logical group(s) this source belongs - to. - items: + - additionalPrinterColumns: + - jsonPath: .spec.workload.name + name: Workload + type: string + - jsonPath: .spec.workload.kind + name: Kind + type: string + - jsonPath: .spec.workload.namespace + name: Namespace + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Source configures an application for auto-instrumentation. + 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: + workload: + description: |- + Workload represents the workload or namespace to be instrumented. + This field is required upon creation and cannot be modified. + 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 - type: array - workload: - description: |- - Workload represents the workload or namespace to be instrumented. - This field is required upon creation and cannot be modified. + name: + type: string + namespace: + type: string + required: + - kind + - name + - namespace + type: object + required: + - workload + type: object + status: + properties: + conditions: + description: |- + Represents the observations of a source'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: - kind: + 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: |- - 1. the pascal case representation of the workload kind - it is used in k8s api objects as the `Kind` field. + 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 - name: + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown type: string - namespace: + 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: - - kind - - name - - namespace + - lastTransitionTime + - message + - reason + - status + - type type: object - required: - - workload - type: object - status: - properties: - conditions: - description: |- - Represents the observations of a source'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 - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} From a91f4aad9f2cc253bdfb1f0614e03bdcab387ff4 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 09:45:44 +0200 Subject: [PATCH 183/259] chore: change processor name --- collector/builder-config.yaml | 4 ++-- collector/odigosotelcol/components.go | 6 +++--- collector/odigosotelcol/go.mod | 4 ++-- .../Makefile | 0 .../README.md | 0 .../config.go | 2 +- .../factory.go | 4 ++-- .../generated_component_test.go | 4 ++-- .../generated_package_test.go | 2 +- .../go.mod | 2 +- .../go.sum | 0 .../internal/metadata/generated_status.go | 2 +- .../metadata.yaml | 4 ++-- .../processor.go | 2 +- 14 files changed, 18 insertions(+), 18 deletions(-) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/Makefile (100%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/README.md (100%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/config.go (94%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/factory.go (94%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/generated_component_test.go (97%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/generated_package_test.go (75%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/go.mod (97%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/go.sum (100%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/internal/metadata/generated_status.go (79%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/metadata.yaml (71%) rename collector/processors/{odigossourcetodestinationfilterprocessor => odigosroutingfilterprocessor}/processor.go (98%) diff --git a/collector/builder-config.yaml b/collector/builder-config.yaml index c213ec28a..2b0572145 100644 --- a/collector/builder-config.yaml +++ b/collector/builder-config.yaml @@ -85,7 +85,7 @@ processors: - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor v0.106.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/remotetapprocessor v0.106.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/odigos/processor/odigostrafficmetrics v0.106.0 - - gomod: github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor v0.106.0 + - gomod: github.com/odigos-io/odigos/processor/odigosroutingfilterprocessor v0.106.0 receivers: - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.106.0 @@ -121,4 +121,4 @@ replaces: - github.com/open-telemetry/opentelemetry-collector-contrib/odigos/exporter/mockdestinationexporter => ../exporters/mockdestinationexporter - github.com/open-telemetry/opentelemetry-collector-contrib/odigos/processor/odigostrafficmetrics => ../processors/odigostrafficmetrics - go.opentelemetry.io/collector/odigos/providers/odigosfileprovider => ../providers/odigosfileprovider - - github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor => ../processors/odigossourcetodestinationfilterprocessor + - github.com/odigos-io/odigos/processor/odigosroutingfilterprocessor => ../processors/odigosroutingfilterprocessor diff --git a/collector/odigosotelcol/components.go b/collector/odigosotelcol/components.go index 693e6c342..4d3f1ddf3 100644 --- a/collector/odigosotelcol/components.go +++ b/collector/odigosotelcol/components.go @@ -91,7 +91,7 @@ import ( transformprocessor "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor" remotetapprocessor "github.com/open-telemetry/opentelemetry-collector-contrib/processor/remotetapprocessor" odigostrafficmetrics "github.com/open-telemetry/opentelemetry-collector-contrib/odigos/processor/odigostrafficmetrics" - odigossourcetodestinationfilterprocessor "github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor" + odigosroutingfilterprocessor "github.com/odigos-io/odigos/processor/odigosroutingfilterprocessor" otlpreceiver "go.opentelemetry.io/collector/receiver/otlpreceiver" zipkinreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver" filelogreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver" @@ -257,7 +257,7 @@ func components() (otelcol.Factories, error) { transformprocessor.NewFactory(), remotetapprocessor.NewFactory(), odigostrafficmetrics.NewFactory(), - odigossourcetodestinationfilterprocessor.NewFactory(), + odigosroutingfilterprocessor.NewFactory(), ) if err != nil { return otelcol.Factories{}, err @@ -289,7 +289,7 @@ func components() (otelcol.Factories, error) { factories.ProcessorModules[transformprocessor.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor v0.106.0" factories.ProcessorModules[remotetapprocessor.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/processor/remotetapprocessor v0.106.0" factories.ProcessorModules[odigostrafficmetrics.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/odigos/processor/odigostrafficmetrics v0.106.0" - factories.ProcessorModules[odigossourcetodestinationfilterprocessor.NewFactory().Type()] = "github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor v0.106.0" + factories.ProcessorModules[odigosroutingfilterprocessor.NewFactory().Type()] = "github.com/odigos-io/odigos/processor/odigosroutingfilterprocessor v0.106.0" factories.Connectors, err = connector.MakeFactoryMap( forwardconnector.NewFactory(), diff --git a/collector/odigosotelcol/go.mod b/collector/odigosotelcol/go.mod index 6c078a947..a4d4cfe27 100644 --- a/collector/odigosotelcol/go.mod +++ b/collector/odigosotelcol/go.mod @@ -7,7 +7,7 @@ go 1.23.0 toolchain go1.23.4 require ( - github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor v0.106.0 + github.com/odigos-io/odigos/processor/odigosroutingfilterprocessor v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/datadogconnector v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector v0.106.0 @@ -608,6 +608,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/odigos/process replace go.opentelemetry.io/collector/odigos/providers/odigosfileprovider => ../providers/odigosfileprovider -replace github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor => ../processors/odigossourcetodestinationfilterprocessor +replace github.com/odigos-io/odigos/processor/odigosroutingfilterprocessor => ../processors/odigosroutingfilterprocessor exclude github.com/knadh/koanf v1.5.0 diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/Makefile b/collector/processors/odigosroutingfilterprocessor/Makefile similarity index 100% rename from collector/processors/odigossourcetodestinationfilterprocessor/Makefile rename to collector/processors/odigosroutingfilterprocessor/Makefile diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/README.md b/collector/processors/odigosroutingfilterprocessor/README.md similarity index 100% rename from collector/processors/odigossourcetodestinationfilterprocessor/README.md rename to collector/processors/odigosroutingfilterprocessor/README.md diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/config.go b/collector/processors/odigosroutingfilterprocessor/config.go similarity index 94% rename from collector/processors/odigossourcetodestinationfilterprocessor/config.go rename to collector/processors/odigosroutingfilterprocessor/config.go index 374fed2f1..1dc797423 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/config.go +++ b/collector/processors/odigosroutingfilterprocessor/config.go @@ -1,4 +1,4 @@ -package odigossourcetodestinationfilterprocessor +package odigosroutingfilterprocessor import ( "errors" diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/factory.go b/collector/processors/odigosroutingfilterprocessor/factory.go similarity index 94% rename from collector/processors/odigossourcetodestinationfilterprocessor/factory.go rename to collector/processors/odigosroutingfilterprocessor/factory.go index dc74e36bd..030d0ef05 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/factory.go +++ b/collector/processors/odigosroutingfilterprocessor/factory.go @@ -1,4 +1,4 @@ -package odigossourcetodestinationfilterprocessor +package odigosroutingfilterprocessor import ( "context" @@ -11,7 +11,7 @@ import ( func NewFactory() processor.Factory { return processor.NewFactory( - component.MustNewType("odigossourcetodestinationfilterprocessor"), + component.MustNewType("odigosroutingfilterprocessor"), createDefaultConfig, processor.WithTraces(createTracesProcessor, component.StabilityLevelBeta), processor.WithLogs(createLogsProcessor, component.StabilityLevelBeta), diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go b/collector/processors/odigosroutingfilterprocessor/generated_component_test.go similarity index 97% rename from collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go rename to collector/processors/odigosroutingfilterprocessor/generated_component_test.go index db5ca6438..66036d3d6 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/generated_component_test.go +++ b/collector/processors/odigosroutingfilterprocessor/generated_component_test.go @@ -1,6 +1,6 @@ // Code generated by mdatagen. DO NOT EDIT. -package odigossourcetodestinationfilterprocessor +package odigosroutingfilterprocessor import ( "context" @@ -21,7 +21,7 @@ import ( ) func TestComponentFactoryType(t *testing.T) { - require.Equal(t, "odigossourcetodestinationfilterprocessor", NewFactory().Type().String()) + require.Equal(t, "odigosroutingfilterprocessor", NewFactory().Type().String()) } func TestComponentConfigStruct(t *testing.T) { diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/generated_package_test.go b/collector/processors/odigosroutingfilterprocessor/generated_package_test.go similarity index 75% rename from collector/processors/odigossourcetodestinationfilterprocessor/generated_package_test.go rename to collector/processors/odigosroutingfilterprocessor/generated_package_test.go index 9a82c579b..78388deeb 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/generated_package_test.go +++ b/collector/processors/odigosroutingfilterprocessor/generated_package_test.go @@ -1,6 +1,6 @@ // Code generated by mdatagen. DO NOT EDIT. -package odigossourcetodestinationfilterprocessor +package odigosroutingfilterprocessor import ( "go.uber.org/goleak" diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/go.mod b/collector/processors/odigosroutingfilterprocessor/go.mod similarity index 97% rename from collector/processors/odigossourcetodestinationfilterprocessor/go.mod rename to collector/processors/odigosroutingfilterprocessor/go.mod index f33882c3f..62a648f23 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/go.mod +++ b/collector/processors/odigosroutingfilterprocessor/go.mod @@ -1,4 +1,4 @@ -module github.com/odigos-io/odigos/processor/odigossourcetodestinationfilterprocessor +module github.com/odigos-io/odigos/processor/odigosroutingfilterprocessor go 1.23.0 diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/go.sum b/collector/processors/odigosroutingfilterprocessor/go.sum similarity index 100% rename from collector/processors/odigossourcetodestinationfilterprocessor/go.sum rename to collector/processors/odigosroutingfilterprocessor/go.sum diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go b/collector/processors/odigosroutingfilterprocessor/internal/metadata/generated_status.go similarity index 79% rename from collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go rename to collector/processors/odigosroutingfilterprocessor/internal/metadata/generated_status.go index 2cb2f390e..689cf4d3e 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/internal/metadata/generated_status.go +++ b/collector/processors/odigosroutingfilterprocessor/internal/metadata/generated_status.go @@ -7,7 +7,7 @@ import ( ) var ( - Type = component.MustNewType("odigossourcetodestinationfilterprocessor") + Type = component.MustNewType("odigosroutingfilterprocessor") ) const ( diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml b/collector/processors/odigosroutingfilterprocessor/metadata.yaml similarity index 71% rename from collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml rename to collector/processors/odigosroutingfilterprocessor/metadata.yaml index 280f01fd6..465876066 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/metadata.yaml +++ b/collector/processors/odigosroutingfilterprocessor/metadata.yaml @@ -1,4 +1,4 @@ -type: odigossourcetodestinationfilterprocessor +type: odigosroutingfilterprocessor status: class: processor @@ -11,4 +11,4 @@ status: - contrib codeowners: active: - - along + - alon diff --git a/collector/processors/odigossourcetodestinationfilterprocessor/processor.go b/collector/processors/odigosroutingfilterprocessor/processor.go similarity index 98% rename from collector/processors/odigossourcetodestinationfilterprocessor/processor.go rename to collector/processors/odigosroutingfilterprocessor/processor.go index 404098051..148facfad 100644 --- a/collector/processors/odigossourcetodestinationfilterprocessor/processor.go +++ b/collector/processors/odigosroutingfilterprocessor/processor.go @@ -1,4 +1,4 @@ -package odigossourcetodestinationfilterprocessor +package odigosroutingfilterprocessor import ( "context" From 76bd45abadad797f60eb0d12da839abf172990f4 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 09:47:11 +0200 Subject: [PATCH 184/259] chore: use new processor name --- autoscaler/controllers/common/processors.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoscaler/controllers/common/processors.go b/autoscaler/controllers/common/processors.go index 70f4d3ac0..38ad7a452 100644 --- a/autoscaler/controllers/common/processors.go +++ b/autoscaler/controllers/common/processors.go @@ -127,10 +127,10 @@ func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProce allProcessors.Items = append(allProcessors.Items, odigosv1.Processor{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("odigossourcetodestinationfilter-%s", dest.Name), + Name: fmt.Sprintf("odigosroutingfilterprocessor-%s", dest.Name), }, Spec: odigosv1.ProcessorSpec{ - Type: "odigossourcetodestinationfilterprocessor", + Type: "odigosroutingfilterprocessor", ProcessorConfig: runtime.RawExtension{Raw: marshalConfig(filterConfig)}, CollectorRoles: []odigosv1.CollectorsGroupRole{ odigosv1.CollectorsGroupRoleClusterGateway, From ba64e37f410027220e266766d154b1f8f85862f5 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 10:04:04 +0200 Subject: [PATCH 185/259] chore: match logs and metrics --- .../odigosroutingfilterprocessor/processor.go | 69 ++++++------------- 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/collector/processors/odigosroutingfilterprocessor/processor.go b/collector/processors/odigosroutingfilterprocessor/processor.go index 148facfad..13526dea7 100644 --- a/collector/processors/odigosroutingfilterprocessor/processor.go +++ b/collector/processors/odigosroutingfilterprocessor/processor.go @@ -20,6 +20,7 @@ func (fp *filterProcessor) processTraces(ctx context.Context, td ptrace.Traces) for i := 0; i < rspans.Len(); i++ { resourceSpan := rspans.At(i) + resourceAttributes := resourceSpan.Resource().Attributes() ilSpans := resourceSpan.ScopeSpans() for j := 0; j < ilSpans.Len(); j++ { @@ -27,7 +28,8 @@ func (fp *filterProcessor) processTraces(ctx context.Context, td ptrace.Traces) spans := scopeSpan.Spans() spans.RemoveIf(func(span ptrace.Span) bool { - return !fp.matches(span, resourceSpan) + namespace, name, kind := extractResourceDetails(resourceAttributes) + return !fp.matches(name, namespace, kind) }) } } @@ -48,7 +50,8 @@ func (fp *filterProcessor) processMetrics(ctx context.Context, md pmetric.Metric metrics := scopeMetric.Metrics() metrics.RemoveIf(func(metric pmetric.Metric) bool { - return !fp.metricMatches(metric, resourceAttributes) + namespace, name, kind := extractResourceDetails(resourceAttributes) + return !fp.matches(name, namespace, kind) }) } } @@ -56,22 +59,6 @@ func (fp *filterProcessor) processMetrics(ctx context.Context, md pmetric.Metric return md, nil } -func (fp *filterProcessor) metricMatches(metric pmetric.Metric, resourceAttributes pcommon.Map) bool { - for _, condition := range fp.config.MatchConditions { - name, _ := resourceAttributes.Get("name") - namespace, _ := resourceAttributes.Get("namespace") - kind, _ := resourceAttributes.Get("kind") - - if name.AsString() == condition.Name && - namespace.AsString() == condition.Namespace && - kind.AsString() == condition.Kind { - return true - } - } - - return false -} - func (fp *filterProcessor) processLogs(ctx context.Context, ld plog.Logs) (plog.Logs, error) { rLogs := ld.ResourceLogs() @@ -85,7 +72,8 @@ func (fp *filterProcessor) processLogs(ctx context.Context, ld plog.Logs) (plog. logRecords := scopeLog.LogRecords() logRecords.RemoveIf(func(log plog.LogRecord) bool { - return !fp.logMatches(log.Attributes(), resourceAttributes) + namespace, name, kind := extractResourceDetails(resourceAttributes) + return !fp.matches(name, namespace, kind) }) } } @@ -93,16 +81,15 @@ func (fp *filterProcessor) processLogs(ctx context.Context, ld plog.Logs) (plog. return ld, nil } -func (fp *filterProcessor) logMatches(logAttributes, resourceAttributes pcommon.Map) bool { - - name, _ := resourceAttributes.Get("name") - namespace, _ := resourceAttributes.Get("namespace") - kind, _ := resourceAttributes.Get("kind") +func (fp *filterProcessor) matches(name, namespace, kind string) bool { + if name == "" || namespace == "" || kind == "" { + return false + } for _, condition := range fp.config.MatchConditions { - if name.AsString() == condition.Name && - namespace.AsString() == condition.Namespace && - kind.AsString() == condition.Kind { + if name == condition.Name && + namespace == condition.Namespace && + kind == condition.Kind { return true } } @@ -110,40 +97,28 @@ func (fp *filterProcessor) logMatches(logAttributes, resourceAttributes pcommon. return false } -func (fp *filterProcessor) matches(span ptrace.Span, resourceSpan ptrace.ResourceSpans) bool { - - attributes := resourceSpan.Resource().Attributes() - - namespace := getAttribute(attributes, "k8s.namespace.name") +func extractResourceDetails(attributes pcommon.Map) (namespace, name, kind string) { + namespace = getAttribute(attributes, "k8s.namespace.name") if namespace == "" { - return false + return "", "", "" } - name, kind := getDynamicNameAndKind(attributes) + name, kind = getDynamicNameAndKind(attributes) if name == "" || kind == "" { - return false - } - - for _, condition := range fp.config.MatchConditions { - if name == condition.Name && - namespace == condition.Namespace && - kind == condition.Kind { - return true - } + return "", "", "" } - return false + return namespace, name, kind } func getDynamicNameAndKind(attributes pcommon.Map) (name string, kind string) { - resourceTypes := []struct { kind string key string }{ {"Deployment", "k8s.deployment.name"}, - {"statefulSet", "k8s.statefulset.name"}, - {"daemonSet", "k8s.daemonset.name"}, + {"StatefulSet", "k8s.statefulset.name"}, + {"DaemonSet", "k8s.daemonset.name"}, } for _, resourceType := range resourceTypes { From e4f3a8358272726a7c600d45f88dacf8496b56e0 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 10:56:55 +0200 Subject: [PATCH 186/259] chore: gen-2144 performance fixes --- autoscaler/controllers/common/processors.go | 10 +++---- .../odigosroutingfilterprocessor/config.go | 27 ++++++++----------- .../odigosroutingfilterprocessor/processor.go | 12 +++------ 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/autoscaler/controllers/common/processors.go b/autoscaler/controllers/common/processors.go index 38ad7a452..3861d111e 100644 --- a/autoscaler/controllers/common/processors.go +++ b/autoscaler/controllers/common/processors.go @@ -104,16 +104,12 @@ func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProce logger.Info("Matched sources for destination", "matchedSources", matchedSources) - var matchConditions []map[string]string + matchConditions := make(map[string]bool) for _, source := range matchedSources { logger.Info("Adding match condition for source", "sourceName", source.Spec.Workload.Name, "namespace", source.Spec.Workload.Namespace, "kind", source.Spec.Workload.Kind) - matchCondition := map[string]string{ - "name": source.Spec.Workload.Name, - "namespace": source.Spec.Workload.Namespace, - "kind": string(source.Spec.Workload.Kind), - } - matchConditions = append(matchConditions, matchCondition) + key := fmt.Sprintf("%s/%s/%s", source.Spec.Workload.Namespace, source.Spec.Workload.Name, source.Spec.Workload.Kind) + matchConditions[key] = true } if len(matchConditions) == 0 { diff --git a/collector/processors/odigosroutingfilterprocessor/config.go b/collector/processors/odigosroutingfilterprocessor/config.go index 1dc797423..4c0d7e177 100644 --- a/collector/processors/odigosroutingfilterprocessor/config.go +++ b/collector/processors/odigosroutingfilterprocessor/config.go @@ -2,12 +2,14 @@ package odigosroutingfilterprocessor import ( "errors" + "fmt" + "strings" "go.opentelemetry.io/collector/component" ) type Config struct { - MatchConditions []MatchCondition `mapstructure:"match_conditions"` + MatchConditions map[string]bool `mapstructure:"match_conditions"` } var _ component.Config = (*Config)(nil) @@ -16,23 +18,16 @@ func (cfg *Config) Validate() error { if len(cfg.MatchConditions) == 0 { return errors.New("at least one match condition must be specified") } - for _, condition := range cfg.MatchConditions { - if err := condition.Validate(); err != nil { - return err + + for key := range cfg.MatchConditions { + parts := strings.Split(key, "/") + if len(parts) != 3 { + return fmt.Errorf("invalid match condition key format: %s (expected 'namespace/name/kind')", key) + } + if parts[0] == "" || parts[1] == "" || parts[2] == "" { + return fmt.Errorf("invalid match condition key: %s (namespace, name, and kind must be non-empty)", key) } } - return nil -} -type MatchCondition struct { - Name string `mapstructure:"name"` - Namespace string `mapstructure:"namespace"` - Kind string `mapstructure:"kind"` -} - -func (mc *MatchCondition) Validate() error { - if mc.Name == "" || mc.Namespace == "" || mc.Kind == "" { - return errors.New("all match condition fields (name, namespace, kind) must be specified") - } return nil } diff --git a/collector/processors/odigosroutingfilterprocessor/processor.go b/collector/processors/odigosroutingfilterprocessor/processor.go index 13526dea7..653ac0322 100644 --- a/collector/processors/odigosroutingfilterprocessor/processor.go +++ b/collector/processors/odigosroutingfilterprocessor/processor.go @@ -2,6 +2,7 @@ package odigosroutingfilterprocessor import ( "context" + "fmt" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" @@ -86,15 +87,8 @@ func (fp *filterProcessor) matches(name, namespace, kind string) bool { return false } - for _, condition := range fp.config.MatchConditions { - if name == condition.Name && - namespace == condition.Namespace && - kind == condition.Kind { - return true - } - } - - return false + key := fmt.Sprintf("%s/%s/%s", namespace, name, kind) + return fp.config.MatchConditions[key] } func extractResourceDetails(attributes pcommon.Map) (namespace, name, kind string) { From 7024a7f8eb5a16145a32ff19b6e2ca928e3abec1 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 10:58:04 +0200 Subject: [PATCH 187/259] chore: type fix --- collector/processors/odigosroutingfilterprocessor/factory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collector/processors/odigosroutingfilterprocessor/factory.go b/collector/processors/odigosroutingfilterprocessor/factory.go index 030d0ef05..f6329d803 100644 --- a/collector/processors/odigosroutingfilterprocessor/factory.go +++ b/collector/processors/odigosroutingfilterprocessor/factory.go @@ -21,7 +21,7 @@ func NewFactory() processor.Factory { func createDefaultConfig() component.Config { return &Config{ - MatchConditions: []MatchCondition{}, + MatchConditions: map[string]bool{}, } } From 8056f3db96eb55a8f32b2fe37ad93ea3002473bb Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 11:15:49 +0200 Subject: [PATCH 188/259] chore: add the source reconcailer --- autoscaler/controllers/source_controller.go | 56 +++++++++++++++++++++ autoscaler/main.go | 10 ++++ 2 files changed, 66 insertions(+) create mode 100644 autoscaler/controllers/source_controller.go diff --git a/autoscaler/controllers/source_controller.go b/autoscaler/controllers/source_controller.go new file mode 100644 index 000000000..1869f4774 --- /dev/null +++ b/autoscaler/controllers/source_controller.go @@ -0,0 +1,56 @@ +package controllers + +import ( + "context" + + controllerconfig "github.com/odigos-io/odigos/autoscaler/controllers/controller_config" + "github.com/odigos-io/odigos/autoscaler/controllers/gateway" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + v1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" +) + +type SourceReconciler struct { + client.Client + Scheme *runtime.Scheme + ImagePullSecrets []string + OdigosVersion string + Config *controllerconfig.ControllerConfig +} + +// Reconcile ensures that any changes to Source CRDs (creation, deletion, or label modifications) +// trigger a recalculation of the ConfigMap that configures the routing filter processors. +func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.V(0).Info("Reconciling Source") + + err := gateway.Sync(ctx, r.Client, r.Scheme, r.ImagePullSecrets, r.OdigosVersion, r.Config) + if err != nil { + logger.Error(err, "Failed to sync gateway configuration") + return ctrl.Result{}, err + } + + logger.V(1).Info("Successfully synced gateway configuration for Source", "Source", req.NamespacedName) + return ctrl.Result{}, nil +} + +func (r *SourceReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1.Source{}). + // Trigger reconciliation on create, delete, or label changes + WithEventFilter(predicate.Or( + predicate.GenerationChangedPredicate{}, + predicate.LabelChangedPredicate{}, + predicate.Funcs{ + DeleteFunc: func(e event.DeleteEvent) bool { + return true + }, + }, + )). + Complete(r) +} diff --git a/autoscaler/main.go b/autoscaler/main.go index 4757f758e..0e83a540e 100644 --- a/autoscaler/main.go +++ b/autoscaler/main.go @@ -287,6 +287,16 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "DaemonSet") os.Exit(1) } + if err = (&controllers.SourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + ImagePullSecrets: imagePullSecrets, + OdigosVersion: odigosVersion, + Config: config, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Source") + os.Exit(1) + } if err = actions.SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create odigos actions controllers") From d25ae079086d18519acaaea8ae6bbd3ad956a3fd Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 1 Jan 2025 14:59:26 +0200 Subject: [PATCH 189/259] [GEN-2150]: handle smart source-selection (exclude non-changed sources from API payload) (#2113) This pull request includes several changes to improve error handling, simplify TypeScript types, and update UI components in the frontend codebase. The most important changes include modifications to error handling in `services/sources.go`, simplification of TypeScript types and payloads, and updates to UI components for better user experience. Error Handling Improvements: * [`frontend/services/sources.go`](diffhunk://#diff-d8f62b6675961fc4e307e4fd622a59132ddc730b6e701ae1bc43dd1695f969a7L298-R298): Modified `createSourceCRD` and `deleteSourceCRD` functions to return errors instead of nil when a source is not found. [[1]](diffhunk://#diff-d8f62b6675961fc4e307e4fd622a59132ddc730b6e701ae1bc43dd1695f969a7L298-R298) [[2]](diffhunk://#diff-d8f62b6675961fc4e307e4fd622a59132ddc730b6e701ae1bc43dd1695f969a7L327-R326) TypeScript Type Simplifications: * [`frontend/webapp/containers/main/destinations/add-destination/index.tsx`](diffhunk://#diff-a5ff632e01d3e161b70dce2f959db69fd1893edb484af12b7849810487347001L61-R62): Simplified the payload handling by directly using `configuredSources` and `configuredFutureApps`. * [`frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx`](diffhunk://#diff-2de6982f9ae22619e89eda6e002fd17bb887b0625bbd4281c7a778c352642979L18-R30): Refactored to use `getApiPaylod` with forced types to satisfy TypeScript without breaking the onboarding flow. UI Component Updates: * [`frontend/webapp/containers/main/overview/multi-source-control/index.tsx`](diffhunk://#diff-cdd99a8fcd0484b24586f22b1f7b1bcf8d964bead53eeb0b2c07646c7301af70L53-R61): Simplified the payload structure and ensured the deselect function is called correctly. * [`frontend/webapp/containers/main/overview/overview-data-flow/build-action-nodes.ts`](diffhunk://#diff-2a2957e6035bd001e3c0c52f29f01f4473b6b99a8ce4381a08e9de504916c3b1L78-R78): Updated subtitles for better clarity. * [`frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx`](diffhunk://#diff-6b67649d370d208941f4e5a78c0c9de2f5b9b65fd49e0ede8ef57066982450aaL2-R4): Removed unused imports and refactored the component for better readability and performance. [[1]](diffhunk://#diff-6b67649d370d208941f4e5a78c0c9de2f5b9b65fd49e0ede8ef57066982450aaL2-R4) [[2]](diffhunk://#diff-6b67649d370d208941f4e5a78c0c9de2f5b9b65fd49e0ede8ef57066982450aaL69-L78) [[3]](diffhunk://#diff-6b67649d370d208941f4e5a78c0c9de2f5b9b65fd49e0ede8ef57066982450aaR82-R95) [[4]](diffhunk://#diff-6b67649d370d208941f4e5a78c0c9de2f5b9b65fd49e0ede8ef57066982450aaL117-R127) [[5]](diffhunk://#diff-6b67649d370d208941f4e5a78c0c9de2f5b9b65fd49e0ede8ef57066982450aaL155-R141) --------- Co-authored-by: Mike Dame Co-authored-by: Ron Federman --- frontend/services/sources.go | 6 +- .../destinations/add-destination/index.tsx | 12 +- .../overview/multi-source-control/index.tsx | 8 +- .../overview-data-flow/build-action-nodes.ts | 2 +- .../build-destination-nodes.ts | 2 +- .../overview-data-flow/build-rule-nodes.ts | 2 +- .../overview-data-flow/build-source-nodes.ts | 2 +- .../choose-source-modal/index.tsx | 20 +- .../sources-list/index.tsx | 54 ++--- .../source-controls/index.tsx | 8 +- .../sources-list/index.tsx | 9 +- .../main/sources/choose-sources/index.tsx | 17 +- .../hooks/compute-platform/useNamespace.ts | 6 +- .../webapp/hooks/sources/useSourceCRUD.ts | 4 +- .../webapp/hooks/sources/useSourceFormData.ts | 195 +++++++++--------- .../reuseable-components/badge/index.tsx | 2 +- .../reuseable-components/checkbox/index.tsx | 6 +- .../icon-button/index.tsx | 21 +- .../nodes-data-flow/nodes/add-node.tsx | 2 +- .../nodes-data-flow/nodes/base-node.tsx | 21 +- .../reuseable-components/toggle/index.tsx | 10 +- 21 files changed, 191 insertions(+), 218 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 4167548d2..ffe967d1f 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -295,8 +295,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if source != nil && err == nil { - // we don't want to throw error, because we want to allow the UI to send an array of all sources even if instrumented - return nil + return err } newSource := &v1alpha1.Source{ @@ -324,8 +323,7 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) if err != nil { - // we don't want to throw error, because we want to allow the UI to send an array of all sources even if not instrumented - return nil + return err } err = kube.DefaultClient.OdigosClient.Sources(nsName).Delete(ctx, source.Name, metav1.DeleteOptions{}) diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index 99b01179a..540a9029a 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -58,16 +58,8 @@ export function AddDestinationContainer() { const clickDone = async () => { setIsLoading(true); - const payload: typeof configuredSources = {}; - - Object.entries(configuredSources).forEach(([namespace, sources]) => { - payload[namespace] = sources.map((source) => ({ - ...source, - selected: true, - })); - }); - - await persistSources(payload, configuredFutureApps); + // configuredSources & configuredFutureApps are set in store from the previous step in onboarding flow + await persistSources(configuredSources, configuredFutureApps); await Promise.all(configuredDestinations.map(async ({ form }) => await createDestination(form))); resetState(); diff --git a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx index ebe13b893..a3a550475 100644 --- a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx +++ b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx @@ -50,15 +50,15 @@ export const MultiSourceControl = () => { }; const onDelete = () => { - const payload: typeof configuredSources = {}; + const payload = {}; - Object.entries(configuredSources).forEach(([namespace, selectedSources]) => { - payload[namespace] = selectedSources.map(({ selected, ...rest }) => ({ ...rest, selected: false })); + Object.entries(configuredSources).forEach(([namespace, sources]) => { + payload[namespace] = sources.map(({ name, kind }) => ({ name, kind, selected: false })); }); persistSources(payload, {}); - onDeselect(); setIsWarnModalOpen(false); + onDeselect(); }; return ( diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/build-action-nodes.ts b/frontend/webapp/containers/main/overview/overview-data-flow/build-action-nodes.ts index 59b8a82de..1a25e0c23 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/build-action-nodes.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/build-action-nodes.ts @@ -75,7 +75,7 @@ export const buildActionNodes = ({ loading, entities, positions, unfilteredCount type: OVERVIEW_NODE_TYPES.ADD_ACTION, status: STATUSES.HEALTHY, title: 'ADD ACTION', - subTitle: `Add ${!!unfilteredCount ? 'a new' : 'first'} action to modify the OpenTelemetry data`, + subTitle: 'To modify OpenTelemetry data', }, }); } else { diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts b/frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts index 148ab7e73..cdee55af6 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/build-destination-nodes.ts @@ -74,7 +74,7 @@ export const buildDestinationNodes = ({ loading, entities, positions, unfiltered type: OVERVIEW_NODE_TYPES.ADD_DESTINATION, status: STATUSES.HEALTHY, title: 'ADD DESTINATION', - subTitle: `Add ${!!unfilteredCount ? 'a new' : 'first'} destination to monitor the OpenTelemetry data`, + subTitle: 'To monitor OpenTelemetry data', }, }); } else { diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/build-rule-nodes.ts b/frontend/webapp/containers/main/overview/overview-data-flow/build-rule-nodes.ts index e5e147cd8..7a5fe8831 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/build-rule-nodes.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/build-rule-nodes.ts @@ -74,7 +74,7 @@ export const buildRuleNodes = ({ loading, entities, positions, unfilteredCounts type: OVERVIEW_NODE_TYPES.ADD_RULE, status: STATUSES.HEALTHY, title: 'ADD RULE', - subTitle: `Add ${!!unfilteredCount ? 'a new' : 'first'} rule to modify the OpenTelemetry data`, + subTitle: 'To modify OpenTelemetry data', }, }); } else { diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts b/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts index 3695d7d7b..03777a101 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts @@ -82,7 +82,7 @@ export const buildSourceNodes = ({ loading, entities, positions, unfilteredCount type: OVERVIEW_NODE_TYPES.ADD_SOURCE, status: STATUSES.HEALTHY, title: 'ADD SOURCE', - subTitle: `Add ${!!unfilteredCount ? 'a new' : 'first'} source to collect OpenTelemetry data`, + subTitle: 'To collect OpenTelemetry data', }, }); } else { diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx index d3c02e349..6072e15c4 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { type IAppState } from '@/store'; import { ChooseSourcesBody } from '../choose-sources-body'; import { Modal, NavigationButtons } from '@/reuseable-components'; import { useKeyDown, useSourceCRUD, useSourceFormData } from '@/hooks'; @@ -15,17 +16,18 @@ export const AddSourceModal: React.FC = ({ isOpen, onClose }) => { const { persistSources } = useSourceCRUD({ onSuccess: onClose }); const handleSubmit = async () => { - const { availableSources, selectedSources, selectedFutureApps } = menuState; - const payload: typeof availableSources = {}; + const { getApiPaylod, selectedFutureApps } = menuState; - Object.entries(availableSources).forEach(([namespace, sources]) => { - payload[namespace] = sources.map((source) => ({ - ...source, - selected: !!selectedSources[namespace].find(({ kind, name }) => kind === source.kind && name === source.name), - })); - }); + // Type of "getApiPaylod()" is actually: + // { [namespace: string]: Pick[] }; + // + // But we will force it as type: + // { [namespace: string]: K8sActualSource[] }; + // + // This forced type is to satisfy TypeScript, + // while knowing that this doesn't break the onboarding flow in any-way... - await persistSources(payload, selectedFutureApps); + await persistSources(getApiPaylod() as IAppState['configuredSources'], selectedFutureApps); }; return ( diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx index f38e35d4a..820715512 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx @@ -1,8 +1,7 @@ import React from 'react'; -import Image from 'next/image'; import theme from '@/styles/theme'; import styled from 'styled-components'; -import { UseSourceFormDataResponse } from '@/hooks'; +import { type UseSourceFormDataResponse } from '@/hooks'; import { Checkbox, Divider, ExtendIcon, FadeLoader, NoDataFound, Text, Toggle } from '@/reuseable-components'; interface Props extends UseSourceFormDataResponse { @@ -66,16 +65,6 @@ const SelectionCount = styled(Text)` width: 18px; `; -const ArrowIcon = styled(Image)` - &.open { - transform: rotate(180deg); - } - &.close { - transform: rotate(0deg); - } - transition: transform 0.3s; -`; - const NoDataFoundWrapper = styled.div` margin: 50px 0; display: flex; @@ -90,22 +79,20 @@ const NoDataFoundWrapper = styled.div` export const SourcesList: React.FC = ({ isModal = false, namespacesLoading, + filterNamespaces, + filterSources, selectedNamespace, onSelectNamespace, - availableSources, selectedSources, onSelectSource, selectedFutureApps, onSelectFutureApps, - searchText, selectAllForNamespace, onSelectAll, - - filterSources, }) => { - const namespaces = Object.entries(availableSources); + const namespaces = filterNamespaces(); if (!namespaces.length) { return {namespacesLoading ? : }; @@ -114,35 +101,30 @@ export const SourcesList: React.FC = ({ return ( {namespaces.map(([namespace, sources]) => { - const namespaceLoaded = !!selectedSources[namespace]; - - const available = availableSources[namespace] || []; - const selected = selectedSources[namespace] || []; - const futureApps = selectedFutureApps[namespace] || false; - - const namespacePassesFilters = !searchText || namespace.toLowerCase().includes(searchText); - if (!namespacePassesFilters) return null; - + const sourcesForNamespace = selectedSources[namespace] || []; + const futureAppsForNamespace = selectedFutureApps[namespace] || false; + const isNamespaceLoaded = !!sourcesForNamespace.length; const isNamespaceSelected = selectedNamespace === namespace && !selectAllForNamespace; - const isNamespaceCanSelect = namespaceLoaded && !!available.length; - const isNamespaceAllSourcesSelected = isNamespaceCanSelect && selected.length === sources.length; - const filtered = filterSources(namespace, { cancelSearch: true }); - const hasFilteredSources = !!filtered.length; + const onlySelectedSources = sourcesForNamespace.filter(({ selected }) => selected); + const filteredSources = filterSources(namespace, { cancelSearch: true }); + + const isNamespaceAllSourcesSelected = isNamespaceLoaded && onlySelectedSources.length === sources.length; + const hasFilteredSources = !!filteredSources.length; return ( onSelectNamespace(namespace)}> - onSelectAll(bool, namespace)} /> + onSelectAll(bool, namespace)} /> {namespace} - onSelectFutureApps(bool, namespace)} /> + onSelectFutureApps(bool, namespace)} /> - {namespaceLoaded ? `${selected.length}/${sources.length}` : null} + {isNamespaceLoaded ? `${onlySelectedSources.length}/${sources.length}` : null} @@ -152,11 +134,11 @@ export const SourcesList: React.FC = ({ (hasFilteredSources ? ( - + - {filtered.map((source) => { - const isSourceSelected = !!selected.find(({ name }) => name === source.name); + {filteredSources.map((source) => { + const isSourceSelected = !!onlySelectedSources.find(({ name }) => name === source.name); return ( onSelectSource(source)}> diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/source-controls/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/source-controls/index.tsx index 0dacbbb7c..2f441c50b 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/source-controls/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/source-controls/index.tsx @@ -26,17 +26,17 @@ export const SourceControls: React.FC = ({ selectedNamespace, onSelectNamespace, selectedSources, + onSelectAll, selectedFutureApps, onSelectFutureApps, searchText, setSearchText, - selectAll, - onSelectAll, showSelectedOnly, setShowSelectedOnly, }) => { - const selectedAppsCount = (selectedSources[selectedNamespace || ''] || []).length; + const sources = selectedSources[selectedNamespace] || []; + const selectedAppsCount = sources.filter(({ selected }) => selected).length; return ( <> @@ -61,7 +61,7 @@ export const SourceControls: React.FC = ({ - + diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx index 76f4a163a..7122b385e 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/sources-list/index.tsx @@ -77,22 +77,21 @@ export const SourcesList: React.FC = ({ namespacesLoading, selectedNamespace, - availableSources, selectedSources, onSelectSource, filterSources, }) => { - const sources = availableSources[selectedNamespace] || []; + const sources = selectedSources[selectedNamespace] || []; if (!sources.length) { - return {namespacesLoading ? : }; + return {namespacesLoading ? : }; } return ( {filterSources().map((source) => { - const isSelected = !!selectedSources[selectedNamespace].find(({ name }) => name === source.name); + const isSelected = selectedSources[selectedNamespace].find(({ name }) => name === source.name)?.selected || false; return ( onSelectSource(source)}> @@ -111,7 +110,7 @@ export const SourcesList: React.FC = ({ {isSelected && ( - + )} diff --git a/frontend/webapp/containers/main/sources/choose-sources/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/index.tsx index c500c72b8..447c923b9 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/index.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { ROUTES } from '@/utils'; import { ArrowIcon } from '@/assets'; -import { useAppStore } from '@/store'; import styled from 'styled-components'; import { SetupHeader } from '@/components'; import { useRouter } from 'next/navigation'; import { useSourceFormData } from '@/hooks'; +import { IAppState, useAppStore } from '@/store'; import { ChooseSourcesBody } from './choose-sources-body'; const HeaderWrapper = styled.div` @@ -18,11 +18,20 @@ export function ChooseSourcesContainer() { const menuState = useSourceFormData(); const onNext = () => { - const { availableSources, selectedSources, selectedFutureApps } = menuState; + const { recordedInitialValues, getApiPaylod, selectedFutureApps } = menuState; const { setAvailableSources, setConfiguredSources, setConfiguredFutureApps } = appState; - setAvailableSources(availableSources); - setConfiguredSources(selectedSources); + // Types of "recordedInitialValues" and "getApiPaylod()" are actually: + // { [namespace: string]: Pick[] }; + // + // But we will force them as type: + // { [namespace: string]: K8sActualSource[] }; + // + // This forced type is to satisfy TypeScript, + // while knowing that this doesn't break the onboarding flow in any-way... + + setAvailableSources(recordedInitialValues as IAppState['availableSources']); + setConfiguredSources(getApiPaylod() as IAppState['configuredSources']); setConfiguredFutureApps(selectedFutureApps); router.push(ROUTES.CHOOSE_DESTINATION); diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index 33d5a5c1d..68c7a5f0c 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -7,7 +7,7 @@ import { type ComputePlatform, NOTIFICATION_TYPE, type PersistNamespaceItemInput export const useNamespace = (namespaceName?: string) => { const { addNotification } = useNotificationStore(); - const cp = useComputePlatform(); + const { data: cpData, loading: cpLoading } = useComputePlatform(); const { data, loading } = useQuery(GET_NAMESPACES, { skip: !namespaceName, @@ -30,9 +30,9 @@ export const useNamespace = (namespaceName?: string) => { }); return { - loading, + loading: loading || cpLoading, data: data?.computePlatform.k8sActualNamespace, - allNamespaces: cp.data?.computePlatform.k8sActualNamespaces, + allNamespaces: cpData?.computePlatform.k8sActualNamespaces, persistNamespace: async (namespace: PersistNamespaceItemInput) => await persistNamespaceMutation({ variables: { namespace } }), }; }; diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 3f44a5286..1ffc5de54 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -3,7 +3,7 @@ import { ACTION, getSseTargetFromId } from '@/utils'; import { PERSIST_SOURCE, UPDATE_K8S_ACTUAL_SOURCE } from '@/graphql'; import { useComputePlatform, useNamespace } from '../compute-platform'; import { type PendingItem, useAppStore, useNotificationStore, usePendingStore } from '@/store'; -import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type PatchSourceRequestInput, type K8sActualSource, NOTIFICATION_TYPE } from '@/types'; +import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type PatchSourceRequestInput, NOTIFICATION_TYPE, type K8sActualSource } from '@/types'; interface Params { onSuccess?: (type: string) => void; @@ -86,7 +86,7 @@ export const useSourceCRUD = (params?: Params) => { for (const [namespace, sources] of Object.entries(selectAppsList)) { const addToPendingStore: PendingItem[] = []; - const sendToGql: { name: string; kind: string; selected: boolean }[] = []; + const sendToGql: Pick[] = []; sources.forEach(({ name, kind, selected }) => { addToPendingStore.push({ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: { namespace, name, kind } }); diff --git a/frontend/webapp/hooks/sources/useSourceFormData.ts b/frontend/webapp/hooks/sources/useSourceFormData.ts index 7f31a3ff9..428d1a9e4 100644 --- a/frontend/webapp/hooks/sources/useSourceFormData.ts +++ b/frontend/webapp/hooks/sources/useSourceFormData.ts @@ -4,14 +4,7 @@ import type { K8sActualSource } from '@/types'; import { useNamespace } from '../compute-platform'; type SelectedNamespace = string; - -export type SourcesByNamespace = { - [namespace: string]: K8sActualSource[]; -}; - -export type FutureAppsByNamespace = { - [namespace: string]: boolean; -}; +type SelectedSource = Pick; interface UseSourceFormDataParams { autoSelectNamespace?: boolean; @@ -19,24 +12,24 @@ interface UseSourceFormDataParams { export interface UseSourceFormDataResponse { namespacesLoading: boolean; + recordedInitialValues: { [namespace: SelectedNamespace]: SelectedSource[] }; + filterNamespaces: (options?: { cancelSearch?: boolean }) => [SelectedNamespace, SelectedSource[]][]; + filterSources: (namespace?: SelectedNamespace, options?: { cancelSearch?: boolean; cancelSelected?: boolean }) => SelectedSource[]; + getApiPaylod: () => { [namespace: SelectedNamespace]: SelectedSource[] }; selectedNamespace: SelectedNamespace; - availableSources: SourcesByNamespace; - selectedSources: SourcesByNamespace; - selectedFutureApps: FutureAppsByNamespace; onSelectNamespace: (namespace: SelectedNamespace) => void; - onSelectSource: (source: K8sActualSource, namespace?: SelectedNamespace) => void; + selectedSources: { [namespace: SelectedNamespace]: SelectedSource[] }; + onSelectSource: (source: SelectedSource, namespace?: SelectedNamespace) => void; + selectedFutureApps: { [namespace: SelectedNamespace]: boolean }; onSelectFutureApps: (bool: boolean, namespace?: SelectedNamespace) => void; searchText: string; - selectAll: boolean; - selectAllForNamespace: string; - showSelectedOnly: boolean; setSearchText: Dispatch>; - onSelectAll: (bool: boolean, namespace?: string, isFromInterval?: boolean) => void; + showSelectedOnly: boolean; setShowSelectedOnly: Dispatch>; - - filterSources: (namespace?: string, options?: { cancelSearch?: boolean; cancelSelected?: boolean }) => K8sActualSource[]; + selectAllForNamespace: SelectedNamespace; + onSelectAll: (bool: boolean, namespace?: SelectedNamespace, isFromInterval?: boolean) => void; } export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFormDataResponse => { @@ -44,23 +37,26 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo // only for "onboarding" - get unsaved values and set to state // (this is to persist the values when user navigates back to this page) - const appStore = useAppStore((state) => state); + const appStore = useAppStore(); - const [selectAll, setSelectAll] = useState(false); const [selectAllForNamespace, setSelectAllForNamespace] = useState(''); const [selectedNamespace, setSelectedNamespace] = useState(''); - const [availableSources, setAvailableSources] = useState(appStore.availableSources); - const [selectedSources, setSelectedSources] = useState(appStore.configuredSources); - const [selectedFutureApps, setSelectedFutureApps] = useState(appStore.configuredFutureApps); - const { allNamespaces, data: namespacesData, loading: namespacesLoading } = useNamespace(selectedNamespace); + const [selectedSources, setSelectedSources] = useState(appStore.configuredSources); + const [selectedFutureApps, setSelectedFutureApps] = useState(appStore.configuredFutureApps); + + const { allNamespaces, data: singleNamespace, loading: namespacesLoading } = useNamespace(selectedNamespace); + // Keeps intial values fetched from API, so we can later filter the user-specific-selections, therebey minimizing the amount of data sent to the API on "persist sources". + const [recordedInitialValues, setRecordedInitialValues] = useState(appStore.availableSources); useEffect(() => { if (!!allNamespaces?.length) { - // auto-select the 1st namespace - if (autoSelectNamespace) setSelectedNamespace(allNamespaces[0].name); - - // initialize all namespaces (to avoid undefined errors) - setAvailableSources((prev) => { + // initialize all states (to avoid undefined errors) + setRecordedInitialValues((prev) => { + const payload = { ...prev }; + allNamespaces.forEach(({ name }) => (payload[name] = payload[name] || [])); + return payload; + }); + setSelectedSources((prev) => { const payload = { ...prev }; allNamespaces.forEach(({ name }) => (payload[name] = payload[name] || [])); return payload; @@ -70,81 +66,54 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo allNamespaces.forEach(({ name }) => (payload[name] = payload[name] || false)); return payload; }); + // auto-select the 1st namespace + if (autoSelectNamespace) setSelectedNamespace(allNamespaces[0].name); } }, [allNamespaces, autoSelectNamespace]); useEffect(() => { - if (!!namespacesData) { - // set available sources for current selected namespace - const { name, k8sActualSources = [] } = namespacesData; - setAvailableSources((prev) => ({ ...prev, [name]: k8sActualSources })); - setSelectedSources((prev) => ({ ...prev, [name]: prev[name] || k8sActualSources.filter(({ selected }) => selected) })); + if (!!singleNamespace) { + // initialize sources for this namespace + const { name, k8sActualSources = [] } = singleNamespace; + setRecordedInitialValues((prev) => ({ ...prev, [name]: k8sActualSources })); + setSelectedSources((prev) => ({ ...prev, [name]: !!prev[name].length ? prev[name] : k8sActualSources })); } - }, [namespacesData]); + }, [singleNamespace]); // form filters const [searchText, setSearchText] = useState(''); const [showSelectedOnly, setShowSelectedOnly] = useState(false); - const doSelectAll = () => { - setSelectedSources((prev) => { - const payload = { ...prev }; - - Object.entries(availableSources).forEach(([namespace, sources]) => { - payload[namespace] = sources; - }); - - return payload; - }); - }; - - const doUnselectAll = () => { - setSelectedSources((prev) => { - const payload = { ...prev }; - - Object.keys(availableSources).forEach((namespace) => { - payload[namespace] = []; - }); - - return payload; - }); - }; - - const namespaceWasSelected = useRef(false); const onSelectAll: UseSourceFormDataResponse['onSelectAll'] = useCallback( (bool, namespace, isFromInterval) => { if (!!namespace) { - if (!isFromInterval) namespaceWasSelected.current = selectedNamespace === namespace; - const nsAvailableSources = availableSources[namespace]; - const nsSelectedSources = selectedSources[namespace]; + // When clicking "select all" on a namespace - if (!nsSelectedSources && bool) { + if (!isFromInterval && bool) { onSelectNamespace(namespace); setSelectAllForNamespace(namespace); } else { - setSelectedSources((prev) => ({ ...prev, [namespace]: bool ? nsAvailableSources : [] })); + setSelectedSources((prev) => ({ ...prev, [namespace]: selectedSources[namespace].map((source) => ({ ...source, selected: bool })) })); setSelectAllForNamespace(''); - - // Note: if we want to select all, but not open the expanded view, we can use the following: - // if (!!nsAvailableSources.length && !namespaceWasSelected.current) setSelectedNamespace(''); - - namespaceWasSelected.current = false; } } else { - setSelectAll(bool); + // When clicking "select all" on all namespaces - if (bool) { - doSelectAll(); - } else { - doUnselectAll(); - } + setSelectedSources((prev) => { + const payload = { ...prev }; + + Object.entries(payload).forEach(([namespace, sources]) => { + payload[namespace] = sources.map((source) => ({ ...source, selected: bool })); + }); + + return payload; + }); } }, - [availableSources, selectedSources], + [selectedSources], ); - // this is to keep trying "select all" per namespace until the sources are loaded (allows for 1-click, better UX). - // if selectedSources returns an emtpy array, it will stop to prevent inifnite loop where no availableSources ever exist for that namespace + // This is to keep trying "select all" per namespace, until the sources are loaded (allows for 1-click, better UX). useEffect(() => { if (!!selectAllForNamespace) { const interval = setInterval(() => onSelectAll(true, selectAllForNamespace, true), 100); @@ -153,10 +122,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo }, [selectAllForNamespace, onSelectAll]); const onSelectNamespace: UseSourceFormDataResponse['onSelectNamespace'] = (namespace) => { - const alreadySelected = selectedNamespace === namespace; - - setSelectedNamespace(alreadySelected ? '' : namespace); - setAvailableSources((prev) => ({ ...prev, [namespace]: prev[namespace] || [] })); + setSelectedNamespace((prev) => (prev === namespace ? '' : namespace)); }; const onSelectSource: UseSourceFormDataResponse['onSelectSource'] = (source, namespace) => { @@ -164,17 +130,18 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo if (!id) return; - const selected = [...(selectedSources[id] || [])]; - const foundIdx = selected.findIndex(({ name, kind }) => name === source.name && kind === source.kind); + const arr = [...(selectedSources[id] || [])]; + const foundIdx = arr.findIndex(({ name, kind }) => name === source.name && kind === source.kind); if (foundIdx !== -1) { - selected.splice(foundIdx, 1); + // Replace the item with a new object to avoid mutating a possibly read-only object + const updatedItem = { ...arr[foundIdx], selected: !arr[foundIdx].selected }; + arr[foundIdx] = updatedItem; } else { - selected.push(source); + arr.push({ ...source, selected: true }); } - setSelectedSources((prev) => ({ ...prev, [id]: selected })); - setSelectAll(false); + setSelectedSources((prev) => ({ ...prev, [id]: arr })); }; const onSelectFutureApps: UseSourceFormDataResponse['onSelectFutureApps'] = (bool, namespace) => { @@ -185,6 +152,15 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo setSelectedFutureApps((prev) => ({ ...prev, [id]: bool })); }; + const filterNamespaces: UseSourceFormDataResponse['filterNamespaces'] = (options) => { + const { cancelSearch } = options || {}; + const namespaces = Object.entries(selectedSources); + + const isSearchOk = (targetText: string) => cancelSearch || !searchText || targetText.toLowerCase().includes(searchText); + + return namespaces.filter(([namespace]) => isSearchOk(namespace)); + }; + const filterSources: UseSourceFormDataResponse['filterSources'] = (namespace, options) => { const { cancelSearch, cancelSelected } = options || {}; const id = namespace || selectedNamespace; @@ -192,31 +168,48 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo if (!id) return []; const isSearchOk = (targetText: string) => cancelSearch || !searchText || targetText.toLowerCase().includes(searchText); - const isOnlySelectedOk = (selected: Record[], compareKey: string, target: string) => cancelSelected || !showSelectedOnly || !!selected.find((item) => item[compareKey] === target); - const filtered = availableSources[id].filter((source) => isSearchOk(source.name) && isOnlySelectedOk(selectedSources[id], 'name', source.name)); + const isOnlySelectedOk = (sources: Record[], compareKey: string, target: string) => + cancelSelected || !showSelectedOnly || !!sources.find((item) => item[compareKey] === target && item.selected); - return filtered; + return selectedSources[id].filter((source) => isSearchOk(source.name) && isOnlySelectedOk(selectedSources[id], 'name', source.name)); + }; + + const getApiPaylod: UseSourceFormDataResponse['getApiPaylod'] = () => { + const payload: UseSourceFormDataResponse['selectedSources'] = {}; + + Object.entries(selectedSources).forEach(([namespace, sources]) => { + sources.forEach((source) => { + const foundInitial = recordedInitialValues[namespace]?.find((initialSource) => initialSource.name === source.name && initialSource.kind === source.kind); + + if (foundInitial?.selected !== source.selected) { + if (!payload[namespace]) payload[namespace] = []; + payload[namespace].push(source); + } + }); + }); + + return payload; }; return { namespacesLoading, + recordedInitialValues, + filterNamespaces, + filterSources, + getApiPaylod, selectedNamespace, - availableSources, - selectedSources, - selectedFutureApps, onSelectNamespace, + selectedSources, onSelectSource, + selectedFutureApps, onSelectFutureApps, searchText, - selectAll, - selectAllForNamespace, - showSelectedOnly, setSearchText, - onSelectAll, + showSelectedOnly, setShowSelectedOnly, - - filterSources, + selectAllForNamespace, + onSelectAll, }; }; diff --git a/frontend/webapp/reuseable-components/badge/index.tsx b/frontend/webapp/reuseable-components/badge/index.tsx index a4acb7cd7..1d70f10e2 100644 --- a/frontend/webapp/reuseable-components/badge/index.tsx +++ b/frontend/webapp/reuseable-components/badge/index.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; interface Props { - label: string | number; + label: string | number | JSX.Element; filled?: boolean; } diff --git a/frontend/webapp/reuseable-components/checkbox/index.tsx b/frontend/webapp/reuseable-components/checkbox/index.tsx index 294ad9f6d..2987273fc 100644 --- a/frontend/webapp/reuseable-components/checkbox/index.tsx +++ b/frontend/webapp/reuseable-components/checkbox/index.tsx @@ -16,6 +16,7 @@ interface CheckboxProps { disabled?: boolean; style?: React.CSSProperties; errorMessage?: string; + allowPropagation?: boolean; } const Container = styled.div<{ $disabled?: CheckboxProps['disabled'] }>` @@ -39,14 +40,13 @@ const CheckboxWrapper = styled.div<{ $isChecked: boolean; $disabled?: CheckboxPr transition: border 0.3s, background-color 0.3s; `; -export const Checkbox: React.FC = ({ title, titleColor, tooltip, value = false, onChange, disabled, style, errorMessage }) => { +export const Checkbox: React.FC = ({ title, titleColor, tooltip, value = false, onChange, disabled, style, errorMessage, allowPropagation = false }) => { const [isChecked, setIsChecked] = useState(value); useEffect(() => setIsChecked(value), [value]); const handleToggle: React.MouseEventHandler = (e) => { if (disabled) return; - - e.stopPropagation(); + if (!allowPropagation) e.stopPropagation(); if (onChange) onChange(!isChecked); else setIsChecked((prev) => !prev); diff --git a/frontend/webapp/reuseable-components/icon-button/index.tsx b/frontend/webapp/reuseable-components/icon-button/index.tsx index 3a4a1bd1d..cc74b4b93 100644 --- a/frontend/webapp/reuseable-components/icon-button/index.tsx +++ b/frontend/webapp/reuseable-components/icon-button/index.tsx @@ -1,18 +1,19 @@ import React, { CSSProperties, PropsWithChildren } from 'react'; -import styled, { keyframes } from 'styled-components'; import { Tooltip } from '../tooltip'; +import styled, { keyframes } from 'styled-components'; interface Props extends PropsWithChildren { onClick?: () => void; tooltip?: string; + size?: number; withPing?: boolean; pingColor?: CSSProperties['backgroundColor']; } -const Button = styled.button` +const Button = styled.button<{ $size: number }>` position: relative; - width: 36px; - height: 36px; + width: ${({ $size }) => $size}px; + height: ${({ $size }) => $size}px; border: none; border-radius: 100%; background-color: transparent; @@ -36,10 +37,10 @@ const pingAnimation = keyframes` } `; -const Ping = styled.div<{ $color: Props['pingColor'] }>` +const Ping = styled.div<{ $size: number; $color: Props['pingColor'] }>` position: absolute; - top: 8px; - right: 8px; + top: ${({ $size }) => $size / 5}px; + right: ${({ $size }) => $size / 5}px; width: 6px; height: 6px; border-radius: 100%; @@ -55,11 +56,11 @@ const Ping = styled.div<{ $color: Props['pingColor'] }>` } `; -export const IconButton: React.FC = ({ children, onClick, tooltip, withPing, pingColor, ...props }) => { +export const IconButton: React.FC = ({ children, onClick, tooltip, size = 36, withPing, pingColor, ...props }) => { return ( - diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/add-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/add-node.tsx index 64f139d25..0a7f35ccd 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/add-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/add-node.tsx @@ -23,7 +23,7 @@ interface Props > {} const Container = styled(FlexColumn)<{ $nodeWidth: Props['data']['nodeWidth'] }>` - min-height: 69px; + min-height: 50px; // negative width applied here because of the padding left&right width: ${({ $nodeWidth }) => `${$nodeWidth - 40}px`}; padding: 16px 24px 16px 16px; diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx index cfb27aaaf..40c4b7629 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx @@ -39,32 +39,27 @@ const BaseNode: React.FC = ({ id: nodeId, data }) => { const isPending = isThisPending({ entityType, entityId }); const renderActions = () => { - const getSourceLocation = () => { - const { namespace, name, kind } = raw as K8sActualSource; - const selected = { ...configuredSources }; - if (!selected[namespace]) selected[namespace] = []; + const { namespace, name, kind } = raw as K8sActualSource; + const sources = { ...configuredSources }; + if (!sources[namespace]) sources[namespace] = []; - const index = selected[namespace].findIndex((x) => x.name === name && x.kind === kind); - return { index, namespace, selected }; - }; + const index = sources[namespace].findIndex((x) => x.name === name && x.kind === kind); const onSelectSource = () => { - const { index, namespace, selected } = getSourceLocation(); - if (index === -1) { - selected[namespace].push(raw as K8sActualSource); + sources[namespace].push(raw as K8sActualSource); } else { - selected[namespace].splice(index, 1); + sources[namespace].splice(index, 1); } - setConfiguredSources(selected); + setConfiguredSources(sources); }; return ( <> {/* TODO: handle action/icon to apply instrumentation-rules for individual sources (@Notion GEN-1650) */} {isPending ? : isError ? : null} - {entityType === 'source' ? : null} + {entityType === 'source' ? : null} ); }; diff --git a/frontend/webapp/reuseable-components/toggle/index.tsx b/frontend/webapp/reuseable-components/toggle/index.tsx index 010d525ac..5c6b96331 100644 --- a/frontend/webapp/reuseable-components/toggle/index.tsx +++ b/frontend/webapp/reuseable-components/toggle/index.tsx @@ -1,4 +1,3 @@ -import Image from 'next/image'; import { Text } from '../text'; import { Tooltip } from '../tooltip'; import styled from 'styled-components'; @@ -10,6 +9,7 @@ interface ToggleProps { initialValue?: boolean; onChange?: (value: boolean) => void; disabled?: boolean; + allowPropagation?: boolean; } const Container = styled.div<{ $disabled?: ToggleProps['disabled'] }>` @@ -44,20 +44,22 @@ const ToggleSwitch = styled.div<{ $isActive: boolean; $disabled?: ToggleProps['d } `; -const Toggle: React.FC = ({ title, tooltip, initialValue = false, onChange, disabled }) => { +const Toggle: React.FC = ({ title, tooltip, initialValue = false, onChange, disabled, allowPropagation = false }) => { const [isActive, setIsActive] = useState(initialValue); useEffect(() => setIsActive(initialValue), [initialValue]); const handleToggle: React.MouseEventHandler = (e) => { if (disabled) return; - - e.stopPropagation(); + if (!allowPropagation) e.stopPropagation(); setIsActive((prev) => { const newValue = !prev; if (onChange) onChange(newValue); return newValue; }); + + if (onChange) onChange(!isActive); + else setIsActive((prev) => !prev); }; return ( From dbc89fe75c071375f26f141ee74435eb34ff50cc Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 15:38:08 +0200 Subject: [PATCH 190/259] chore: new labels working e2e --- .../crd/bases/odigos.io_destinations.yaml | 6 +-- api/odigos/v1alpha1/destination_types.go | 4 +- autoscaler/controllers/common/processors.go | 47 +++++++++---------- .../odigosroutingfilterprocessor/config.go | 3 +- .../crds/odigos.io_destinations.yaml | 6 +-- 5 files changed, 28 insertions(+), 38 deletions(-) diff --git a/api/config/crd/bases/odigos.io_destinations.yaml b/api/config/crd/bases/odigos.io_destinations.yaml index 7b6205e35..98f4be78a 100644 --- a/api/config/crd/bases/odigos.io_destinations.yaml +++ b/api/config/crd/bases/odigos.io_destinations.yaml @@ -84,12 +84,8 @@ spec: type: array modes: description: |- - Mode can contain a combination of "all", "namespaces", or "groups". + Modes can contain a combination of "all", "namespaces", or "groups". Determines how sources are selected for this destination. - enum: - - all - - namespaces - - groups items: type: string type: array diff --git a/api/odigos/v1alpha1/destination_types.go b/api/odigos/v1alpha1/destination_types.go index 7eabe0557..872909487 100644 --- a/api/odigos/v1alpha1/destination_types.go +++ b/api/odigos/v1alpha1/destination_types.go @@ -39,9 +39,9 @@ type DestinationSpec struct { // SourceSelector defines the criteria for selecting sources. type SourceSelector struct { - // Mode can contain a combination of "all", "namespaces", or "groups". + // Modes can contain a combination of "all", "namespaces", or "groups". // Determines how sources are selected for this destination. - // +kubebuilder:validation:Enum=all;namespaces;groups + // +kubebuilder:validation:Items=enum=all;namespaces;groups Modes []string `json:"modes,omitempty"` // Namespaces specifies the namespaces for "namespaces" mode. diff --git a/autoscaler/controllers/common/processors.go b/autoscaler/controllers/common/processors.go index 3861d111e..161ef2996 100644 --- a/autoscaler/controllers/common/processors.go +++ b/autoscaler/controllers/common/processors.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "sort" - "strings" "github.com/go-logr/logr" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" @@ -90,7 +89,6 @@ func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProce logger.Info("Processing destination for filter processor") if dest.Spec.SourceSelector == nil || contains(dest.Spec.SourceSelector.Modes, "all") { - logger.Info("Skipping destination as SourceSelector is nil or set to 'all'") continue } @@ -102,8 +100,6 @@ func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProce matchedSources = append(matchedSources, fetchSourcesByGroups(ctx, kubeClient, dest.Spec.SourceSelector.Groups, logger)...) } - logger.Info("Matched sources for destination", "matchedSources", matchedSources) - matchConditions := make(map[string]bool) for _, source := range matchedSources { logger.Info("Adding match condition for source", "sourceName", source.Spec.Workload.Name, "namespace", source.Spec.Workload.Namespace, "kind", source.Spec.Workload.Kind) @@ -112,11 +108,6 @@ func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProce matchConditions[key] = true } - if len(matchConditions) == 0 { - logger.Info("No matched sources for destination. Skipping processor creation.") - continue - } - filterConfig := map[string]interface{}{ "match_conditions": matchConditions, } @@ -154,23 +145,31 @@ func fetchSourcesByNamespaces(ctx context.Context, kubeClient client.Client, nam } func fetchSourcesByGroups(ctx context.Context, kubeClient client.Client, groups []string, logger logr.Logger) []odigosv1.Source { - selectors := make([]string, len(groups)) - for i, group := range groups { - selectors[i] = fmt.Sprintf("odigos.io/group-%s=true", group) - } - labelSelector := labels.SelectorFromSet(labels.Set{ - strings.Join(selectors, ","): "", - }) + sourceMap := make(map[string]odigosv1.Source) + for _, group := range groups { + labelSelector := labels.Set{fmt.Sprintf("odigos.io/group-%s", group): "true"}.AsSelector() - sourceList := &odigosv1.SourceList{} - err := kubeClient.List(ctx, sourceList, &client.ListOptions{ - LabelSelector: labelSelector, - }) - if err != nil { - logger.Error(err, "Failed to fetch sources by groups", "groups", groups) - return nil + sourceList := &odigosv1.SourceList{} + err := kubeClient.List(ctx, sourceList, &client.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + logger.Error(err, "Failed to fetch sources for group", "group", group) + continue + } + + for _, source := range sourceList.Items { + key := fmt.Sprintf("%s/%s", source.Namespace, source.Name) + if _, exists := sourceMap[key]; !exists { + sourceMap[key] = source + } + } } - return sourceList.Items + sources := make([]odigosv1.Source, 0, len(sourceMap)) + for _, source := range sourceMap { + sources = append(sources, source) + } + return sources } func marshalConfig(config map[string]interface{}) []byte { diff --git a/collector/processors/odigosroutingfilterprocessor/config.go b/collector/processors/odigosroutingfilterprocessor/config.go index 4c0d7e177..692dcac7d 100644 --- a/collector/processors/odigosroutingfilterprocessor/config.go +++ b/collector/processors/odigosroutingfilterprocessor/config.go @@ -1,7 +1,6 @@ package odigosroutingfilterprocessor import ( - "errors" "fmt" "strings" @@ -16,7 +15,7 @@ var _ component.Config = (*Config)(nil) func (cfg *Config) Validate() error { if len(cfg.MatchConditions) == 0 { - return errors.New("at least one match condition must be specified") + return nil } for key := range cfg.MatchConditions { diff --git a/helm/odigos/templates/crds/odigos.io_destinations.yaml b/helm/odigos/templates/crds/odigos.io_destinations.yaml index 7b6205e35..98f4be78a 100644 --- a/helm/odigos/templates/crds/odigos.io_destinations.yaml +++ b/helm/odigos/templates/crds/odigos.io_destinations.yaml @@ -84,12 +84,8 @@ spec: type: array modes: description: |- - Mode can contain a combination of "all", "namespaces", or "groups". + Modes can contain a combination of "all", "namespaces", or "groups". Determines how sources are selected for this destination. - enum: - - all - - namespaces - - groups items: type: string type: array From 18d971e5a19f3ce4d136ea1ad185afdd9b1ed8d9 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 15:43:53 +0200 Subject: [PATCH 191/259] chore: remove dup code in make file --- Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Makefile b/Makefile index d7dfd0954..68ee8fb82 100644 --- a/Makefile +++ b/Makefile @@ -201,10 +201,6 @@ cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) --nowait -.PHONY: cli-uninstall -cli-uninstall: - @echo "Uninstalling odigos from source. version: $(ODIGOS_CLI_VERSION)" - cd ./cli ; go run -tags=embed_manifests . uninstall .PHONY: cli-uninstall cli-uninstall: From e39349926a33cc99923ae6331820cdecc9e1f9e3 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 15:44:25 +0200 Subject: [PATCH 192/259] chore: remove dup code in make file --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 68ee8fb82..7590ea15f 100644 --- a/Makefile +++ b/Makefile @@ -201,7 +201,6 @@ cli-install: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . install --version $(ODIGOS_CLI_VERSION) --nowait - .PHONY: cli-uninstall cli-uninstall: @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" From ff6854b1a1930949c3286412b58b35774c625fba Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 15:45:14 +0200 Subject: [PATCH 193/259] chore: fix typo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7590ea15f..96005d933 100644 --- a/Makefile +++ b/Makefile @@ -203,7 +203,7 @@ cli-install: .PHONY: cli-uninstall cli-uninstall: - @echo "Installing odigos from source. version: $(ODIGOS_CLI_VERSION)" + @echo "Uninstalling odigos from source. version: $(ODIGOS_CLI_VERSION)" cd ./cli ; go run -tags=embed_manifests . uninstall .PHONY: cli-upgrade From f76af081b8400c255e28947112ed740704370758 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 15:46:47 +0200 Subject: [PATCH 194/259] chore: remove overhead comments" --- api/odigos/v1alpha1/destination_types.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/odigos/v1alpha1/destination_types.go b/api/odigos/v1alpha1/destination_types.go index 872909487..04fa8715f 100644 --- a/api/odigos/v1alpha1/destination_types.go +++ b/api/odigos/v1alpha1/destination_types.go @@ -37,18 +37,15 @@ type DestinationSpec struct { SourceSelector *SourceSelector `json:"sourceSelector,omitempty"` } -// SourceSelector defines the criteria for selecting sources. type SourceSelector struct { // Modes can contain a combination of "all", "namespaces", or "groups". // Determines how sources are selected for this destination. // +kubebuilder:validation:Items=enum=all;namespaces;groups Modes []string `json:"modes,omitempty"` - // Namespaces specifies the namespaces for "namespaces" mode. // +optional Namespaces []string `json:"namespaces,omitempty"` - // Groups specifies the groups for "groups" mode. // +optional Groups []string `json:"groups,omitempty"` } From e5572a6deabae62699133cb7a805371d9e5e430b Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 15:59:40 +0200 Subject: [PATCH 195/259] chore: use util file --- autoscaler/controllers/common/processors.go | 26 ++++----------------- autoscaler/utils/helpers.go | 25 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 autoscaler/utils/helpers.go diff --git a/autoscaler/controllers/common/processors.go b/autoscaler/controllers/common/processors.go index 161ef2996..ccf87351d 100644 --- a/autoscaler/controllers/common/processors.go +++ b/autoscaler/controllers/common/processors.go @@ -8,6 +8,7 @@ import ( "github.com/go-logr/logr" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/autoscaler/utils" "github.com/odigos-io/odigos/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -88,15 +89,15 @@ func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProce logger := log.Log.WithValues("destination", dest.Name) logger.Info("Processing destination for filter processor") - if dest.Spec.SourceSelector == nil || contains(dest.Spec.SourceSelector.Modes, "all") { + if dest.Spec.SourceSelector == nil || utils.Contains(dest.Spec.SourceSelector.Modes, "all") { continue } var matchedSources []odigosv1.Source - if contains(dest.Spec.SourceSelector.Modes, "namespaces") { + if utils.Contains(dest.Spec.SourceSelector.Modes, "namespaces") { matchedSources = append(matchedSources, fetchSourcesByNamespaces(ctx, kubeClient, dest.Spec.SourceSelector.Namespaces, logger)...) } - if contains(dest.Spec.SourceSelector.Modes, "groups") { + if utils.Contains(dest.Spec.SourceSelector.Modes, "groups") { matchedSources = append(matchedSources, fetchSourcesByGroups(ctx, kubeClient, dest.Spec.SourceSelector.Groups, logger)...) } @@ -118,7 +119,7 @@ func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProce }, Spec: odigosv1.ProcessorSpec{ Type: "odigosroutingfilterprocessor", - ProcessorConfig: runtime.RawExtension{Raw: marshalConfig(filterConfig)}, + ProcessorConfig: runtime.RawExtension{Raw: utils.MarshalConfig(filterConfig, logger)}, CollectorRoles: []odigosv1.CollectorsGroupRole{ odigosv1.CollectorsGroupRoleClusterGateway, }, @@ -171,20 +172,3 @@ func fetchSourcesByGroups(ctx context.Context, kubeClient client.Client, groups } return sources } - -func marshalConfig(config map[string]interface{}) []byte { - data, err := json.Marshal(config) - if err != nil { - log.Log.Error(err, "Failed to marshal processor config") - } - return data -} - -func contains(arr []string, val string) bool { - for _, item := range arr { - if item == val { - return true - } - } - return false -} diff --git a/autoscaler/utils/helpers.go b/autoscaler/utils/helpers.go new file mode 100644 index 000000000..698c45603 --- /dev/null +++ b/autoscaler/utils/helpers.go @@ -0,0 +1,25 @@ +package utils + +import ( + "encoding/json" + + "github.com/go-logr/logr" +) + +func MarshalConfig(config map[string]interface{}, logger logr.Logger) []byte { + data, err := json.Marshal(config) + if err != nil { + logger.Error(err, "Failed to marshal processor config") + return []byte("{}") + } + return data +} + +func Contains(arr []string, val string) bool { + for _, item := range arr { + if item == val { + return true + } + } + return false +} From ff8416b1e89b4c14dba26be8219161efc85cc8f9 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 16:00:18 +0200 Subject: [PATCH 196/259] chore: remove line --- autoscaler/controllers/destination_controller.go | 1 - 1 file changed, 1 deletion(-) diff --git a/autoscaler/controllers/destination_controller.go b/autoscaler/controllers/destination_controller.go index c0459e022..81c7c9e72 100644 --- a/autoscaler/controllers/destination_controller.go +++ b/autoscaler/controllers/destination_controller.go @@ -41,7 +41,6 @@ type DestinationReconciler struct { func (r *DestinationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) logger.V(0).Info("Reconciling Destination") - err := gateway.Sync(ctx, r.Client, r.Scheme, r.ImagePullSecrets, r.OdigosVersion, r.Config) if err != nil { return ctrl.Result{}, err From fdc320f2994393dcb73b7d2ed18609d982166cf7 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 1 Jan 2025 16:33:34 +0200 Subject: [PATCH 197/259] chore: wip\ --- autoscaler/controllers/source_controller.go | 2 - .../odigosroutingfilterprocessor/README.md | 54 ------------------- 2 files changed, 56 deletions(-) delete mode 100644 collector/processors/odigosroutingfilterprocessor/README.md diff --git a/autoscaler/controllers/source_controller.go b/autoscaler/controllers/source_controller.go index 1869f4774..64991d30b 100644 --- a/autoscaler/controllers/source_controller.go +++ b/autoscaler/controllers/source_controller.go @@ -31,11 +31,9 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr err := gateway.Sync(ctx, r.Client, r.Scheme, r.ImagePullSecrets, r.OdigosVersion, r.Config) if err != nil { - logger.Error(err, "Failed to sync gateway configuration") return ctrl.Result{}, err } - logger.V(1).Info("Successfully synced gateway configuration for Source", "Source", req.NamespacedName) return ctrl.Result{}, nil } diff --git a/collector/processors/odigosroutingfilterprocessor/README.md b/collector/processors/odigosroutingfilterprocessor/README.md deleted file mode 100644 index 9ec21bb9a..000000000 --- a/collector/processors/odigosroutingfilterprocessor/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Odigos Sampling processor - -This processor samples traces based on the following supported rules, grouping into these categories: - -1. Endpoint Rules: - -- HTTP Latency Rule: This rule allows you to configure service, endpoint, and threshold. Traces with a duration less than the specified threshold will be deleted. - - -``` yaml - groupbytrace: - wait_duration: 10s - odigossampling: - rules: - endpoint_rules: - - name: "http-latency-test" - type: "http_latency" - rule_details: - "threshold": 1050 - "http_route": "/buy" - "service_name": "frontend" - "fallback_sampling_ratio": 20.0 - ``` -- threshold: The maximum allowable trace duration in milliseconds. Traces with a duration less than this value will be deleted. -- endpoint: The specific HTTP route prefix to match for sampling. Only traces with routes starting with this prefix will be considered. For example, configuring /buy will also match /buy/product. -- service: The name of the service for which the rule applies. Only traces from this service will be considered. -- fallback_sampling_ratio: specifies the percentage of traces that meet the service/http_route filter but fall below the threshold that you still want to retain. For example, if a rule is set for service A and http_route B with a minimum latency threshold of 1 second, you might still want to keep some traces below this threshold. Setting the ratio to 20% ensures that 20% of these traces will be retained. - -2. Global Rules: -- Error Rule: This rule allows you to configure a list of status codes [ERROR/OK/UNSET]. traces with a status code that not configured will be delete. - -``` yaml -rules: - global_rules: - - name: "error-rule" - type: error - rule_details: - fallback_sampling_ratio: 50 -``` -- fallback_sampling_ratio: This parameter specifies the percentage of non-error traces you want to retain. For instance, setting it to 50 means you will see 100% of error traces and 50% of non-error traces. - - -**Notes:** -- When using the `odigossampling` processor, it is mandatory to use the `groupbytrace` processor beforehand. -``` -service: - pipelines: - traces: - receivers: - processors: - - groupbytrace - - odigossampling - exporters: -``` \ No newline at end of file From 744ceca0a3911539754961c3216ef4e9deeabbdc Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 2 Jan 2025 15:51:29 -0500 Subject: [PATCH 198/259] Sync main into feature/source-crd (#2121) Co-authored-by: Eden Federman Co-authored-by: Tamir David --- .../controllers/datacollection/configmap.go | 39 ++++++++++++++++++- .../controllers/datacollection/daemonset.go | 13 +++++++ collector/builder-config.yaml | 1 + collector/odigosotelcol/components.go | 3 ++ collector/odigosotelcol/go.mod | 2 + common/config/config.go | 6 +-- common/config/datadog.go | 19 ++++++++- common/config/testdata/debugexporter.yaml | 1 - common/config/testdata/minimal.yaml | 1 - common/config/testdata/withbaseminimal.yaml | 1 - docs/cli/odigos_diagnose.mdx | 2 +- docs/pipeline/actions/troubleshooting.mdx | 2 +- tests/common/assert/pipeline-ready.yaml | 7 ++++ tests/e2e/README.md | 3 +- 14 files changed, 89 insertions(+), 11 deletions(-) diff --git a/autoscaler/controllers/datacollection/configmap.go b/autoscaler/controllers/datacollection/configmap.go index f17baec96..d1d4d84dd 100644 --- a/autoscaler/controllers/datacollection/configmap.go +++ b/autoscaler/controllers/datacollection/configmap.go @@ -344,8 +344,45 @@ func calculateConfigMapData(nodeCG *odigosv1.CollectorsGroup, sources *odigosv1. "collection_interval": "10s", } + cfg.Receivers["hostmetrics"] = config.GenericMap{ + "collection_interval": "10s", + "root_path": "/hostfs", + "scrapers": config.GenericMap{ + "paging": config.GenericMap{ + "metrics": config.GenericMap{ + "system.paging.utilization": config.GenericMap{ + "enabled": true, + }, + }, + }, + "cpu": config.GenericMap{ + "metrics": config.GenericMap{ + "system.cpu.utilization": config.GenericMap{ + "enabled": true, + }, + }, + }, + "disk": struct{}{}, + "filesystem": config.GenericMap{ + "metrics": config.GenericMap{ + "system.filesystem.utilization": config.GenericMap{ + "enabled": true, + }, + }, + "exclude_mount_points": config.GenericMap{ + "match_type": "regexp", + "mount_points": []string{"/var/lib/kubelet/*"}, + }, + }, + "load": struct{}{}, + "memory": struct{}{}, + "network": struct{}{}, + "processes": struct{}{}, + }, + } + cfg.Service.Pipelines["metrics"] = config.Pipeline{ - Receivers: []string{"otlp", "kubeletstats"}, + Receivers: []string{"otlp", "kubeletstats", "hostmetrics"}, Processors: append(commonProcessors, metricsProcessors...), Exporters: []string{"otlp/gateway"}, } diff --git a/autoscaler/controllers/datacollection/daemonset.go b/autoscaler/controllers/datacollection/daemonset.go index a8063bdaa..0d511470e 100644 --- a/autoscaler/controllers/datacollection/daemonset.go +++ b/autoscaler/controllers/datacollection/daemonset.go @@ -261,6 +261,14 @@ func getDesiredDaemonSet(datacollection *odigosv1.CollectorsGroup, }, }, }, + { + Name: "hostfs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/", + }, + }, + }, }, Containers: []corev1.Container{ { @@ -287,6 +295,11 @@ func getDesiredDaemonSet(datacollection *odigosv1.CollectorsGroup, MountPath: "/var/lib/kubelet/pod-resources", ReadOnly: true, }, + { + Name: "hostfs", + MountPath: "/hostfs", + ReadOnly: true, + }, }, Env: []corev1.EnvVar{ { diff --git a/collector/builder-config.yaml b/collector/builder-config.yaml index 1329edcf4..8ae74ad81 100644 --- a/collector/builder-config.yaml +++ b/collector/builder-config.yaml @@ -91,6 +91,7 @@ receivers: - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.106.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver v0.106.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver v0.106.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver v0.106.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.106.0 # https://github.com/open-telemetry/opentelemetry-collector/issues/8127 diff --git a/collector/odigosotelcol/components.go b/collector/odigosotelcol/components.go index 1dc94fa3c..d16f0406d 100644 --- a/collector/odigosotelcol/components.go +++ b/collector/odigosotelcol/components.go @@ -95,6 +95,7 @@ import ( zipkinreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver" filelogreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver" kubeletstatsreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver" + hostmetricsreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver" prometheusreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver" ) @@ -122,6 +123,7 @@ func components() (otelcol.Factories, error) { zipkinreceiver.NewFactory(), filelogreceiver.NewFactory(), kubeletstatsreceiver.NewFactory(), + hostmetricsreceiver.NewFactory(), prometheusreceiver.NewFactory(), ) if err != nil { @@ -132,6 +134,7 @@ func components() (otelcol.Factories, error) { factories.ReceiverModules[zipkinreceiver.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.106.0" factories.ReceiverModules[filelogreceiver.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver v0.106.0" factories.ReceiverModules[kubeletstatsreceiver.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver v0.106.0" + factories.ReceiverModules[hostmetricsreceiver.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver v0.106.0" factories.ReceiverModules[prometheusreceiver.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.106.0" factories.Exporters, err = exporter.MakeFactoryMap( diff --git a/collector/odigosotelcol/go.mod b/collector/odigosotelcol/go.mod index c923bb9ed..41e88ad33 100644 --- a/collector/odigosotelcol/go.mod +++ b/collector/odigosotelcol/go.mod @@ -79,6 +79,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver v0.106.0 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.106.0 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.106.0 @@ -451,6 +452,7 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect + github.com/prometheus-community/windows_exporter v0.25.1 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect diff --git a/common/config/config.go b/common/config/config.go index 50e0fec91..fdaafef8a 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -31,19 +31,19 @@ type Config struct { Exporters GenericMap `json:"exporters"` Processors GenericMap `json:"processors"` Extensions GenericMap `json:"extensions"` - Connectors GenericMap `json:"connectors"` + Connectors GenericMap `json:"connectors,omitempty"` Service Service `json:"service"` } type Telemetry struct { - Metrics GenericMap `json:"metrics"` + Metrics GenericMap `json:"metrics"` Resource map[string]*string `json:"resource"` } type Service struct { Extensions []string `json:"extensions"` Pipelines map[string]Pipeline `json:"pipelines"` - Telemetry Telemetry `json:"telemetry,omitempty"` + Telemetry Telemetry `json:"telemetry,omitempty"` } type Pipeline struct { diff --git a/common/config/datadog.go b/common/config/datadog.go index 1534e15bf..eb0c5041b 100644 --- a/common/config/datadog.go +++ b/common/config/datadog.go @@ -35,16 +35,33 @@ func (d *Datadog) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) e }, } + connectorEnabled := false + connectorName := "datadog/connector-" + dest.GetID() + if isTracingEnabled(dest) && isMetricsEnabled(dest) { + currentConfig.Connectors[connectorName] = struct{}{} + connectorEnabled = true + } + if isTracingEnabled(dest) { tracesPipelineName := "traces/datadog-" + dest.GetID() + exporters := []string{exporterName} + if connectorEnabled { + exporters = append(exporters, connectorName) + } + currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ - Exporters: []string{exporterName}, + Exporters: exporters, } } if isMetricsEnabled(dest) { metricsPipelineName := "metrics/datadog-" + dest.GetID() + var receivers []string + if connectorEnabled { + receivers = []string{connectorName} + } currentConfig.Service.Pipelines[metricsPipelineName] = Pipeline{ + Receivers: receivers, Exporters: []string{exporterName}, } } diff --git a/common/config/testdata/debugexporter.yaml b/common/config/testdata/debugexporter.yaml index 5a0985d6b..4998e9827 100644 --- a/common/config/testdata/debugexporter.yaml +++ b/common/config/testdata/debugexporter.yaml @@ -24,7 +24,6 @@ extensions: health_check: endpoint: 0.0.0.0:13133 pprof: {} -connectors: {} service: extensions: - health_check diff --git a/common/config/testdata/minimal.yaml b/common/config/testdata/minimal.yaml index 2dc86edc7..ba38b79d9 100644 --- a/common/config/testdata/minimal.yaml +++ b/common/config/testdata/minimal.yaml @@ -22,7 +22,6 @@ extensions: health_check: endpoint: 0.0.0.0:13133 pprof: {} -connectors: {} service: extensions: - health_check diff --git a/common/config/testdata/withbaseminimal.yaml b/common/config/testdata/withbaseminimal.yaml index 090a3700a..ebfb677b1 100644 --- a/common/config/testdata/withbaseminimal.yaml +++ b/common/config/testdata/withbaseminimal.yaml @@ -7,7 +7,6 @@ exporters: {} processors: batch: {} extensions: {} -connectors: {} service: extensions: [] pipelines: {} diff --git a/docs/cli/odigos_diagnose.mdx b/docs/cli/odigos_diagnose.mdx index 5691d612e..4be3f34cd 100644 --- a/docs/cli/odigos_diagnose.mdx +++ b/docs/cli/odigos_diagnose.mdx @@ -3,7 +3,7 @@ title: "odigos diagnose" sidebarTitle: "odigos diagnose" --- -Retrieves Logs of all Odigos Components in the odigos-system namespace and CRDs of Actions, Instrumentation resources and more.
+Retrieves Logs of all Odigos components in the odigos-system namespace and CRDs of Actions, instrumentation resources and more.
The results will be saved in a compressed file for further troubleshooting. ## Synopsis diff --git a/docs/pipeline/actions/troubleshooting.mdx b/docs/pipeline/actions/troubleshooting.mdx index cc925c91d..9f569c1b6 100644 --- a/docs/pipeline/actions/troubleshooting.mdx +++ b/docs/pipeline/actions/troubleshooting.mdx @@ -45,7 +45,7 @@ And might find an error message like: ## View Autoscaler Logs -The `odigos-autoscaler` controller is the component that watch for changes in the action or processor CRD and generates the collector confgiuration. +The `odigos-autoscaler` controller is the component that watches for changes in the action or processor CRD and generates the collector configuration. If you don't see your processor in the collector configuration, you can check the logs of the autoscaler for errors or clues: diff --git a/tests/common/assert/pipeline-ready.yaml b/tests/common/assert/pipeline-ready.yaml index 6c023c690..3ae40fe5c 100644 --- a/tests/common/assert/pipeline-ready.yaml +++ b/tests/common/assert/pipeline-ready.yaml @@ -149,6 +149,9 @@ spec: - mountPath: /var/lib/kubelet/pod-resources name: kubeletpodresources readOnly: true + - mountPath: /hostfs + name: hostfs + readOnly: true env: - name: NODE_NAME valueFrom: @@ -193,6 +196,10 @@ spec: path: /var/lib/kubelet/pod-resources type: "" name: kubeletpodresources + - hostPath: + path: / + type: "" + name: hostfs status: numberAvailable: 1 numberReady: 1 \ No newline at end of file diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 5948adbc2..1d434da94 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -141,7 +141,8 @@ helm install -n traces grafana grafana/grafana \ --set "datasources.datasources\.yaml.datasources[0].type=tempo" \ --set "datasources.datasources\.yaml.datasources[0].url=http://e2e-tests-tempo:3100" \ --set "datasources.datasources\.yaml.datasources[0].access=proxy" \ - --set "datasources.datasources\.yaml.datasources[0].isDefault=true"``` + --set "datasources.datasources\.yaml.datasources[0].isDefault=true" +``` - Port forward to the grafana service: From d6a01b5c31ed56175579b4a59e63d19e6d8b4936 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 6 Jan 2025 11:07:25 +0200 Subject: [PATCH 199/259] merge main into feat/source branch (#2136) Co-authored-by: Ron Federman <73110295+RonFed@users.noreply.github.com> Co-authored-by: Tamir David --- agents/python/setup.py | 2 +- cli/cmd/resources/README.md | 1 - cli/cmd/resources/ui.go | 12 +- frontend/graph/generated.go | 59 ++++++- frontend/graph/model/models_gen.go | 5 +- frontend/graph/schema.graphqls | 5 +- frontend/graph/schema.resolvers.go | 2 +- frontend/services/namespaces.go | 6 +- .../icons/common/code-brackets-icon.tsx | 16 ++ .../webapp/assets/icons/common/code-icon.tsx | 2 +- frontend/webapp/assets/icons/common/index.ts | 2 + .../webapp/assets/icons/common/list-icon.tsx | 11 ++ .../webapp/components/common/buttons/index.ts | 1 + .../buttons/toggle-code-component/index.tsx | 40 +++++ frontend/webapp/components/common/index.ts | 1 + .../overview/all-drawers/describe-drawer.tsx | 17 +- .../main/overview/overview-drawer/index.tsx | 2 +- .../choose-source-modal/index.tsx | 6 +- .../sources-list/index.tsx | 2 +- .../source-controls/index.tsx | 5 +- .../main/sources/choose-sources/index.tsx | 10 +- .../sources/source-drawer-container/index.tsx | 11 +- frontend/webapp/cypress/functions/index.ts | 1 - .../graphql/queries/compute-platform.ts | 2 + frontend/webapp/graphql/queries/describe.ts | 6 - .../hooks/describe/useDescribeOdigos.ts | 34 +++- .../hooks/describe/useDescribeSource.ts | 44 ++++- .../webapp/hooks/sources/useSourceCRUD.ts | 7 + .../webapp/hooks/sources/useSourceFormData.ts | 40 +++-- frontend/webapp/package.json | 3 +- .../reuseable-components/code/index.tsx | 160 +++++++++++++++++- .../reuseable-components/data-card/index.tsx | 15 +- frontend/webapp/types/compute-platform.ts | 1 + helm/odigos/templates/ui/clusterrole.yaml | 8 +- .../instrumentationconfig/common.go | 10 +- .../instrumentationconfig/common_test.go | 159 +++++------------ ...go => instrumentationconfig_controller.go} | 29 +--- .../instrumentationrule_controller.go | 37 ++-- .../instrumentationconfig/manager.go | 9 +- .../instrumentationconfig_controller.go | 49 ------ .../instrumentationdevice/manager.go | 11 +- .../instrumentationdevice/suite_test.go | 131 -------------- .../instrumentation_rule.go} | 3 +- .../predicates/runtime_details_changed.go | 62 +++++++ instrumentor/report/events.go | 6 +- 45 files changed, 604 insertions(+), 441 deletions(-) create mode 100644 frontend/webapp/assets/icons/common/code-brackets-icon.tsx create mode 100644 frontend/webapp/assets/icons/common/list-icon.tsx create mode 100644 frontend/webapp/components/common/buttons/index.ts create mode 100644 frontend/webapp/components/common/buttons/toggle-code-component/index.tsx rename instrumentor/controllers/instrumentationconfig/{instrumentedapplication_controller.go => instrumentationconfig_controller.go} (65%) delete mode 100644 instrumentor/controllers/instrumentationdevice/suite_test.go rename instrumentor/controllers/utils/{predicate.go => predicates/instrumentation_rule.go} (98%) create mode 100644 instrumentor/controllers/utils/predicates/runtime_details_changed.go diff --git a/agents/python/setup.py b/agents/python/setup.py index 7054ee2be..51f84aacd 100644 --- a/agents/python/setup.py +++ b/agents/python/setup.py @@ -6,7 +6,7 @@ # index_url = 'http://host.docker.internal:8080/packages/odigos_opentelemetry_python-0.1.1-py3-none-any.whl' install_requires = [ - f"odigos-opentelemetry-python @ {index_url}" if index_url else "odigos-opentelemetry-python==1.0.22" + f"odigos-opentelemetry-python @ {index_url}" if index_url else "odigos-opentelemetry-python==1.0.23" ] setup( diff --git a/cli/cmd/resources/README.md b/cli/cmd/resources/README.md index 257d15fc8..5cdf5b6ce 100644 --- a/cli/cmd/resources/README.md +++ b/cli/cmd/resources/README.md @@ -63,5 +63,4 @@ In this doc, we'll keep track of the permissions requested across different reso | UI | "" | secrets | get, list, create, patch, update | Manages destination secrets for configurations. | | UI | odigos.io | instrumentationrules, destinations | get, list, create, patch, update, delete | CRUD for destinations and instrumentation rules. | | UI | odigos.io | collectorsgroups | get, list | Monitors groupings of collectors for UI updates. | -| UI | odigos.io | sources | get, list, create, delete | Manages sources for instrumentation and monitoring. | | UI | actions.odigos.io | \* | get, list, create, patch, update, delete | Pipeline action management. | diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index f02d44122..c51b0b53b 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -230,26 +230,22 @@ func NewUIClusterRole() *rbacv1.ClusterRole { Verbs: []string{"get", "list"}, }, { // Need "pods" for "Describe Source" - // for collector metrics - watch and list collectors pods + // for collector metrics - watch and list collectors pods APIGroups: []string{""}, Resources: []string{"pods"}, Verbs: []string{"get", "list", "watch"}, }, - { // Needed to read Odigos entities + { // Needed to read Odigos entities, + // "watch" to notify UI about changes with sources APIGroups: []string{"odigos.io"}, Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, - Verbs: []string{"get", "list"}, + Verbs: []string{"get", "list", "watch"}, }, { // Needed to instrument / uninstrument sources APIGroups: []string{"odigos.io"}, Resources: []string{"sources"}, Verbs: []string{"get", "list", "create", "delete"}, }, - { // Needed to notify UI about changes with sources - APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentationconfigs", "instrumentationinstances"}, - Verbs: []string{"watch"}, - }, }, } } diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index b41a613fe..dc7c1e41a 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -259,6 +259,7 @@ type ComplexityRoot struct { K8sActualNamespace struct { K8sActualSources func(childComplexity int) int Name func(childComplexity int) int + Selected func(childComplexity int) int } K8sActualSource struct { @@ -1376,6 +1377,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8sActualNamespace.Name(childComplexity), true + case "K8sActualNamespace.selected": + if e.complexity.K8sActualNamespace.Selected == nil { + break + } + + return e.complexity.K8sActualNamespace.Selected(childComplexity), true + case "K8sActualSource.conditions": if e.complexity.K8sActualSource.Conditions == nil { break @@ -3858,6 +3866,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespaces(_ c switch field.Name { case "name": return ec.fieldContext_K8sActualNamespace_name(ctx, field) + case "selected": + return ec.fieldContext_K8sActualNamespace_selected(ctx, field) case "k8sActualSources": return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } @@ -3905,6 +3915,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx switch field.Name { case "name": return ec.fieldContext_K8sActualNamespace_name(ctx, field) + case "selected": + return ec.fieldContext_K8sActualNamespace_selected(ctx, field) case "k8sActualSources": return ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) } @@ -8513,6 +8525,47 @@ func (ec *executionContext) fieldContext_K8sActualNamespace_name(_ context.Conte return fc, nil } +func (ec *executionContext) _K8sActualNamespace_selected(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_K8sActualNamespace_selected(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Selected, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*bool) + fc.Result = res + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_K8sActualNamespace_selected(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "K8sActualNamespace", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _K8sActualNamespace_k8sActualSources(ctx context.Context, field graphql.CollectedField, obj *model.K8sActualNamespace) (ret graphql.Marshaler) { fc, err := ec.fieldContext_K8sActualNamespace_k8sActualSources(ctx, field) if err != nil { @@ -17394,7 +17447,7 @@ func (ec *executionContext) unmarshalInputPersistNamespaceItemInput(ctx context. it.Name = data case "futureSelected": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("futureSelected")) - data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) + data, err := ec.unmarshalNBoolean2bool(ctx, v) if err != nil { return it, err } @@ -17435,7 +17488,7 @@ func (ec *executionContext) unmarshalInputPersistNamespaceSourceInput(ctx contex it.Kind = data case "selected": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("selected")) - data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) + data, err := ec.unmarshalNBoolean2bool(ctx, v) if err != nil { return it, err } @@ -19281,6 +19334,8 @@ func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.Sel if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } + case "selected": + out.Values[i] = ec._K8sActualNamespace_selected(ctx, field, obj) case "k8sActualSources": field := field diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index bf9bf142b..3e2b61810 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -297,6 +297,7 @@ type InstrumentationRuleInput struct { type K8sActualNamespace struct { Name string `json:"name"` + Selected *bool `json:"selected,omitempty"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` } @@ -418,13 +419,13 @@ type PayloadCollectionInput struct { type PersistNamespaceItemInput struct { Name string `json:"name"` - FutureSelected *bool `json:"futureSelected,omitempty"` + FutureSelected bool `json:"futureSelected"` } type PersistNamespaceSourceInput struct { Name string `json:"name"` Kind K8sResourceKind `json:"kind"` - Selected *bool `json:"selected,omitempty"` + Selected bool `json:"selected"` } type PiiMaskingAction struct { diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 602e4440f..d59133ada 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -61,6 +61,7 @@ type Condition { type K8sActualNamespace { name: String! + selected: Boolean k8sActualSources: [K8sActualSource]! } @@ -282,13 +283,13 @@ input ExportedSignalsInput { input PersistNamespaceItemInput { name: String! - futureSelected: Boolean + futureSelected: Boolean! } input PersistNamespaceSourceInput { name: String! kind: K8sResourceKind! - selected: Boolean + selected: Boolean! } type TestConnectionResponse { diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 5c341760e..541ebae81 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -351,7 +351,7 @@ func (r *mutationResolver) CreateNewDestination(ctx context.Context, destination // PersistK8sNamespace is the resolver for the persistK8sNamespace field. func (r *mutationResolver) PersistK8sNamespace(ctx context.Context, namespace model.PersistNamespaceItemInput) (bool, error) { - jsonMergePayload := services.GetJsonMergePatchForInstrumentationLabel(namespace.FutureSelected) + jsonMergePayload := services.GetJsonMergePatchForInstrumentationLabel(&namespace.FutureSelected) _, err := kube.DefaultClient.CoreV1().Namespaces().Patch(ctx, namespace.Name, types.MergePatchType, jsonMergePayload, metav1.PatchOptions{}) if err != nil { return false, fmt.Errorf("failed to patch namespace: %v", err) diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 7b4e34a62..7c8c22ca2 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -179,11 +179,7 @@ func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []mo for _, workload := range workloads { currWorkload := workload g.Go(func() error { - // Only instrument/uninstrument selected workloads, ignore the rest - if currWorkload.Selected != nil { - return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), currWorkload.Selected) - } - return nil + return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), &currWorkload.Selected) }) } diff --git a/frontend/webapp/assets/icons/common/code-brackets-icon.tsx b/frontend/webapp/assets/icons/common/code-brackets-icon.tsx new file mode 100644 index 000000000..431639054 --- /dev/null +++ b/frontend/webapp/assets/icons/common/code-brackets-icon.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { SVG } from '@/assets'; +import theme from '@/styles/theme'; + +export const CodeBracketsIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate = 0, onClick }) => { + return ( + + + + ); +}; diff --git a/frontend/webapp/assets/icons/common/code-icon.tsx b/frontend/webapp/assets/icons/common/code-icon.tsx index b5d44d3a4..c80eaa521 100644 --- a/frontend/webapp/assets/icons/common/code-icon.tsx +++ b/frontend/webapp/assets/icons/common/code-icon.tsx @@ -9,7 +9,7 @@ export const CodeIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate = stroke={fill} strokeLinecap='round' strokeLinejoin='round' - d='M5.33333 2.66669C4.22876 2.66669 3.33333 3.46263 3.33333 4.44446V6.22224C3.33333 7.20408 2.4379 8.00002 1.33333 8.00002C2.4379 8.00002 3.33333 8.79596 3.33333 9.7778V11.5556C3.33333 12.5374 4.22876 13.3334 5.33333 13.3334M10.6667 2.66669C11.7712 2.66669 12.6667 3.46263 12.6667 4.44446V6.22224C12.6667 7.20408 13.5621 8.00002 14.6667 8.00002C13.5621 8.00002 12.6667 8.79596 12.6667 9.7778V11.5556C12.6667 12.5374 11.7712 13.3334 10.6667 13.3334' + d='M11.334 12a18.802 18.802 0 0 0 3.231-3.66.62.62 0 0 0 0-.68A18.8 18.8 0 0 0 11.334 4m-6.665 8a18.803 18.803 0 0 1-3.231-3.66.62.62 0 0 1 0-.68A18.801 18.801 0 0 1 4.669 4m4.667-1.332L6.669 13.335' /> ); diff --git a/frontend/webapp/assets/icons/common/index.ts b/frontend/webapp/assets/icons/common/index.ts index 56406ac10..927b05909 100644 --- a/frontend/webapp/assets/icons/common/index.ts +++ b/frontend/webapp/assets/icons/common/index.ts @@ -3,6 +3,7 @@ export * from './avatar-icon'; export * from './check-circled-icon'; export * from './check-icon'; export * from './cross-circled-icon'; +export * from './code-brackets-icon'; export * from './code-icon'; export * from './cross-icon'; export * from './edit-icon'; @@ -14,6 +15,7 @@ export * from './eye-open-icon'; export * from './filter-icon'; export * from './folder-icon'; export * from './info-icon'; +export * from './list-icon'; export * from './no-data-icon'; export * from './notebook-icon'; export * from './notification-icon'; diff --git a/frontend/webapp/assets/icons/common/list-icon.tsx b/frontend/webapp/assets/icons/common/list-icon.tsx new file mode 100644 index 000000000..6e9045dc0 --- /dev/null +++ b/frontend/webapp/assets/icons/common/list-icon.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { SVG } from '@/assets'; +import theme from '@/styles/theme'; + +export const ListIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate = 0, onClick }) => { + return ( + + + + ); +}; diff --git a/frontend/webapp/components/common/buttons/index.ts b/frontend/webapp/components/common/buttons/index.ts new file mode 100644 index 000000000..b38aaa54e --- /dev/null +++ b/frontend/webapp/components/common/buttons/index.ts @@ -0,0 +1 @@ +export * from './toggle-code-component'; diff --git a/frontend/webapp/components/common/buttons/toggle-code-component/index.tsx b/frontend/webapp/components/common/buttons/toggle-code-component/index.tsx new file mode 100644 index 000000000..acc553627 --- /dev/null +++ b/frontend/webapp/components/common/buttons/toggle-code-component/index.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { FlexRow } from '@/styles'; +import styled from 'styled-components'; +import { CodeIcon, ListIcon } from '@/assets'; + +interface Props { + isCodeMode: boolean; + setIsCodeMode: (isCodeMode: boolean) => void; +} + +const Container = styled(FlexRow)` + gap: 0; +`; + +const Button = styled.button<{ $position: 'left' | 'right'; $selected: boolean }>` + padding: 4px 8px; + background-color: ${({ theme, $selected }) => ($selected ? theme.colors.white_opacity['008'] : 'transparent')}; + border-radius: ${({ $position }) => ($position === 'left' ? '32px 0px 0px 32px' : $position === 'right' ? '0px 32px 32px 0px' : '0')}; + border: 1px solid ${({ theme }) => theme.colors.border}; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + &:hover { + border: 1px solid ${({ theme }) => theme.colors.secondary}; + } +`; + +export const ToggleCodeComponent: React.FC = ({ isCodeMode, setIsCodeMode }) => { + return ( + + + + + ); +}; diff --git a/frontend/webapp/components/common/index.ts b/frontend/webapp/components/common/index.ts index 00da52a3e..d656b740a 100644 --- a/frontend/webapp/components/common/index.ts +++ b/frontend/webapp/components/common/index.ts @@ -1 +1,2 @@ +export * from './buttons'; export * from './dropdowns'; diff --git a/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx b/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx index 0b98ebdaf..a5d1702ff 100644 --- a/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx +++ b/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx @@ -1,8 +1,9 @@ -import React from 'react'; -import { CodeIcon } from '@/assets'; +import React, { useState } from 'react'; import styled from 'styled-components'; +import { CodeBracketsIcon } from '@/assets'; import { useDescribeOdigos } from '@/hooks'; import { DATA_CARDS, safeJsonStringify } from '@/utils'; +import { ToggleCodeComponent } from '@/components/common'; import { DataCard, DataCardFieldTypes } from '@/reuseable-components'; import OverviewDrawer from '@/containers/main/overview/overview-drawer'; @@ -15,18 +16,24 @@ const DataContainer = styled.div` `; export const DescribeDrawer: React.FC = () => { - const { data: describe } = useDescribeOdigos(); + const { data: describe, restructureForPrettyMode } = useDescribeOdigos(); + const [isCodeMode, setIsCodeMode] = useState(false); return ( - + } data={[ { type: DataCardFieldTypes.CODE, + value: JSON.stringify({ + language: 'json', + code: safeJsonStringify(isCodeMode ? describe : restructureForPrettyMode(describe)), + pretty: !isCodeMode, + }), width: 'inherit', - value: JSON.stringify({ language: 'json', code: safeJsonStringify(describe) }), }, ]} /> diff --git a/frontend/webapp/containers/main/overview/overview-drawer/index.tsx b/frontend/webapp/containers/main/overview/overview-drawer/index.tsx index e9986aafd..7dd4e6853 100644 --- a/frontend/webapp/containers/main/overview/overview-drawer/index.tsx +++ b/frontend/webapp/containers/main/overview/overview-drawer/index.tsx @@ -5,8 +5,8 @@ import DrawerFooter from './drawer-footer'; import { Drawer } from '@/reuseable-components'; import DrawerHeader, { DrawerHeaderRef } from './drawer-header'; import { CancelWarning, DeleteWarning } from '@/components/modals'; +import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES } from '@/types'; import { useDestinationCRUD, useKeyDown, useSourceCRUD } from '@/hooks'; -import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, WorkloadId } from '@/types'; import { useDrawerStore, useNotificationStore, usePendingStore } from '@/store'; const DRAWER_WIDTH = `${640 + 64}px`; // +64 because of "ContentArea" padding diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx index 6072e15c4..58c8d5383 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-source-modal/index.tsx @@ -16,9 +16,9 @@ export const AddSourceModal: React.FC = ({ isOpen, onClose }) => { const { persistSources } = useSourceCRUD({ onSuccess: onClose }); const handleSubmit = async () => { - const { getApiPaylod, selectedFutureApps } = menuState; + const { getApiSourcesPayload, getApiFutureAppsPayload } = menuState; - // Type of "getApiPaylod()" is actually: + // Type of "getApiSourcesPayload()" is actually: // { [namespace: string]: Pick[] }; // // But we will force it as type: @@ -27,7 +27,7 @@ export const AddSourceModal: React.FC = ({ isOpen, onClose }) => { // This forced type is to satisfy TypeScript, // while knowing that this doesn't break the onboarding flow in any-way... - await persistSources(getApiPaylod() as IAppState['configuredSources'], selectedFutureApps); + await persistSources(getApiSourcesPayload() as IAppState['configuredSources'], getApiFutureAppsPayload()); }; return ( diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx index 820715512..3374cd0dc 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/sources-list/index.tsx @@ -109,7 +109,7 @@ export const SourcesList: React.FC = ({ const onlySelectedSources = sourcesForNamespace.filter(({ selected }) => selected); const filteredSources = filterSources(namespace, { cancelSearch: true }); - const isNamespaceAllSourcesSelected = isNamespaceLoaded && onlySelectedSources.length === sources.length; + const isNamespaceAllSourcesSelected = !!onlySelectedSources.length && onlySelectedSources.length === sources.length; const hasFilteredSources = !!filteredSources.length; return ( diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/source-controls/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/source-controls/index.tsx index 2f441c50b..5745c1774 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/source-controls/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-simple/source-controls/index.tsx @@ -35,6 +35,7 @@ export const SourceControls: React.FC = ({ showSelectedOnly, setShowSelectedOnly, }) => { + const futureApps = selectedFutureApps[selectedNamespace] || false; const sources = selectedSources[selectedNamespace] || []; const selectedAppsCount = sources.filter(({ selected }) => selected).length; @@ -61,9 +62,9 @@ export const SourceControls: React.FC = ({ - + - + diff --git a/frontend/webapp/containers/main/sources/choose-sources/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/index.tsx index 447c923b9..b919ab1d2 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/index.tsx @@ -18,10 +18,10 @@ export function ChooseSourcesContainer() { const menuState = useSourceFormData(); const onNext = () => { - const { recordedInitialValues, getApiPaylod, selectedFutureApps } = menuState; + const { recordedInitialSources, getApiSourcesPayload, getApiFutureAppsPayload } = menuState; const { setAvailableSources, setConfiguredSources, setConfiguredFutureApps } = appState; - // Types of "recordedInitialValues" and "getApiPaylod()" are actually: + // Types of "recordedInitialSources" and "getApiSourcesPayload()" are actually: // { [namespace: string]: Pick[] }; // // But we will force them as type: @@ -30,9 +30,9 @@ export function ChooseSourcesContainer() { // This forced type is to satisfy TypeScript, // while knowing that this doesn't break the onboarding flow in any-way... - setAvailableSources(recordedInitialValues as IAppState['availableSources']); - setConfiguredSources(getApiPaylod() as IAppState['configuredSources']); - setConfiguredFutureApps(selectedFutureApps); + setAvailableSources(recordedInitialSources as IAppState['availableSources']); + setConfiguredSources(getApiSourcesPayload() as IAppState['configuredSources']); + setConfiguredFutureApps(getApiFutureAppsPayload()); router.push(ROUTES.CHOOSE_DESTINATION); }; diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx index 5e23efabb..3e710b159 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -3,6 +3,7 @@ import buildCard from './build-card'; import styled from 'styled-components'; import { useDrawerStore } from '@/store'; import buildDrawerItem from './build-drawer-item'; +import { ToggleCodeComponent } from '@/components'; import { UpdateSourceBody } from '../update-source-body'; import { useDescribeSource, useSourceCRUD } from '@/hooks'; import OverviewDrawer from '../../overview/overview-drawer'; @@ -49,6 +50,7 @@ export const SourceDrawer: React.FC = () => { }, }); + const [isCodeMode, setIsCodeMode] = useState(false); // for "describe source" const [isEditing, setIsEditing] = useState(false); const [isFormDirty, setIsFormDirty] = useState(false); const [formData, setFormData] = useState({ ...EMPTY_FORM }); @@ -99,7 +101,7 @@ export const SourceDrawer: React.FC = () => { if (!selectedItem?.item) return null; const { id, item } = selectedItem as { id: WorkloadId; item: K8sActualSource }; - const { data: describe } = useDescribeSource(id); + const { data: describe, restructureForPrettyMode } = useDescribeSource(id); const handleEdit = (bool?: boolean) => { setIsEditing(typeof bool === 'boolean' ? bool : true); @@ -151,11 +153,16 @@ export const SourceDrawer: React.FC = () => { } data={[ { type: DataCardFieldTypes.CODE, + value: JSON.stringify({ + language: 'json', + code: safeJsonStringify(isCodeMode ? describe : restructureForPrettyMode(describe)), + pretty: !isCodeMode, + }), width: 'inherit', - value: JSON.stringify({ language: 'json', code: safeJsonStringify(describe) }), }, ]} /> diff --git a/frontend/webapp/cypress/functions/index.ts b/frontend/webapp/cypress/functions/index.ts index 15f4e3c18..20074ce87 100644 --- a/frontend/webapp/cypress/functions/index.ts +++ b/frontend/webapp/cypress/functions/index.ts @@ -81,7 +81,6 @@ interface DeleteEntityOptions { export const deleteEntity = ({ nodeId, nodeContains, warnModalTitle, warnModalNote }: DeleteEntityOptions, callback?: () => void) => { cy.contains(nodeId, nodeContains).should('exist').click(); cy.get(DATA_IDS.DRAWER).should('exist'); - cy.get(DATA_IDS.DRAWER_EDIT).click(); cy.get(DATA_IDS.DRAWER_DELETE).click(); if (!!warnModalTitle) cy.get(DATA_IDS.MODAL).contains(warnModalTitle).should('exist'); diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index c02fd80dd..f39a4fe59 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -5,6 +5,7 @@ export const GET_COMPUTE_PLATFORM = gql` computePlatform { k8sActualNamespaces { name + selected } k8sActualSources { namespace @@ -107,6 +108,7 @@ export const GET_NAMESPACES = gql` computePlatform { k8sActualNamespace(name: $namespaceName) { name + selected k8sActualSources { kind name diff --git a/frontend/webapp/graphql/queries/describe.ts b/frontend/webapp/graphql/queries/describe.ts index cfd09670e..8c76ddb9d 100644 --- a/frontend/webapp/graphql/queries/describe.ts +++ b/frontend/webapp/graphql/queries/describe.ts @@ -189,12 +189,6 @@ export const DESCRIBE_SOURCE = gql` } } runtimeInfo { - generation { - name - value - status - explain - } containers { containerName { name diff --git a/frontend/webapp/hooks/describe/useDescribeOdigos.ts b/frontend/webapp/hooks/describe/useDescribeOdigos.ts index 74b90199e..00c060177 100644 --- a/frontend/webapp/hooks/describe/useDescribeOdigos.ts +++ b/frontend/webapp/hooks/describe/useDescribeOdigos.ts @@ -7,9 +7,41 @@ export const useDescribeOdigos = () => { pollInterval: 5000, }); + // This function is used to restructure the data, so that it reflects the output given by "odigos describe" command in the CLI. + // This is not really needed, but it's a nice-to-have feature to make the data more readable. + const restructureForPrettyMode = (code?: DescribeOdigos['describeOdigos']) => { + if (!code) return {}; + + const payload: Record = { + [code.odigosVersion.name]: code.odigosVersion.value, + 'Number Of Sources': code.numberOfSources, + 'Number Of Destinations': code.numberOfDestinations, + }; + + const mapObjects = (obj: any, objectName: string) => { + if (typeof obj === 'object' && !!obj?.name) { + let key = obj.name; + let val = obj.value; + + if (obj.explain) key += `@tooltip=${obj.explain}`; + if (obj.status) val += `@status=${obj.status}`; + else val += '@status=none'; + + if (!payload[objectName]) payload[objectName] = {}; + payload[objectName][key] = val; + } + }; + + Object.values(code.clusterCollector).forEach((val) => mapObjects(val, 'Cluster Collector')); + Object.values(code.nodeCollector).forEach((val) => mapObjects(val, 'Node Collector')); + + return payload; + }; + return { - data: data?.describeOdigos, loading, error, + data: data?.describeOdigos, + restructureForPrettyMode, }; }; diff --git a/frontend/webapp/hooks/describe/useDescribeSource.ts b/frontend/webapp/hooks/describe/useDescribeSource.ts index 209e92794..499444907 100644 --- a/frontend/webapp/hooks/describe/useDescribeSource.ts +++ b/frontend/webapp/hooks/describe/useDescribeSource.ts @@ -8,9 +8,51 @@ export const useDescribeSource = ({ namespace, name, kind }: WorkloadId) => { pollInterval: 5000, }); + // This function is used to restructure the data, so that it reflects the output given by "odigos describe" command in the CLI. + // This is not really needed, but it's a nice-to-have feature to make the data more readable. + const restructureForPrettyMode = (code?: DescribeSource['describeSource']) => { + if (!code) return {}; + + const payload: Record = {}; + + const mapObjects = (obj: any, category?: string, options?: { keyPrefix?: string }) => { + if (typeof obj === 'object' && !!obj?.name) { + let key = options?.keyPrefix ? `${options?.keyPrefix}${obj.name}` : obj.name; + let val = obj.value; + + if (obj.explain) key += `@tooltip=${obj.explain}`; + if (obj.status) val += `@status=${obj.status}`; + else val += '@status=none'; + + if (!!category && !payload[category]) payload[category] = {}; + if (!!category) payload[category][key] = val; + else payload[key] = val; + } + }; + + Object.values(code).forEach((val) => mapObjects(val)); + Object.values(code.labels).forEach((val) => mapObjects(val, 'Labels')); + Object.values(code.instrumentationConfig).forEach((val) => mapObjects(val, 'Instrumentation Config')); + code.runtimeInfo?.containers.forEach((obj, i) => Object.values(obj).forEach((val) => mapObjects(val, 'Runtime Info', { keyPrefix: `Container #${i + 1} - ` }))); + Object.values(code.instrumentationDevice).forEach((val) => mapObjects(val, 'Instrumentation Device')); + code.instrumentationDevice?.containers.forEach((obj, i) => Object.values(obj).forEach((val) => mapObjects(val, 'Instrumentation Device', { keyPrefix: `Container #${i + 1} - ` }))); + + payload['Pods'] = { 'Total Pods': `${code.totalPods}@status=none` }; + code.pods.forEach((obj) => { + Object.values(obj).forEach((val) => mapObjects(val, 'Pods')); + obj.containers.forEach((containers, i) => { + Object.values(containers).forEach((val) => mapObjects(val, 'Pods', { keyPrefix: `Container #${i + 1} - ` })); + containers.instrumentationInstances.forEach((obj, i) => Object.values(obj).forEach((val) => mapObjects(val, 'Pods', { keyPrefix: `Instrumentation Instance #${i + 1} - ` }))); + }); + }); + + return payload; + }; + return { - data: data?.describeSource, loading, error, + data: data?.describeSource, + restructureForPrettyMode, }; }; diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 1ffc5de54..11a69ac66 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -84,6 +84,9 @@ export const useSourceCRUD = (params?: Params) => { persistSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Persisting sources...', undefined, true); + // this is to handle "on success" callback if there are no sources to persist + let hasSources = false; + for (const [namespace, sources] of Object.entries(selectAppsList)) { const addToPendingStore: PendingItem[] = []; const sendToGql: Pick[] = []; @@ -93,6 +96,8 @@ export const useSourceCRUD = (params?: Params) => { sendToGql.push({ name, kind, selected }); }); + if (!!sendToGql.length) hasSources = true; + addPendingItems(addToPendingStore); await createOrDeleteSources({ variables: { namespace, sources: sendToGql } }); } @@ -100,6 +105,8 @@ export const useSourceCRUD = (params?: Params) => { for (const [namespace, futureSelected] of Object.entries(futureSelectAppsList)) { await persistNamespace({ name: namespace, futureSelected }); } + + if (!hasSources) handleComplete(''); }, updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { diff --git a/frontend/webapp/hooks/sources/useSourceFormData.ts b/frontend/webapp/hooks/sources/useSourceFormData.ts index 428d1a9e4..54a71e894 100644 --- a/frontend/webapp/hooks/sources/useSourceFormData.ts +++ b/frontend/webapp/hooks/sources/useSourceFormData.ts @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react'; +import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react'; import { useAppStore } from '@/store'; import type { K8sActualSource } from '@/types'; import { useNamespace } from '../compute-platform'; @@ -12,10 +12,11 @@ interface UseSourceFormDataParams { export interface UseSourceFormDataResponse { namespacesLoading: boolean; - recordedInitialValues: { [namespace: SelectedNamespace]: SelectedSource[] }; + recordedInitialSources: { [namespace: SelectedNamespace]: SelectedSource[] }; filterNamespaces: (options?: { cancelSearch?: boolean }) => [SelectedNamespace, SelectedSource[]][]; filterSources: (namespace?: SelectedNamespace, options?: { cancelSearch?: boolean; cancelSelected?: boolean }) => SelectedSource[]; - getApiPaylod: () => { [namespace: SelectedNamespace]: SelectedSource[] }; + getApiSourcesPayload: () => { [namespace: SelectedNamespace]: SelectedSource[] }; + getApiFutureAppsPayload: () => { [namespace: SelectedNamespace]: boolean }; selectedNamespace: SelectedNamespace; onSelectNamespace: (namespace: SelectedNamespace) => void; @@ -46,12 +47,12 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo const { allNamespaces, data: singleNamespace, loading: namespacesLoading } = useNamespace(selectedNamespace); // Keeps intial values fetched from API, so we can later filter the user-specific-selections, therebey minimizing the amount of data sent to the API on "persist sources". - const [recordedInitialValues, setRecordedInitialValues] = useState(appStore.availableSources); + const [recordedInitialSources, setRecordedInitialSources] = useState(appStore.availableSources); useEffect(() => { if (!!allNamespaces?.length) { // initialize all states (to avoid undefined errors) - setRecordedInitialValues((prev) => { + setRecordedInitialSources((prev) => { const payload = { ...prev }; allNamespaces.forEach(({ name }) => (payload[name] = payload[name] || [])); return payload; @@ -63,7 +64,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo }); setSelectedFutureApps((prev) => { const payload = { ...prev }; - allNamespaces.forEach(({ name }) => (payload[name] = payload[name] || false)); + allNamespaces.forEach(({ name, selected }) => (payload[name] = payload[name] || selected || false)); return payload; }); // auto-select the 1st namespace @@ -75,7 +76,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo if (!!singleNamespace) { // initialize sources for this namespace const { name, k8sActualSources = [] } = singleNamespace; - setRecordedInitialValues((prev) => ({ ...prev, [name]: k8sActualSources })); + setRecordedInitialSources((prev) => ({ ...prev, [name]: k8sActualSources })); setSelectedSources((prev) => ({ ...prev, [name]: !!prev[name].length ? prev[name] : k8sActualSources })); } }, [singleNamespace]); @@ -174,12 +175,13 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo return selectedSources[id].filter((source) => isSearchOk(source.name) && isOnlySelectedOk(selectedSources[id], 'name', source.name)); }; - const getApiPaylod: UseSourceFormDataResponse['getApiPaylod'] = () => { + // This is to filter the user-specific-selections, therebey minimizing the amount of data sent to the API on "persist sources". + const getApiSourcesPayload: UseSourceFormDataResponse['getApiSourcesPayload'] = () => { const payload: UseSourceFormDataResponse['selectedSources'] = {}; Object.entries(selectedSources).forEach(([namespace, sources]) => { sources.forEach((source) => { - const foundInitial = recordedInitialValues[namespace]?.find((initialSource) => initialSource.name === source.name && initialSource.kind === source.kind); + const foundInitial = recordedInitialSources[namespace]?.find((initialSource) => initialSource.name === source.name && initialSource.kind === source.kind); if (foundInitial?.selected !== source.selected) { if (!payload[namespace]) payload[namespace] = []; @@ -191,12 +193,28 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo return payload; }; + // This is to filter the user-specific-selections, therebey minimizing the amount of data sent to the API on "persist namespaces". + const getApiFutureAppsPayload: UseSourceFormDataResponse['getApiFutureAppsPayload'] = () => { + const payload: UseSourceFormDataResponse['selectedFutureApps'] = {}; + + Object.entries(selectedFutureApps).forEach(([namespace, selected]) => { + const foundInitial = allNamespaces?.find((ns) => ns.name === namespace); + + if (foundInitial?.selected !== selected) { + payload[namespace] = selected; + } + }); + + return payload; + }; + return { namespacesLoading, - recordedInitialValues, + recordedInitialSources, filterNamespaces, filterSources, - getApiPaylod, + getApiSourcesPayload, + getApiFutureAppsPayload, selectedNamespace, onSelectNamespace, diff --git a/frontend/webapp/package.json b/frontend/webapp/package.json index 39b8b08dd..74cf14b26 100644 --- a/frontend/webapp/package.json +++ b/frontend/webapp/package.json @@ -36,5 +36,6 @@ "eslint-config-next": "15.0.3", "postcss": "^8.4.49", "typescript": "5.6.3" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/frontend/webapp/reuseable-components/code/index.tsx b/frontend/webapp/reuseable-components/code/index.tsx index e6268d9de..e49e1c4be 100644 --- a/frontend/webapp/reuseable-components/code/index.tsx +++ b/frontend/webapp/reuseable-components/code/index.tsx @@ -1,22 +1,45 @@ -import { useMemo } from 'react'; +import React, { Fragment, useId, useMemo } from 'react'; +import { Text } from '../text'; +import { FlexRow } from '@/styles'; +import theme from '@/styles/theme'; +import { Tooltip } from '../tooltip'; import styled from 'styled-components'; -import { Highlight, themes as prismThemes } from 'prism-react-renderer'; -import { flattenObjectKeys, removeEmptyValuesFromObject, safeJsonParse, safeJsonStringify } from '@/utils'; +import { Highlight, themes as prismThemes, type Token } from 'prism-react-renderer'; +import { flattenObjectKeys, getStatusIcon, removeEmptyValuesFromObject, safeJsonParse, safeJsonStringify } from '@/utils'; +import { NOTIFICATION_TYPE } from '@/types'; interface Props { language: string; code: string; flatten?: boolean; + pretty?: boolean; } -const Token = styled.span` - white-space: pre-wrap; +const Table = styled.table` + border-collapse: collapse; + font-family: ${({ theme }) => theme.font_family.primary}; +`; + +const TableBody = styled.tbody``; + +const TableRow = styled.tr``; + +const TableData = styled.td` + vertical-align: top; + padding: 4px 6px; +`; + +const Title = styled(Text)` + white-space: nowrap; +`; + +const CodeLineToken = styled.span<{ $noWrap?: boolean }>` + white-space: ${({ $noWrap }) => ($noWrap ? 'nowrap' : 'pre-wrap')}; overflow-wrap: break-word; font-size: 12px; - font-family: ${({ theme }) => theme.font_family.code}; `; -export const Code: React.FC = ({ language, code, flatten }) => { +export const Code: React.FC = ({ language, code, flatten, pretty }) => { const str = useMemo(() => { if (language === 'json') { const obj = safeJsonParse(code, {}); @@ -29,6 +52,10 @@ export const Code: React.FC = ({ language, code, flatten }) => { return code; }, [code, language, flatten]); + if (pretty && language === 'json') { + return ; + } + return ( {({ getLineProps, getTokenProps, tokens }) => ( @@ -36,7 +63,7 @@ export const Code: React.FC = ({ language, code, flatten }) => { {tokens.map((line, i) => (
{line.map((token, ii) => ( - + ))}
))} @@ -45,3 +72,120 @@ export const Code: React.FC = ({ language, code, flatten }) => {
); }; + +const formatLineForPrettyMode = (line: Token[]) => { + const ignoreTypes = ['punctuation', 'plain', 'operator']; + + return line + .filter((token) => !ignoreTypes.includes(token.types[0])) + .map(({ types, content }) => { + const updatedTypes = [...types]; + const updatedContent = ['property', 'string'].includes(updatedTypes[0]) ? content.replace(/"/g, '') : content; + + // override types for prettier colors + if (updatedTypes[0] === 'string') { + if (['true', 'false'].includes(updatedContent.split('@')[0])) updatedTypes[0] = 'boolean'; + if (updatedContent.split('@')[0].match(/^[0-9]+$/)) updatedTypes[0] = 'number'; + } + + return { + types: updatedTypes, + content: updatedContent, + }; + }); +}; + +const getComponentsFromPropertyString = (propertyString: string) => { + const [text, ...rest] = propertyString.split('@'); + const components = + rest + ?.map((c) => { + if (!c.includes('=')) return null; + const [type, value] = c.split('='); + + switch (type) { + case 'tooltip': + return ; + case 'status': + if (value === 'none') return
; + let Icon = getStatusIcon(value as NOTIFICATION_TYPE); + if (!Icon) Icon = getStatusIcon(NOTIFICATION_TYPE.WARNING); + return ; + default: + console.warn('unexpected component type!', type); + return null; + } + }) + ?.filter((c) => !!c) || []; + + return { text, components }; +}; + +const PrettyJsonCode: React.FC<{ str: string }> = ({ str }) => { + const renderEmptyRows = (count: number = 2) => { + const rows = new Array(count).fill((props) => ( + + + + + )); + + return ( + + {rows.map((R, i) => ( + + ))} + + ); + }; + + return ( + + {({ getLineProps, getTokenProps, tokens }) => ( + + + {tokens.map((line, i) => { + const formattedLine = formatLineForPrettyMode(line); + const lineProps = getLineProps({ line: formattedLine }); + + if (formattedLine.length === 1 && formattedLine[0].types[0] === 'property') { + return ( + + {renderEmptyRows()} + + + {formattedLine[0].content} + + + + + ); + } else if (formattedLine.length === 2) { + return ( + + {formattedLine.map((token, ii) => { + const { text, components } = getComponentsFromPropertyString(token.content); + const isRowTitle = ii === 0; + + return ( + + + {...components} + {text} + + + ); + })} + + ); + } else { + if (!!formattedLine.length) console.warn('this line is unexpected!', i, formattedLine); + return null; + } + })} + +
+ )} +
+ ); +}; diff --git a/frontend/webapp/reuseable-components/data-card/index.tsx b/frontend/webapp/reuseable-components/data-card/index.tsx index 6c7788944..35ef75e88 100644 --- a/frontend/webapp/reuseable-components/data-card/index.tsx +++ b/frontend/webapp/reuseable-components/data-card/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { Badge, Text } from '@/reuseable-components'; import { DataCardFields, type DataCardRow, DataCardFieldTypes } from './data-card-fields'; +import { FlexRow } from '@/styles'; export { DataCardFields, type DataCardRow, DataCardFieldTypes }; interface Props { @@ -9,6 +10,7 @@ interface Props { titleBadge?: string | number; description?: string; data: DataCardRow[]; + action?: React.ReactNode; } const CardContainer = styled.div` @@ -23,12 +25,14 @@ const CardContainer = styled.div` `; const Header = styled.div` + width: 100%; display: flex; flex-direction: column; gap: 4px; `; const Title = styled(Text)` + width: 100%; display: flex; align-items: center; gap: 8px; @@ -40,16 +44,21 @@ const Description = styled(Text)` color: ${({ theme }) => theme.text.grey}; `; -export const DataCard: React.FC = ({ title = 'Details', titleBadge, description, data }) => { +const ActionWrapper = styled.div` + margin-left: auto; +`; + +export const DataCard: React.FC = ({ title = 'Details', titleBadge, description, data, action }) => { return ( - {!!title || !!description ? ( + {!!title || !!description || !!action ? (
- {!!title && ( + {(!!title || !!action) && ( {title} {/* NOT undefined, because we should allow zero (0) values */} {titleBadge !== undefined && <Badge label={titleBadge} />} + <ActionWrapper>{action}</ActionWrapper> )} diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index 9808d3545..327fb31f7 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -5,6 +5,7 @@ import type { InstrumentationRuleSpec, InstrumentationRuleSpecMapped } from './i export type K8sActualNamespace = { name: string; + selected: boolean; k8sActualSources?: K8sActualSource[]; }; diff --git a/helm/odigos/templates/ui/clusterrole.yaml b/helm/odigos/templates/ui/clusterrole.yaml index 7c5484cc5..6e0cd5181 100644 --- a/helm/odigos/templates/ui/clusterrole.yaml +++ b/helm/odigos/templates/ui/clusterrole.yaml @@ -52,6 +52,7 @@ rules: verbs: - get - list + - watch - apiGroups: - odigos.io resources: @@ -61,10 +62,3 @@ rules: - list - create - delete - - apiGroups: - - odigos.io - resources: - - instrumentationconfigs - - instrumentationinstances - verbs: - - watch diff --git a/instrumentor/controllers/instrumentationconfig/common.go b/instrumentor/controllers/instrumentationconfig/common.go index 22bed59d6..5a84a277e 100644 --- a/instrumentor/controllers/instrumentationconfig/common.go +++ b/instrumentor/controllers/instrumentationconfig/common.go @@ -12,24 +12,24 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func updateInstrumentationConfigForWorkload(ic *odigosv1alpha1.InstrumentationConfig, ia *odigosv1alpha1.InstrumentedApplication, rules *odigosv1alpha1.InstrumentationRuleList, serviceName string) error { +func updateInstrumentationConfigForWorkload(ic *odigosv1alpha1.InstrumentationConfig, rules *odigosv1alpha1.InstrumentationRuleList, serviceName string) error { - workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(ia.Name) + workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(ic.Name) if err != nil { return err } workload := workload.PodWorkload{ Name: workloadName, - Namespace: ia.Namespace, + Namespace: ic.Namespace, Kind: workloadKind, } ic.Spec.ServiceName = serviceName - sdkConfigs := make([]odigosv1alpha1.SdkConfig, 0, len(ia.Spec.RuntimeDetails)) + sdkConfigs := make([]odigosv1alpha1.SdkConfig, 0, len(ic.Status.RuntimeDetailsByContainer)) // create an empty sdk config for each detected programming language - for _, container := range ia.Spec.RuntimeDetails { + for _, container := range ic.Status.RuntimeDetailsByContainer { containerLanguage := container.Language if containerLanguage == common.IgnoredProgrammingLanguage || containerLanguage == common.UnknownProgrammingLanguage { continue diff --git a/instrumentor/controllers/instrumentationconfig/common_test.go b/instrumentor/controllers/instrumentationconfig/common_test.go index c5bede439..1b4c591cb 100644 --- a/instrumentor/controllers/instrumentationconfig/common_test.go +++ b/instrumentor/controllers/instrumentationconfig/common_test.go @@ -11,28 +11,23 @@ import ( ) 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, - }}, + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ + { + ContainerName: "test-container", + Language: common.JavascriptProgrammingLanguage, + }, + }, }, } rules := &odigosv1.InstrumentationRuleList{} - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -45,21 +40,14 @@ func TestUpdateInstrumentationConfigForWorkload_SingleLanguage(t *testing.T) { } 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{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container-1", Language: common.JavascriptProgrammingLanguage, @@ -72,7 +60,7 @@ func TestUpdateInstrumentationConfigForWorkload_MultipleLanguages(t *testing.T) }, } rules := &odigosv1.InstrumentationRuleList{} - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -95,14 +83,8 @@ func TestUpdateInstrumentationConfigForWorkload_IgnoreUnknownLanguage(t *testing Namespace: "testns", }, Spec: odigosv1.InstrumentationConfigSpec{}, - } - ia := odigosv1.InstrumentedApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment-test", - Namespace: "testns", - }, - Spec: odigosv1.InstrumentedApplicationSpec{ - RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container-1", Language: common.JavascriptProgrammingLanguage, @@ -119,7 +101,7 @@ func TestUpdateInstrumentationConfigForWorkload_IgnoreUnknownLanguage(t *testing }, } rules := &odigosv1.InstrumentationRuleList{} - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -139,18 +121,12 @@ func TestUpdateInstrumentationConfigForWorkload_NoLanguages(t *testing.T) { Namespace: "testns", }, Spec: odigosv1.InstrumentationConfigSpec{}, - } - ia := odigosv1.InstrumentedApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment-test", - Namespace: "testns", - }, - Spec: odigosv1.InstrumentedApplicationSpec{ - RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{}, + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{}, }, } rules := &odigosv1.InstrumentationRuleList{} - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -160,21 +136,14 @@ func TestUpdateInstrumentationConfigForWorkload_NoLanguages(t *testing.T) { } 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{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container-1", Language: common.JavascriptProgrammingLanguage, @@ -187,7 +156,7 @@ func TestUpdateInstrumentationConfigForWorkload_SameLanguageMultipleContainers(t }, } rules := &odigosv1.InstrumentationRuleList{} - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -200,21 +169,14 @@ func TestUpdateInstrumentationConfigForWorkload_SameLanguageMultipleContainers(t } 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{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container", Language: common.JavascriptProgrammingLanguage, @@ -237,7 +199,7 @@ func TestUpdateInstrumentationConfigForWorkload_SingleMatchingRule(t *testing.T) }, }, } - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -266,14 +228,8 @@ func TestUpdateInstrumentationConfigForWorkload_InWorkloadList(t *testing.T) { Namespace: "testns", }, Spec: odigosv1.InstrumentationConfigSpec{}, - } - ia := odigosv1.InstrumentedApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment-test", - Namespace: "testns", - }, - Spec: odigosv1.InstrumentedApplicationSpec{ - RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container", Language: common.JavascriptProgrammingLanguage, @@ -303,7 +259,7 @@ func TestUpdateInstrumentationConfigForWorkload_InWorkloadList(t *testing.T) { }, } - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -316,21 +272,14 @@ func TestUpdateInstrumentationConfigForWorkload_InWorkloadList(t *testing.T) { } 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{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container", Language: common.JavascriptProgrammingLanguage, @@ -360,7 +309,7 @@ func TestUpdateInstrumentationConfigForWorkload_NotInWorkloadList(t *testing.T) }, } - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -374,21 +323,14 @@ func TestUpdateInstrumentationConfigForWorkload_NotInWorkloadList(t *testing.T) } 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{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container", Language: common.JavascriptProgrammingLanguage, @@ -412,7 +354,7 @@ func TestUpdateInstrumentationConfigForWorkload_DisabledRule(t *testing.T) { }, } - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -426,21 +368,14 @@ func TestUpdateInstrumentationConfigForWorkload_DisabledRule(t *testing.T) { } 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{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container", Language: common.JavascriptProgrammingLanguage, @@ -476,7 +411,7 @@ func TestUpdateInstrumentationConfigForWorkload_MultipleDefaultRules(t *testing. }, } - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -531,14 +466,8 @@ func TestUpdateInstrumentationConfigForWorkload_RuleForLibrary(t *testing.T) { Namespace: "testns", }, Spec: odigosv1.InstrumentationConfigSpec{}, - } - ia := odigosv1.InstrumentedApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment-test", - Namespace: "testns", - }, - Spec: odigosv1.InstrumentedApplicationSpec{ - RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container", Language: common.JavascriptProgrammingLanguage, @@ -567,7 +496,7 @@ func TestUpdateInstrumentationConfigForWorkload_RuleForLibrary(t *testing.T) { }, } - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } @@ -593,14 +522,8 @@ func TestUpdateInstrumentationConfigForWorkload_LibraryRuleOtherLanguage(t *test Namespace: "testns", }, Spec: odigosv1.InstrumentationConfigSpec{}, - } - ia := odigosv1.InstrumentedApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment-test", - Namespace: "testns", - }, - Spec: odigosv1.InstrumentedApplicationSpec{ - RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test-container", Language: common.JavascriptProgrammingLanguage, @@ -630,7 +553,7 @@ func TestUpdateInstrumentationConfigForWorkload_LibraryRuleOtherLanguage(t *test }, } - err := updateInstrumentationConfigForWorkload(&ic, &ia, rules, "service-name") + err := updateInstrumentationConfigForWorkload(&ic, rules, "service-name") if err != nil { t.Errorf("Expected nil error, got %v", err) } diff --git a/instrumentor/controllers/instrumentationconfig/instrumentedapplication_controller.go b/instrumentor/controllers/instrumentationconfig/instrumentationconfig_controller.go similarity index 65% rename from instrumentor/controllers/instrumentationconfig/instrumentedapplication_controller.go rename to instrumentor/controllers/instrumentationconfig/instrumentationconfig_controller.go index 009007705..6f6e063fb 100644 --- a/instrumentor/controllers/instrumentationconfig/instrumentedapplication_controller.go +++ b/instrumentor/controllers/instrumentationconfig/instrumentationconfig_controller.go @@ -22,45 +22,32 @@ import ( odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/utils" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - 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 { +type InstrumentationConfigReconciler struct { client.Client Scheme *runtime.Scheme } -func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (r *InstrumentationConfigReconciler) 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) + 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 + return ctrl.Result{}, client.IgnoreNotFound(err) } - workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(ia.Name) + workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(ic.Name) if err != nil { return ctrl.Result{}, err } - serviceName, err := resolveServiceName(ctx, r.Client, workloadName, ia.Namespace, workloadKind) + serviceName, err := resolveServiceName(ctx, r.Client, workloadName, ic.Namespace, workloadKind) if err != nil { logger.Error(err, "Failed to resolve service name", "workload", workloadName, "kind", workloadKind) return ctrl.Result{}, err @@ -72,14 +59,14 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c return ctrl.Result{}, err } - err = updateInstrumentationConfigForWorkload(&ic, &ia, instrumentationRules, serviceName) + err = updateInstrumentationConfigForWorkload(&ic, instrumentationRules, serviceName) if err != nil { return ctrl.Result{}, err } err = r.Client.Update(ctx, &ic) if err == nil { - logger.V(0).Info("Updated instrumentation config", "workload", ia.Name) + logger.V(0).Info("Updated instrumentation config", "workload", ic.Name) } return utils.K8SUpdateErrorHandler(err) } diff --git a/instrumentor/controllers/instrumentationconfig/instrumentationrule_controller.go b/instrumentor/controllers/instrumentationconfig/instrumentationrule_controller.go index c2b0a7a47..32ec151db 100644 --- a/instrumentor/controllers/instrumentationconfig/instrumentationrule_controller.go +++ b/instrumentor/controllers/instrumentationconfig/instrumentationrule_controller.go @@ -5,7 +5,6 @@ import ( odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" - 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" @@ -18,7 +17,6 @@ type InstrumentationRuleReconciler struct { } func (r *InstrumentationRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - logger := log.FromContext(ctx) instrumentationRules := &odigosv1alpha1.InstrumentationRuleList{} @@ -27,49 +25,40 @@ func (r *InstrumentationRuleReconciler) Reconcile(ctx context.Context, req ctrl. return ctrl.Result{}, err } - instrumentedApplications := &odigosv1alpha1.InstrumentedApplicationList{} - err = r.Client.List(ctx, instrumentedApplications) + instrumentationConfigs := &odigosv1alpha1.InstrumentationConfigList{} + err = r.Client.List(ctx, instrumentationConfigs) if err != nil { return ctrl.Result{}, err } - for _, ia := range instrumentedApplications.Items { - workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(ia.Name) + for _, ic := range instrumentationConfigs.Items { + currIc := ic + workloadName, workloadKind, err := workload.ExtractWorkloadInfoFromRuntimeObjectName(ic.Name) if err != nil { return ctrl.Result{}, err } - serviceName, err := resolveServiceName(ctx, r.Client, workloadName, ia.Namespace, workloadKind) + serviceName, err := resolveServiceName(ctx, r.Client, workloadName, ic.Namespace, workloadKind) if err != nil { - logger.Error(err, "error resolving service name", "workload", ia.Name) + logger.Error(err, "error resolving service name", "workload", ic.Name) continue } - 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, serviceName) + err = updateInstrumentationConfigForWorkload(&currIc, instrumentationRules, serviceName) if err != nil { - logger.Error(err, "error updating instrumentation config", "workload", ia.Name) + logger.Error(err, "error updating instrumentation config", "workload", ic.Name) continue } - err = r.Client.Update(ctx, ic) + err = r.Client.Update(ctx, &currIc) if client.IgnoreNotFound(err) != nil { - logger.Error(err, "error updating instrumentation config", "workload", ia.Name) + logger.Error(err, "error updating instrumentation config", "workload", ic.Name) return ctrl.Result{}, err } - logger.V(0).Info("Updated instrumentation config", "workload", ia.Name) + logger.V(0).Info("Updated instrumentation config", "workload", ic.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)) + logger.V(0).Info("Payload Collection Rules changed, recalculating instrumentation configs", "number of instrumentation rules", len(instrumentationRules.Items), "number of instrumented workloads", len(instrumentationConfigs.Items)) return ctrl.Result{}, nil } diff --git a/instrumentor/controllers/instrumentationconfig/manager.go b/instrumentor/controllers/instrumentationconfig/manager.go index 1a20b5718..c7e0a3d5e 100644 --- a/instrumentor/controllers/instrumentationconfig/manager.go +++ b/instrumentor/controllers/instrumentationconfig/manager.go @@ -2,6 +2,7 @@ package instrumentationconfig import ( odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + instrumentorpredicate "github.com/odigos-io/odigos/instrumentor/controllers/utils/predicates" appsv1 "k8s.io/api/apps/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -21,12 +22,12 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } - // Watch InstrumentedApplication err = builder. ControllerManagedBy(mgr). - Named("instrumentor-instrumentationconfig-instrumentedapplication"). - For(&odigosv1alpha1.InstrumentedApplication{}). - Complete(&InstrumentedApplicationReconciler{ + Named("instrumentor-instrumentationconfig-instrumentationconfig"). + For(&odigosv1alpha1.InstrumentationConfig{}). + WithEventFilter(&instrumentorpredicate.RuntimeDetailsChangedPredicate{}). + Complete(&InstrumentationConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }) diff --git a/instrumentor/controllers/instrumentationdevice/instrumentationconfig_controller.go b/instrumentor/controllers/instrumentationdevice/instrumentationconfig_controller.go index 724dd63b4..7bebf5949 100644 --- a/instrumentor/controllers/instrumentationdevice/instrumentationconfig_controller.go +++ b/instrumentor/controllers/instrumentationdevice/instrumentationconfig_controller.go @@ -26,9 +26,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/predicate" ) type InstrumentationConfigReconciler struct { @@ -36,53 +34,6 @@ type InstrumentationConfigReconciler struct { Scheme *runtime.Scheme } -type runtimeDetailsChangedPredicate struct{} - -func (o runtimeDetailsChangedPredicate) Create(e event.CreateEvent) bool { - if e.Object == nil { - return false - } - - ic, ok := e.Object.(*odigosv1.InstrumentationConfig) - if !ok { - return false - } - - return len(ic.Status.RuntimeDetailsByContainer) > 0 -} - -func (i runtimeDetailsChangedPredicate) Update(e event.UpdateEvent) bool { - if e.ObjectOld == nil || e.ObjectNew == nil { - return false - } - - oldIc, oldOk := e.ObjectOld.(*odigosv1.InstrumentationConfig) - newIc, newOk := e.ObjectNew.(*odigosv1.InstrumentationConfig) - - if !oldOk || !newOk { - return false - } - - // currently, we only check the lengths of the runtime details - // we should improve this once we support updating the runtime details more than once - if len(oldIc.Status.RuntimeDetailsByContainer) != len(newIc.Status.RuntimeDetailsByContainer) { - return true - } - - return false -} - -func (i runtimeDetailsChangedPredicate) Delete(e event.DeleteEvent) bool { - // when the instrumentation config is deleted we need to clean up the device - return true -} - -func (i runtimeDetailsChangedPredicate) Generic(e event.GenericEvent) bool { - return false -} - -var _ predicate.Predicate = &runtimeDetailsChangedPredicate{} - func (r *InstrumentationConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) diff --git a/instrumentor/controllers/instrumentationdevice/manager.go b/instrumentor/controllers/instrumentationdevice/manager.go index 7748820b1..a8b5599e2 100644 --- a/instrumentor/controllers/instrumentationdevice/manager.go +++ b/instrumentor/controllers/instrumentationdevice/manager.go @@ -3,7 +3,7 @@ package instrumentationdevice import ( odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" - "github.com/odigos-io/odigos/instrumentor/controllers/utils" + instrumentorpredicate "github.com/odigos-io/odigos/instrumentor/controllers/utils/predicates" odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -102,9 +102,12 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. ControllerManagedBy(mgr). - Named("instrumentationdevice-instrumentedapplication"). + Named("instrumentationdevice-instrumentationconfig"). For(&odigosv1.InstrumentationConfig{}). - WithEventFilter(&runtimeDetailsChangedPredicate{}). + // The following events are relevant to the device injection/removal from the instrumentation config: + // 1. When the runtime details are changed + // 2. When the instrumentation config is deleted + WithEventFilter(predicate.Or(&instrumentorpredicate.RuntimeDetailsChangedPredicate{}, &odigospredicate.DeletionPredicate{})). Complete(&InstrumentationConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -152,7 +155,7 @@ func SetupWithManager(mgr ctrl.Manager) error { ControllerManagedBy(mgr). Named("instrumentationdevice-instrumentationrules"). For(&odigosv1.InstrumentationRule{}). - WithEventFilter(&utils.OtelSdkInstrumentationRulePredicate{}). + WithEventFilter(&instrumentorpredicate.OtelSdkInstrumentationRulePredicate{}). Complete(&InstrumentationRuleReconciler{ Client: mgr.GetClient(), }) diff --git a/instrumentor/controllers/instrumentationdevice/suite_test.go b/instrumentor/controllers/instrumentationdevice/suite_test.go deleted file mode 100644 index bd46e238f..000000000 --- a/instrumentor/controllers/instrumentationdevice/suite_test.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -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 instrumentationdevice_test - -import ( - "context" - "path/filepath" - "testing" - - "sigs.k8s.io/controller-runtime/pkg/webhook" - - "github.com/odigos-io/odigos/common" - "github.com/odigos-io/odigos/instrumentor/internal/testutil" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" - "github.com/odigos-io/odigos/instrumentor/controllers/instrumentationdevice" - //+kubebuilder:scaffold:imports -) - -var ( - cfg *rest.Config - k8sClient client.Client - testEnv *envtest.Environment - testCtx context.Context - cancel context.CancelFunc - origGetDefaultSDKs func() map[common.ProgrammingLanguage]common.OtelSdk -) - -func TestControllers(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "InstrumentationDevice Controllers Suite") -} - -var _ = BeforeSuite(func() { - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - testCtx, cancel = context.WithCancel(context.TODO()) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "api", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: true, - } - - var err error - // cfg is defined in this file globally. - cfg, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - err = odigosv1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) - - // create the odigos system namespace - odigosSystemNamespace := testutil.NewOdigosSystemNamespace() - Expect(k8sClient.Create(testCtx, odigosSystemNamespace)).Should(Succeed()) - - configmap := testutil.NewMockOdigosConfig() - Expect(k8sClient.Create(testCtx, configmap)).Should(Succeed()) - - // report the node collector is ready - datacollection := testutil.NewMockDataCollection() - Expect(k8sClient.Create(testCtx, datacollection)).Should(Succeed()) - k8sClient.Get(testCtx, types.NamespacedName{Name: datacollection.GetName(), Namespace: datacollection.GetNamespace()}, datacollection) - datacollection.Status.Ready = true - Expect(k8sClient.Status().Update(testCtx, datacollection)).Should(Succeed()) - - // create odigos configuration with default sdks - origGetDefaultSDKs = instrumentationdevice.GetDefaultSDKs - instrumentationdevice.GetDefaultSDKs = testutil.MockGetDefaultSDKs - - webhookInstallOptions := &testEnv.WebhookInstallOptions - k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme.Scheme, - WebhookServer: webhook.NewServer(webhook.Options{ - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - }), - }) - Expect(err).ToNot(HaveOccurred()) - - err = instrumentationdevice.SetupWithManager(k8sManager) - Expect(err).ToNot(HaveOccurred()) - - go func() { - defer GinkgoRecover() - err = k8sManager.Start(testCtx) - Expect(err).ToNot(HaveOccurred(), "failed to run manager") - }() - -}, 60) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) - instrumentationdevice.GetDefaultSDKs = origGetDefaultSDKs -}) diff --git a/instrumentor/controllers/utils/predicate.go b/instrumentor/controllers/utils/predicates/instrumentation_rule.go similarity index 98% rename from instrumentor/controllers/utils/predicate.go rename to instrumentor/controllers/utils/predicates/instrumentation_rule.go index 896c6ab52..b1da0900f 100644 --- a/instrumentor/controllers/utils/predicate.go +++ b/instrumentor/controllers/utils/predicates/instrumentation_rule.go @@ -1,4 +1,4 @@ -package utils +package predicates import ( odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" @@ -7,6 +7,7 @@ import ( type OtelSdkInstrumentationRulePredicate struct{} + func (o OtelSdkInstrumentationRulePredicate) Create(e event.CreateEvent) bool { // check if delete rule is for otel sdk instrumentationRule, ok := e.Object.(*odigosv1alpha1.InstrumentationRule) diff --git a/instrumentor/controllers/utils/predicates/runtime_details_changed.go b/instrumentor/controllers/utils/predicates/runtime_details_changed.go new file mode 100644 index 000000000..d152d55c7 --- /dev/null +++ b/instrumentor/controllers/utils/predicates/runtime_details_changed.go @@ -0,0 +1,62 @@ +package predicates + +import ( + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// RuntimeDetailsChangedPredicate is a predicate that checks if the runtime details of an InstrumentationConfig have changed. +// +// For Create events, it returns true if the InstrumentationConfig has any runtime details. +// For Update events, it returns true if the runtime details have changed (currently only checks the length of the runtime details). +// For Delete events, it returns false. +// +// TODO: once we support updating the runtime details more than once, we should improve this predicate to check the actual changes. +type RuntimeDetailsChangedPredicate struct{} + +var _ predicate.Predicate = &RuntimeDetailsChangedPredicate{} + +var InstrumentationConfigRuntimeDetailsChangedPredicate = RuntimeDetailsChangedPredicate{} + +func (o RuntimeDetailsChangedPredicate) Create(e event.CreateEvent) bool { + if e.Object == nil { + return false + } + + ic, ok := e.Object.(*odigosv1.InstrumentationConfig) + if !ok { + return false + } + + return len(ic.Status.RuntimeDetailsByContainer) > 0 +} + +func (i RuntimeDetailsChangedPredicate) Update(e event.UpdateEvent) bool { + if e.ObjectOld == nil || e.ObjectNew == nil { + return false + } + + oldIc, oldOk := e.ObjectOld.(*odigosv1.InstrumentationConfig) + newIc, newOk := e.ObjectNew.(*odigosv1.InstrumentationConfig) + + if !oldOk || !newOk { + return false + } + + // currently, we only check the lengths of the runtime details + // we should improve this once we support updating the runtime details more than once + if len(oldIc.Status.RuntimeDetailsByContainer) != len(newIc.Status.RuntimeDetailsByContainer) { + return true + } + + return false +} + +func (i RuntimeDetailsChangedPredicate) Delete(e event.DeleteEvent) bool { + return false +} + +func (i RuntimeDetailsChangedPredicate) Generic(e event.GenericEvent) bool { + return false +} diff --git a/instrumentor/report/events.go b/instrumentor/report/events.go index 6c3ba39d9..765b5c1e7 100644 --- a/instrumentor/report/events.go +++ b/instrumentor/report/events.go @@ -85,7 +85,7 @@ func report(c client.Client, installationId string) error { return err } - var apps odigosv1.InstrumentedApplicationList + var apps odigosv1.InstrumentationConfigList err = c.List(ctx, &apps) if err != nil { return err @@ -98,7 +98,7 @@ func report(c client.Client, installationId string) error { jsApps := 0 unrecognizedApps := 0 for _, app := range apps.Items { - for _, l := range app.Spec.RuntimeDetails { + for _, l := range app.Status.RuntimeDetailsByContainer { switch l.Language { case common.GoProgrammingLanguage: goApps++ @@ -115,7 +115,7 @@ func report(c client.Client, installationId string) error { } } - if len(app.Spec.RuntimeDetails) == 0 { + if len(app.Status.RuntimeDetailsByContainer) == 0 { unrecognizedApps++ } } From f60081fff6e2a32df04555259c5c37e25ed5b796 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Mon, 6 Jan 2025 13:23:13 +0200 Subject: [PATCH 200/259] Sync main into feature/source-crd (#2141) Syncing main with feature branch to resolve conflicts and reduce amount of changed files. --------- Co-authored-by: Ron Federman <73110295+RonFed@users.noreply.github.com> Co-authored-by: Tamir David --- k8sutils/pkg/describe/source/analyze.go | 39 ++++++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/k8sutils/pkg/describe/source/analyze.go b/k8sutils/pkg/describe/source/analyze.go index 8968f2b1a..59e803460 100644 --- a/k8sutils/pkg/describe/source/analyze.go +++ b/k8sutils/pkg/describe/source/analyze.go @@ -22,10 +22,12 @@ type InstrumentationLabelsAnalyze struct { } type ContainerRuntimeInfoAnalyze struct { - ContainerName properties.EntityProperty `json:"containerName"` - Language properties.EntityProperty `json:"language"` - RuntimeVersion properties.EntityProperty `json:"runtimeVersion"` - EnvVars []properties.EntityProperty `json:"envVars"` + ContainerName properties.EntityProperty `json:"containerName"` + Language properties.EntityProperty `json:"language"` + RuntimeVersion properties.EntityProperty `json:"runtimeVersion"` + CriError properties.EntityProperty `json:"criError"` + EnvVars []properties.EntityProperty `json:"envVars"` + ContainerRuntimeEnvs []properties.EntityProperty `json:"containerRuntimeEnvs"` } type RuntimeInfoAnalyze struct { @@ -200,6 +202,18 @@ func analyzeRuntimeDetails(runtimeDetailsByContainer []odigosv1.RuntimeDetailsBy runtimeVersion.Value = "not available" } + criError := properties.EntityProperty{ + Name: "CRI Error", + Explain: "an error message from the container runtime interface (CRI) when trying to get runtime details for this container", + } + if container.CriErrorMessage != nil { + criError.Value = *container.CriErrorMessage + criError.Status = properties.PropertyStatusError + + } else { + criError.Value = "No CRI error observed" + } + envVars := make([]properties.EntityProperty, 0, len(container.EnvVars)) for _, envVar := range container.EnvVars { envVars = append(envVars, properties.EntityProperty{ @@ -207,12 +221,21 @@ func analyzeRuntimeDetails(runtimeDetailsByContainer []odigosv1.RuntimeDetailsBy Value: envVar.Value, }) } + containerRuntimeEnvs := make([]properties.EntityProperty, 0, len(container.EnvFromContainerRuntime)) + for _, envVar := range container.EnvFromContainerRuntime { + containerRuntimeEnvs = append(containerRuntimeEnvs, properties.EntityProperty{ + Name: envVar.Name, + Value: envVar.Value, + }) + } containers = append(containers, ContainerRuntimeInfoAnalyze{ - ContainerName: containerName, - Language: language, - RuntimeVersion: runtimeVersion, - EnvVars: envVars, + ContainerName: containerName, + Language: language, + RuntimeVersion: runtimeVersion, + EnvVars: envVars, + ContainerRuntimeEnvs: containerRuntimeEnvs, + CriError: criError, }) } From ff22deb7354c714a844452d4ba27d115b413990b Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Mon, 6 Jan 2025 10:54:23 -0500 Subject: [PATCH 201/259] Sync main into feature/source-crd (#2144) --- .../workflows/trigger-odigos-enterprise.yaml | 23 ++++ .../odigos.io_instrumentationconfigs.yaml | 4 - .../v1alpha1/instrumentationconfigspec.go | 15 +-- .../v1alpha1/instrumentationconfig_types.go | 3 - cli/cmd/resources/README.md | 7 +- cli/cmd/resources/odiglet.go | 12 -- .../odigos.io_instrumentationconfigs.yaml | 4 - .../odigos/templates/odiglet/clusterrole.yaml | 19 ---- .../common.go | 17 +-- .../daemonset_controller.go | 2 +- .../deployment_controller.go | 2 +- .../deployment_controller_test.go | 36 +++--- .../instrumentationconfig_controller.go} | 18 +-- .../instrumentationconfig_controller_test.go} | 16 +-- .../instrumentedapp_migration_controller.go | 47 ++++++++ .../manager.go | 32 ++++-- .../namespace_controller.go | 4 +- .../namespace_controller_test.go | 48 ++++---- .../source_controller.go | 2 +- .../statefulset_controller.go | 2 +- .../suite_test.go | 8 +- .../startlangdetection/source_controller.go | 2 +- .../workload_controllers.go | 15 +-- instrumentor/go.mod | 2 +- instrumentor/internal/testutil/assertions.go | 16 +-- instrumentor/internal/testutil/helpers.go | 16 --- instrumentor/internal/testutil/mocks.go | 8 +- instrumentor/main.go | 4 +- k8sutils/pkg/describe/source.go | 7 ++ k8sutils/pkg/predicate/creation.go | 2 +- .../pkg/kube/instrumentation_ebpf/shared.go | 6 +- .../pkg/kube/runtime_details/inspection.go | 85 +-------------- .../runtime_details/instconfig_controller.go | 103 ------------------ .../instrumentationconfigs_controller.go | 40 +++++++ odiglet/pkg/kube/runtime_details/manager.go | 12 -- .../assert/simple-demo-runtime-detected.yaml | 80 -------------- .../dotnet-http-server/net6-glibc.Dockerfile | 2 +- .../dotnet-http-server/net6-musl.Dockerfile | 2 +- 38 files changed, 245 insertions(+), 478 deletions(-) create mode 100644 .github/workflows/trigger-odigos-enterprise.yaml rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/common.go (81%) rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/daemonset_controller.go (97%) rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/deployment_controller.go (97%) rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/deployment_controller_test.go (67%) rename instrumentor/controllers/{deleteinstrumentedapplication/instrumentedapplication_controller.go => deleteinstrumentationconfig/instrumentationconfig_controller.go} (83%) rename instrumentor/controllers/{deleteinstrumentedapplication/instrumentedapplication_controller_test.go => deleteinstrumentationconfig/instrumentationconfig_controller_test.go} (56%) create mode 100644 instrumentor/controllers/deleteinstrumentationconfig/instrumentedapp_migration_controller.go rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/manager.go (68%) rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/namespace_controller.go (97%) rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/namespace_controller_test.go (60%) rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/source_controller.go (98%) rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/statefulset_controller.go (97%) rename instrumentor/controllers/{deleteinstrumentedapplication => deleteinstrumentationconfig}/suite_test.go (92%) delete mode 100644 odiglet/pkg/kube/runtime_details/instconfig_controller.go diff --git a/.github/workflows/trigger-odigos-enterprise.yaml b/.github/workflows/trigger-odigos-enterprise.yaml new file mode 100644 index 000000000..ce834a178 --- /dev/null +++ b/.github/workflows/trigger-odigos-enterprise.yaml @@ -0,0 +1,23 @@ +name: Trigger Odigos Enterprise Workflow + +on: + push: + branches: + - 'main' + paths: + - 'common/**' + - 'k8sUtils/**' + - 'procdiscovery/**' + - 'opamserver/**' + - 'instrumentation/**' +jobs: + trigger-odigos-enterprise: + runs-on: ubuntu-latest + steps: + - name: Trigger process PR in Odigos Enterprise + run: | + curl -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${{ secrets.RELEASE_BOT_TOKEN }}" \ + https://api.github.com/repos/odigos-io/odigos-enterprise/dispatches \ + -d '{"event_type": "process_oss_pr", "client_payload": {"pr_creator": "${{ github.event.pull_request.user.login }}"}}' diff --git a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml index 1f16bc4d0..a5f2946e6 100644 --- a/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml +++ b/api/config/crd/bases/odigos.io_instrumentationconfigs.yaml @@ -110,10 +110,6 @@ spec: - optionKey type: object type: array - runtimeDetailsInvalidated: - description: true when the runtime details are invalidated and should - be recalculated - type: boolean sdkConfigs: description: |- Configuration for the OpenTelemetry SDKs that this workload should use. diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigspec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigspec.go index 4c6b6fb4c..d3c2b9b5f 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigspec.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentationconfigspec.go @@ -20,10 +20,9 @@ package v1alpha1 // InstrumentationConfigSpecApplyConfiguration represents a declarative configuration of the InstrumentationConfigSpec type for use // with apply. type InstrumentationConfigSpecApplyConfiguration struct { - ServiceName *string `json:"serviceName,omitempty"` - RuntimeDetailsInvalidated *bool `json:"runtimeDetailsInvalidated,omitempty"` - Config []WorkloadInstrumentationConfigApplyConfiguration `json:"config,omitempty"` - SdkConfigs []SdkConfigApplyConfiguration `json:"sdkConfigs,omitempty"` + ServiceName *string `json:"serviceName,omitempty"` + Config []WorkloadInstrumentationConfigApplyConfiguration `json:"config,omitempty"` + SdkConfigs []SdkConfigApplyConfiguration `json:"sdkConfigs,omitempty"` } // InstrumentationConfigSpecApplyConfiguration constructs a declarative configuration of the InstrumentationConfigSpec type for use with @@ -40,14 +39,6 @@ func (b *InstrumentationConfigSpecApplyConfiguration) WithServiceName(value stri return b } -// WithRuntimeDetailsInvalidated sets the RuntimeDetailsInvalidated 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 RuntimeDetailsInvalidated field is set to the value of the last call. -func (b *InstrumentationConfigSpecApplyConfiguration) WithRuntimeDetailsInvalidated(value bool) *InstrumentationConfigSpecApplyConfiguration { - b.RuntimeDetailsInvalidated = &value - return b -} - // WithConfig adds the given value to the Config 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 Config field. diff --git a/api/odigos/v1alpha1/instrumentationconfig_types.go b/api/odigos/v1alpha1/instrumentationconfig_types.go index f12a4841f..5683acdf6 100644 --- a/api/odigos/v1alpha1/instrumentationconfig_types.go +++ b/api/odigos/v1alpha1/instrumentationconfig_types.go @@ -62,9 +62,6 @@ type InstrumentationConfigSpec struct { // the service.name property is used to populate the `service.name` resource attribute in the telemetry generated by this workload ServiceName string `json:"serviceName,omitempty"` - // true when the runtime details are invalidated and should be recalculated - RuntimeDetailsInvalidated bool `json:"runtimeDetailsInvalidated,omitempty"` - // config for this workload. // the config is a list to allow for multiple config options and values to be applied. // the list is processed in order, and the first matching config is applied. diff --git a/cli/cmd/resources/README.md b/cli/cmd/resources/README.md index 5cdf5b6ce..986a59b33 100644 --- a/cli/cmd/resources/README.md +++ b/cli/cmd/resources/README.md @@ -11,9 +11,7 @@ In this doc, we'll keep track of the permissions requested across different reso | Odiglet | "" | nodes | get, list, watch | Needed for virtual device registration. | | Odiglet | apps | deployments, daemonsets, statefulsets | get, list, watch | Needed for language detection (temporary until new detection logic is ready). | | Odiglet | apps | deployments/status, daemonsets/status, statefulsets/status | get | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | apps | replicasets | get | Needed for language detection (temporary until new detection logic is ready). | -| Odiglet | odigos.io | instrumentedapplications | get, list, watch, create, patch, update | Stores runtime details and language detection (temporary until migration to configs). | -| Odiglet | odigos.io | instrumentedapplications/status | get, patch, update | Updates status of instrumented applications. | +| Odiglet | apps | replicasets | get | Needed for language detection (temporary until new detection logic is ready). | | | Odiglet | odigos.io | instrumentationinstances | create, get, list, patch, update, watch, delete | Manages instrumentation instances for process state storage. | | Odiglet | odigos.io | instrumentationinstances/status | get, patch, update | Updates status of instrumentation instances. | | Odiglet | odigos.io | instrumentationconfigs | get, list, watch, patch, update | Manages instrumentation configurations (future update for runtime details storage). | @@ -21,8 +19,7 @@ In this doc, we'll keep track of the permissions requested across different reso | Odiglet | policy | podsecuritypolicies | use | Allows using privileged PSP (if enabled). | | Instrumentor | "" | nodes, namespaces | get, list, watch | Tracks runtime detection and resource labels for instrumentation. | | Instrumentor | apps | daemonsets, deployments, statefulsets | get, list, watch, update, patch | Adjusts pod specifications for instrumentation. | -| Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Reacts to runtime detections in workloads. | -| Instrumentor | odigos.io | instrumentedapplications/status | get, patch, update | Updates application statuses post-injection. | +| Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Delete deprecated CR. | | | Instrumentor | odigos.io | instrumentationconfigs | create, delete, get, list, patch, update, watch | Manages instrumentation configurations. | | Scheduler | odigos.io | instrumentationconfigs | get, list, watch | Monitors changes in instrumentation configurations for scheduling updates. | | Autoscaler | odigos.io | instrumentationconfigs | get, list, watch | Reads instrumentation configurations to populate the `data-collector` configmaps. | diff --git a/cli/cmd/resources/odiglet.go b/cli/cmd/resources/odiglet.go index 7504a3fff..e8f553ad9 100644 --- a/cli/cmd/resources/odiglet.go +++ b/cli/cmd/resources/odiglet.go @@ -146,18 +146,6 @@ func NewOdigletClusterRole(psp bool) *rbacv1.ClusterRole { Resources: []string{"nodes"}, Verbs: []string{"get", "list", "watch"}, }, - { // Needed for storage of runtime details / language detection (almost deprecated) - // TODO: remove this once Tamir/PR is read for instrumentation app ---> instrumentation config migration - APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications"}, - Verbs: []string{"get", "list", "watch", "create", "patch", "update"}, - }, - { // Needed for storage of runtime details / language detection (almost deprecated) - // TODO: remove this once Tamir/PR is read for instrumentation app ---> instrumentation config migration - APIGroups: []string{"odigos.io"}, - Resources: []string{"instrumentedapplications/status"}, - Verbs: []string{"get", "patch", "update"}, - }, { // Needed for storage of the process instrumentation state APIGroups: []string{"odigos.io"}, Resources: []string{"instrumentationinstances"}, diff --git a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml index 1f16bc4d0..a5f2946e6 100644 --- a/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml +++ b/helm/odigos/templates/crds/odigos.io_instrumentationconfigs.yaml @@ -110,10 +110,6 @@ spec: - optionKey type: object type: array - runtimeDetailsInvalidated: - description: true when the runtime details are invalidated and should - be recalculated - type: boolean sdkConfigs: description: |- Configuration for the OpenTelemetry SDKs that this workload should use. diff --git a/helm/odigos/templates/odiglet/clusterrole.yaml b/helm/odigos/templates/odiglet/clusterrole.yaml index 7a57f159e..370281a86 100644 --- a/helm/odigos/templates/odiglet/clusterrole.yaml +++ b/helm/odigos/templates/odiglet/clusterrole.yaml @@ -49,25 +49,6 @@ rules: - get - list - watch - - apiGroups: - - odigos.io - resources: - - instrumentedapplications - verbs: - - get - - list - - watch - - create - - patch - - update - - apiGroups: - - odigos.io - resources: - - instrumentedapplications/status - verbs: - - get - - patch - - update - apiGroups: - odigos.io resources: diff --git a/instrumentor/controllers/deleteinstrumentedapplication/common.go b/instrumentor/controllers/deleteinstrumentationconfig/common.go similarity index 81% rename from instrumentor/controllers/deleteinstrumentedapplication/common.go rename to instrumentor/controllers/deleteinstrumentationconfig/common.go index ef5ee898f..5a0770df4 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/common.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/common.go @@ -1,4 +1,4 @@ -package deleteinstrumentedapplication +package deleteinstrumentationconfig import ( "context" @@ -32,7 +32,7 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work } } - if err := deleteWorkloadInstrumentedApplication(ctx, kubeClient, workloadObject); err != nil { + if err := deleteWorkloadInstrumentationConfig(ctx, kubeClient, workloadObject); err != nil { logger.Error(err, "error removing runtime details") return err } @@ -45,29 +45,18 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work return nil } -func deleteWorkloadInstrumentedApplication(ctx context.Context, kubeClient client.Client, workloadObject client.Object) error { - +func deleteWorkloadInstrumentationConfig(ctx context.Context, kubeClient client.Client, workloadObject client.Object) error { ns := workloadObject.GetNamespace() name := workloadObject.GetName() kind := workload.WorkloadKindFromClientObject(workloadObject) instrumentedApplicationName := workload.CalculateWorkloadRuntimeObjectName(name, kind) - instAppErr := kubeClient.Delete(ctx, &odigosv1.InstrumentedApplication{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: instrumentedApplicationName, - }, - }) - instConfigErr := kubeClient.Delete(ctx, &odigosv1.InstrumentationConfig{ ObjectMeta: metav1.ObjectMeta{ Namespace: ns, Name: instrumentedApplicationName, }, }) - if instAppErr != nil { - return client.IgnoreNotFound(instAppErr) - } if instConfigErr != nil { return client.IgnoreNotFound(instConfigErr) diff --git a/instrumentor/controllers/deleteinstrumentedapplication/daemonset_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/daemonset_controller.go similarity index 97% rename from instrumentor/controllers/deleteinstrumentedapplication/daemonset_controller.go rename to instrumentor/controllers/deleteinstrumentationconfig/daemonset_controller.go index aa884096e..f4b9adef2 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/daemonset_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/daemonset_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deleteinstrumentedapplication +package deleteinstrumentationconfig import ( "context" diff --git a/instrumentor/controllers/deleteinstrumentedapplication/deployment_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/deployment_controller.go similarity index 97% rename from instrumentor/controllers/deleteinstrumentedapplication/deployment_controller.go rename to instrumentor/controllers/deleteinstrumentationconfig/deployment_controller.go index 2279dd002..42c909ddc 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/deployment_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/deployment_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deleteinstrumentedapplication +package deleteinstrumentationconfig import ( "context" diff --git a/instrumentor/controllers/deleteinstrumentedapplication/deployment_controller_test.go b/instrumentor/controllers/deleteinstrumentationconfig/deployment_controller_test.go similarity index 67% rename from instrumentor/controllers/deleteinstrumentedapplication/deployment_controller_test.go rename to instrumentor/controllers/deleteinstrumentationconfig/deployment_controller_test.go index 5ce787c7c..5f35dc787 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/deployment_controller_test.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/deployment_controller_test.go @@ -1,4 +1,4 @@ -package deleteinstrumentedapplication_test +package deleteinstrumentationconfig_test import ( "context" @@ -11,13 +11,13 @@ import ( corev1 "k8s.io/api/core/v1" ) -var _ = Describe("deleteInstrumentedApplication Deployment controller", func() { +var _ = Describe("deleteInstrumentationConfig Deployment controller", func() { ctx := context.Background() var namespace *corev1.Namespace var deployment *appsv1.Deployment - var instrumentedApplication *odigosv1.InstrumentedApplication + var instrumentationConfig *odigosv1.InstrumentationConfig - Describe("Delete InstrumentedApplication", func() { + Describe("Delete InstrumentationConfig", func() { When("Namespace is not instrumented", func() { @@ -28,20 +28,20 @@ var _ = Describe("deleteInstrumentedApplication Deployment controller", func() { deployment = testutil.SetOdigosInstrumentationEnabled(testutil.NewMockTestDeployment(namespace)) Expect(k8sClient.Create(ctx, deployment)).Should(Succeed()) - instrumentedApplication = testutil.NewMockInstrumentedApplication(deployment) - Expect(k8sClient.Create(ctx, instrumentedApplication)).Should(Succeed()) + instrumentationConfig = testutil.NewMockInstrumentationConfig(deployment) + Expect(k8sClient.Create(ctx, instrumentationConfig)).Should(Succeed()) }) - It("InstrumentedApplication deleted after removing instrumentation label from deployment", func() { + It("InstrumentationConfig deleted after removing instrumentation label from deployment", func() { deployment = testutil.DeleteOdigosInstrumentationLabel(deployment) Expect(k8sClient.Update(ctx, deployment)).Should(Succeed()) - testutil.AssertInstrumentedApplicationDeleted(ctx, k8sClient, instrumentedApplication) + testutil.AssertInstrumentationConfigDeleted(ctx, k8sClient, instrumentationConfig) }) - It("InstrumentedApplication deleted after setting instrumentation label to disabled", func() { + It("InstrumentationConfig deleted after setting instrumentation label to disabled", func() { deployment = testutil.SetOdigosInstrumentationDisabled(deployment) Expect(k8sClient.Update(ctx, deployment)).Should(Succeed()) - testutil.AssertInstrumentedApplicationDeleted(ctx, k8sClient, instrumentedApplication) + testutil.AssertInstrumentationConfigDeleted(ctx, k8sClient, instrumentationConfig) }) }) @@ -55,20 +55,20 @@ var _ = Describe("deleteInstrumentedApplication Deployment controller", func() { deployment = testutil.SetOdigosInstrumentationEnabled(testutil.NewMockTestDeployment(namespace)) Expect(k8sClient.Create(ctx, deployment)).Should(Succeed()) - instrumentedApplication = testutil.NewMockInstrumentedApplication(deployment) - Expect(k8sClient.Create(ctx, instrumentedApplication)).Should(Succeed()) + instrumentationConfig = testutil.NewMockInstrumentationConfig(deployment) + Expect(k8sClient.Create(ctx, instrumentationConfig)).Should(Succeed()) }) - It("InstrumentedApplication retain when removing instrumentation label from deployment", func() { + It("InstrumentationConfig retain when removing instrumentation label from deployment", func() { deployment = testutil.DeleteOdigosInstrumentationLabel(deployment) Expect(k8sClient.Update(ctx, deployment)).Should(Succeed()) - testutil.AssertInstrumentedApplicationRetained(ctx, k8sClient, instrumentedApplication) + testutil.AssertInstrumentationConfigRetained(ctx, k8sClient, instrumentationConfig) }) - It("InstrumentedApplication deleted after setting instrumentation label to disabled", func() { + It("InstrumentationConfig deleted after setting instrumentation label to disabled", func() { deployment = testutil.SetOdigosInstrumentationDisabled(deployment) Expect(k8sClient.Update(ctx, deployment)).Should(Succeed()) - testutil.AssertInstrumentedApplicationDeleted(ctx, k8sClient, instrumentedApplication) + testutil.AssertInstrumentationConfigDeleted(ctx, k8sClient, instrumentationConfig) }) }) }) @@ -83,8 +83,8 @@ var _ = Describe("deleteInstrumentedApplication Deployment controller", func() { deployment = testutil.SetReportedNameAnnotation(deployment, "test") Expect(k8sClient.Create(ctx, deployment)).Should(Succeed()) - instrumentedApplication = testutil.NewMockInstrumentedApplication(deployment) - Expect(k8sClient.Create(ctx, instrumentedApplication)).Should(Succeed()) + instrumentationConfig = testutil.NewMockInstrumentationConfig(deployment) + Expect(k8sClient.Create(ctx, instrumentationConfig)).Should(Succeed()) }) It("should delete the reported name annotation on instrumentation label deleted", func() { diff --git a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go similarity index 83% rename from instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go rename to instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go index 52866a90b..ab8ebeaf4 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deleteinstrumentedapplication +package deleteinstrumentationconfig import ( "context" @@ -58,24 +58,24 @@ func getObjectByOwnerReference(ctx context.Context, k8sClient client.Client, own return nil, fmt.Errorf("unsupported owner kind %s", ownerRef.Kind) } -type InstrumentedApplicationReconciler struct { +type InstrumentationConfigReconciler struct { client.Client Scheme *runtime.Scheme } -func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (r *InstrumentationConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) - var instrumentedApplication odigosv1.InstrumentedApplication - err := r.Client.Get(ctx, req.NamespacedName, &instrumentedApplication) + var instrumentationConfig odigosv1.InstrumentationConfig + err := r.Client.Get(ctx, req.NamespacedName, &instrumentationConfig) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } - // find the workload object which is the owner of the InstrumentedApplication - ownerReferences := instrumentedApplication.GetOwnerReferences() + // find the workload object which is the owner of the InstrumentationConfig + ownerReferences := instrumentationConfig.GetOwnerReferences() if len(ownerReferences) != 1 { - logger.Info("InstrumentedApplication should have exactly one owner reference") + logger.Info("InstrumentationConfig should have exactly one owner reference") return ctrl.Result{}, nil } workloadObject, err := getObjectByOwnerReference(ctx, r.Client, ownerReferences[0], req.Namespace) @@ -98,7 +98,7 @@ func (r *InstrumentedApplicationReconciler) Reconcile(ctx context.Context, req c } if len(sourceList.Items) == 0 { logger.Info("Deleting instrumented application for non-enabled workload") - err := r.Client.Delete(ctx, &instrumentedApplication) + err := r.Client.Delete(ctx, &instrumentationConfig) return ctrl.Result{}, client.IgnoreNotFound(err) } } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller_test.go b/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller_test.go similarity index 56% rename from instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller_test.go rename to instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller_test.go index 7320c1d31..be6490913 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/instrumentedapplication_controller_test.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller_test.go @@ -1,4 +1,4 @@ -package deleteinstrumentedapplication_test +package deleteinstrumentationconfig_test import ( "context" @@ -11,13 +11,13 @@ import ( corev1 "k8s.io/api/core/v1" ) -var _ = Describe("deleteInstrumentedApplication InstrumentedApplication controller", func() { +var _ = Describe("deleteInstrumentationConfig InstrumentationConfig controller", func() { ctx := context.Background() var namespace *corev1.Namespace var deployment *appsv1.Deployment - var instrumentedApplication *odigosv1.InstrumentedApplication + var instrumentationConfig *odigosv1.InstrumentationConfig - Describe("Delete InstrumentedApplication", func() { + Describe("Delete InstrumentationConfig", func() { When("Object created after deployment reconciled", func() { @@ -29,12 +29,12 @@ var _ = Describe("deleteInstrumentedApplication InstrumentedApplication controll Expect(k8sClient.Create(ctx, deployment)).Should(Succeed()) }) - It("InstrumentedApplication created for deployment which is not enabled", func() { + It("InstrumentationConfig created for deployment which is not enabled", func() { - instrumentedApplication = testutil.NewMockInstrumentedApplication(deployment) - Expect(k8sClient.Create(ctx, instrumentedApplication)).Should(Succeed()) + instrumentationConfig = testutil.NewMockInstrumentationConfig(deployment) + Expect(k8sClient.Create(ctx, instrumentationConfig)).Should(Succeed()) - testutil.AssertInstrumentedApplicationDeleted(ctx, k8sClient, instrumentedApplication) + testutil.AssertInstrumentationConfigDeleted(ctx, k8sClient, instrumentationConfig) }) }) diff --git a/instrumentor/controllers/deleteinstrumentationconfig/instrumentedapp_migration_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/instrumentedapp_migration_controller.go new file mode 100644 index 000000000..ef831b8bd --- /dev/null +++ b/instrumentor/controllers/deleteinstrumentationconfig/instrumentedapp_migration_controller.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. +*/ + +package deleteinstrumentationconfig + +import ( + "context" + + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// InstrumentationConfigReconciler is used as a migration, to delete InstrumentedApplication objects +// This is added in January 2025, once enough time will pass, we can remove this and remove the instrumentedApplication CRD as well. +type InstrumentedApplicationMigrationReconciler struct { + client.Client +} + +func (r *InstrumentedApplicationMigrationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + if err := r.Client.Delete(ctx, &odigosv1.InstrumentedApplication{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Namespace, + Name: req.Name, + }, + }); err == nil { + logger.Info("InstrumentedApplication deleted", "name", req.Name) + } + return ctrl.Result{}, nil +} diff --git a/instrumentor/controllers/deleteinstrumentedapplication/manager.go b/instrumentor/controllers/deleteinstrumentationconfig/manager.go similarity index 68% rename from instrumentor/controllers/deleteinstrumentedapplication/manager.go rename to instrumentor/controllers/deleteinstrumentationconfig/manager.go index e401c2354..851afa213 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/manager.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/manager.go @@ -1,8 +1,9 @@ -package deleteinstrumentedapplication +package deleteinstrumentationconfig import ( odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/predicate" + odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -14,7 +15,7 @@ import ( func SetupWithManager(mgr ctrl.Manager) error { err := builder. ControllerManagedBy(mgr). - Named("deleteinstrumentedapplication-deployment"). + Named("deleteinstrumentationconfig-deployment"). For(&appsv1.Deployment{}). WithEventFilter(predicate.LabelChangedPredicate{}). Complete(&DeploymentReconciler{ @@ -27,7 +28,7 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. ControllerManagedBy(mgr). - Named("deleteinstrumentedapplication-statefulset"). + Named("deleteinstrumentationconfig-statefulset"). For(&appsv1.StatefulSet{}). WithEventFilter(predicate.LabelChangedPredicate{}). Complete(&StatefulSetReconciler{ @@ -40,7 +41,7 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. ControllerManagedBy(mgr). - Named("deleteinstrumentedapplication-daemonset"). + Named("deleteinstrumentationconfig-daemonset"). For(&appsv1.DaemonSet{}). WithEventFilter(predicate.LabelChangedPredicate{}). Complete(&DaemonSetReconciler{ @@ -53,7 +54,7 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. ControllerManagedBy(mgr). - Named("deleteinstrumentedapplication-namespace"). + Named("deleteinstrumentationconfig-namespace"). For(&corev1.Namespace{}). WithEventFilter(&NsLabelBecameDisabledPredicate{}). Complete(&NamespaceReconciler{ @@ -66,9 +67,10 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. ControllerManagedBy(mgr). - Named("deleteinstrumentedapplication-instrumentedapplication"). - For(&odigosv1.InstrumentedApplication{}). - Complete(&InstrumentedApplicationReconciler{ + Named("deleteinstrumentationconfig-instrumentationconfig"). + For(&odigosv1.InstrumentationConfig{}). + WithEventFilter(&odigospredicate.CreationPredicate{}). + Complete(&InstrumentationConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }) @@ -78,7 +80,7 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. ControllerManagedBy(mgr). - Named("deleteinstrumentedapplication-source"). + Named("deleteinstrumentationconfig-source"). WithEventFilter(&k8sutils.OnlyUpdatesPredicate{}). For(&odigosv1.Source{}). Complete(&SourceReconciler{ @@ -89,6 +91,18 @@ func SetupWithManager(mgr ctrl.Manager) error { return err } + err = builder. + ControllerManagedBy(mgr). + Named("deleteinstrumentationconfig-instrumentedapp-migration"). + For(&odigosv1.InstrumentedApplication{}). + WithEventFilter(&odigospredicate.CreationPredicate{}). + Complete(&InstrumentedApplicationMigrationReconciler{ + Client: mgr.GetClient(), + }) + if err != nil { + return err + } + return nil } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/namespace_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go similarity index 97% rename from instrumentor/controllers/deleteinstrumentedapplication/namespace_controller.go rename to instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go index b71c74142..ef8271473 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/namespace_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deleteinstrumentedapplication +package deleteinstrumentationconfig import ( "context" @@ -156,7 +156,7 @@ func syncGenericWorkloadListToNs(ctx context.Context, c client.Client, kind work } var err error - err = errors.Join(err, deleteWorkloadInstrumentedApplication(ctx, c, freshWorkloadCopy)) + err = errors.Join(err, deleteWorkloadInstrumentationConfig(ctx, c, freshWorkloadCopy)) err = errors.Join(err, removeReportedNameAnnotation(ctx, c, freshWorkloadCopy)) return err } diff --git a/instrumentor/controllers/deleteinstrumentedapplication/namespace_controller_test.go b/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller_test.go similarity index 60% rename from instrumentor/controllers/deleteinstrumentedapplication/namespace_controller_test.go rename to instrumentor/controllers/deleteinstrumentationconfig/namespace_controller_test.go index d8fbd19da..62d3a3681 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/namespace_controller_test.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller_test.go @@ -1,4 +1,4 @@ -package deleteinstrumentedapplication_test +package deleteinstrumentationconfig_test import ( "context" @@ -11,7 +11,7 @@ import ( corev1 "k8s.io/api/core/v1" ) -var _ = Describe("DeleteInstrumentedApplication Namespace controller", func() { +var _ = Describe("DeleteInstrumentationConfig Namespace controller", func() { ctx := context.Background() var namespace *corev1.Namespace @@ -20,9 +20,9 @@ var _ = Describe("DeleteInstrumentedApplication Namespace controller", func() { var daemonSet *appsv1.DaemonSet var statefulSet *appsv1.StatefulSet - var instrumentedApplicationDeployment *odigosv1.InstrumentedApplication - var instrumentedApplicationDaemonSet *odigosv1.InstrumentedApplication - var instrumentedApplicationStatefulSet *odigosv1.InstrumentedApplication + var instrumentationConfigDeployment *odigosv1.InstrumentationConfig + var instrumentationConfigDaemonSet *odigosv1.InstrumentationConfig + var instrumentationConfigStatefulSet *odigosv1.InstrumentationConfig When("namespace instrumentation is disabled", func() { @@ -42,12 +42,12 @@ var _ = Describe("DeleteInstrumentedApplication Namespace controller", func() { Expect(k8sClient.Create(ctx, statefulSet)).Should(Succeed()) // these workloads has instrumentation application because the namespace has instrumentation enabled - instrumentedApplicationDeployment = testutil.NewMockInstrumentedApplication(deployment) - Expect(k8sClient.Create(ctx, instrumentedApplicationDeployment)).Should(Succeed()) - instrumentedApplicationDaemonSet = testutil.NewMockInstrumentedApplication(daemonSet) - Expect(k8sClient.Create(ctx, instrumentedApplicationDaemonSet)).Should(Succeed()) - instrumentedApplicationStatefulSet = testutil.NewMockInstrumentedApplication(statefulSet) - Expect(k8sClient.Create(ctx, instrumentedApplicationStatefulSet)).Should(Succeed()) + instrumentationConfigDeployment = testutil.NewMockInstrumentationConfig(deployment) + Expect(k8sClient.Create(ctx, instrumentationConfigDeployment)).Should(Succeed()) + instrumentationConfigDaemonSet = testutil.NewMockInstrumentationConfig(daemonSet) + Expect(k8sClient.Create(ctx, instrumentationConfigDaemonSet)).Should(Succeed()) + instrumentationConfigStatefulSet = testutil.NewMockInstrumentationConfig(statefulSet) + Expect(k8sClient.Create(ctx, instrumentationConfigStatefulSet)).Should(Succeed()) }) It("should delete instrumented application", func() { @@ -55,9 +55,9 @@ var _ = Describe("DeleteInstrumentedApplication Namespace controller", func() { namespace = testutil.SetOdigosInstrumentationDisabled(namespace) Expect(k8sClient.Update(ctx, namespace)).Should(Succeed()) - testutil.AssertInstrumentedApplicationDeleted(ctx, k8sClient, instrumentedApplicationDeployment) - testutil.AssertInstrumentedApplicationDeleted(ctx, k8sClient, instrumentedApplicationDaemonSet) - testutil.AssertInstrumentedApplicationDeleted(ctx, k8sClient, instrumentedApplicationStatefulSet) + testutil.AssertInstrumentationConfigDeleted(ctx, k8sClient, instrumentationConfigDeployment) + testutil.AssertInstrumentationConfigDeleted(ctx, k8sClient, instrumentationConfigDaemonSet) + testutil.AssertInstrumentationConfigDeleted(ctx, k8sClient, instrumentationConfigStatefulSet) }) It("should delete reported name annotation", func() { @@ -82,24 +82,22 @@ var _ = Describe("DeleteInstrumentedApplication Namespace controller", func() { Expect(k8sClient.Create(ctx, statefulSet)).Should(Succeed()) // these workloads has instrumentation application because the namespace has instrumentation enabled - instrumentedApplicationDeployment = testutil.NewMockInstrumentedApplication(deployment) - Expect(k8sClient.Create(ctx, instrumentedApplicationDeployment)).Should(Succeed()) - instrumentedApplicationDaemonSet = testutil.NewMockInstrumentedApplication(daemonSet) - Expect(k8sClient.Create(ctx, instrumentedApplicationDaemonSet)).Should(Succeed()) - instrumentedApplicationStatefulSet = testutil.NewMockInstrumentedApplication(statefulSet) - Expect(k8sClient.Create(ctx, instrumentedApplicationStatefulSet)).Should(Succeed()) + instrumentationConfigDeployment = testutil.NewMockInstrumentationConfig(deployment) + Expect(k8sClient.Create(ctx, instrumentationConfigDeployment)).Should(Succeed()) + instrumentationConfigDaemonSet = testutil.NewMockInstrumentationConfig(daemonSet) + Expect(k8sClient.Create(ctx, instrumentationConfigDaemonSet)).Should(Succeed()) + instrumentationConfigStatefulSet = testutil.NewMockInstrumentationConfig(statefulSet) + Expect(k8sClient.Create(ctx, instrumentationConfigStatefulSet)).Should(Succeed()) }) It("should retain instrumented application", func() { namespace = testutil.SetOdigosInstrumentationDisabled(namespace) Expect(k8sClient.Update(ctx, namespace)).Should(Succeed()) - testutil.AssertInstrumentedApplicationRetained(ctx, k8sClient, instrumentedApplicationDeployment) - testutil.AssertInstrumentedApplicationRetained(ctx, k8sClient, instrumentedApplicationDaemonSet) - testutil.AssertInstrumentedApplicationRetained(ctx, k8sClient, instrumentedApplicationStatefulSet) + testutil.AssertInstrumentationConfigRetained(ctx, k8sClient, instrumentationConfigDeployment) + testutil.AssertInstrumentationConfigRetained(ctx, k8sClient, instrumentationConfigDaemonSet) + testutil.AssertInstrumentationConfigRetained(ctx, k8sClient, instrumentationConfigStatefulSet) }) - }) - }) }) diff --git a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go similarity index 98% rename from instrumentor/controllers/deleteinstrumentedapplication/source_controller.go rename to instrumentor/controllers/deleteinstrumentationconfig/source_controller.go index acd8aba31..9f0d758b7 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/source_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deleteinstrumentedapplication +package deleteinstrumentationconfig import ( "context" diff --git a/instrumentor/controllers/deleteinstrumentedapplication/statefulset_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/statefulset_controller.go similarity index 97% rename from instrumentor/controllers/deleteinstrumentedapplication/statefulset_controller.go rename to instrumentor/controllers/deleteinstrumentationconfig/statefulset_controller.go index dc2ac4f6b..70f274f4a 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/statefulset_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/statefulset_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deleteinstrumentedapplication +package deleteinstrumentationconfig import ( "context" diff --git a/instrumentor/controllers/deleteinstrumentedapplication/suite_test.go b/instrumentor/controllers/deleteinstrumentationconfig/suite_test.go similarity index 92% rename from instrumentor/controllers/deleteinstrumentedapplication/suite_test.go rename to instrumentor/controllers/deleteinstrumentationconfig/suite_test.go index debe0bfa1..1a0543b0d 100644 --- a/instrumentor/controllers/deleteinstrumentedapplication/suite_test.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/suite_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deleteinstrumentedapplication_test +package deleteinstrumentationconfig_test import ( "context" @@ -32,7 +32,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" - "github.com/odigos-io/odigos/instrumentor/controllers/deleteinstrumentedapplication" + "github.com/odigos-io/odigos/instrumentor/controllers/deleteinstrumentationconfig" //+kubebuilder:scaffold:imports ) @@ -47,7 +47,7 @@ var ( func TestControllers(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "DeleteInstrumentedApplication Controllers Suite") + RunSpecs(t, "DeleteInstrumentationConfig Controllers Suite") } var _ = BeforeSuite(func() { @@ -81,7 +81,7 @@ var _ = BeforeSuite(func() { }) Expect(err).ToNot(HaveOccurred()) - err = deleteinstrumentedapplication.SetupWithManager(k8sManager) + err = deleteinstrumentationconfig.SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) go func() { diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 6e4269981..46e837d7d 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -41,7 +41,7 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if source.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(source, consts.SourceFinalizer) { controllerutil.AddFinalizer(source, consts.SourceFinalizer) - // Removed by deleteinstrumentedapplication controller + // Removed by deleteinstrumentationconfig controller controllerutil.AddFinalizer(source, consts.InstrumentedApplicationFinalizer) if source.Labels == nil { diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index 0ef0e7489..ab186bb15 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -4,7 +4,6 @@ import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/yaml" "k8s.io/apimachinery/pkg/runtime" @@ -89,9 +88,6 @@ func requestOdigletsToCalculateRuntimeDetails(ctx context.Context, k8sClient cli Name: instConfigName, Namespace: namespace, }, - Spec: odigosv1.InstrumentationConfigSpec{ - RuntimeDetailsInvalidated: true, - }, } if err := ctrl.SetControllerReference(obj, instConfig, scheme); err != nil { @@ -99,14 +95,11 @@ func requestOdigletsToCalculateRuntimeDetails(ctx context.Context, k8sClient cli return err } - instConfigBytes, _ := yaml.Marshal(instConfig) - - force := true - patchOptions := client.PatchOptions{ - FieldManager: "instrumentor", - Force: &force, + err := k8sClient.Create(ctx, instConfig) + if err != nil { + return client.IgnoreAlreadyExists(err) } logger.V(0).Info("Requested calculation of runtime details from odiglets", "name", instConfigName, "namespace", namespace) - return k8sClient.Patch(ctx, instConfig, client.RawPatch(types.ApplyPatchType, instConfigBytes), &patchOptions) + return nil } diff --git a/instrumentor/go.mod b/instrumentor/go.mod index 652605d95..fd3552bff 100644 --- a/instrumentor/go.mod +++ b/instrumentor/go.mod @@ -93,7 +93,7 @@ require ( k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect - sigs.k8s.io/yaml v1.4.0 + sigs.k8s.io/yaml v1.4.0 // indirect ) replace ( diff --git a/instrumentor/internal/testutil/assertions.go b/instrumentor/internal/testutil/assertions.go index fde46d00c..899a91a95 100644 --- a/instrumentor/internal/testutil/assertions.go +++ b/instrumentor/internal/testutil/assertions.go @@ -18,20 +18,20 @@ const ( interval = time.Millisecond * 250 ) -func AssertInstrumentedApplicationDeleted(ctx context.Context, k8sClient client.Client, instrumentedApplication *odigosv1.InstrumentedApplication) { - key := client.ObjectKey{Namespace: instrumentedApplication.GetNamespace(), Name: instrumentedApplication.GetName()} +func AssertInstrumentationConfigDeleted(ctx context.Context, k8sClient client.Client, instrumentationConfig *odigosv1.InstrumentationConfig) { + key := client.ObjectKey{Namespace: instrumentationConfig.GetNamespace(), Name: instrumentationConfig.GetName()} Eventually(func() bool { - var runtimeDetails odigosv1.InstrumentedApplication - err := k8sClient.Get(ctx, key, &runtimeDetails) + var ic odigosv1.InstrumentationConfig + err := k8sClient.Get(ctx, key, &ic) return apierrors.IsNotFound(err) }, timeout, interval).Should(BeTrue()) } -func AssertInstrumentedApplicationRetained(ctx context.Context, k8sClient client.Client, instrumentedApplication *odigosv1.InstrumentedApplication) { - key := client.ObjectKey{Namespace: instrumentedApplication.GetNamespace(), Name: instrumentedApplication.GetName()} +func AssertInstrumentationConfigRetained(ctx context.Context, k8sClient client.Client, instrumentationConfig *odigosv1.InstrumentationConfig) { + key := client.ObjectKey{Namespace: instrumentationConfig.GetNamespace(), Name: instrumentationConfig.GetName()} Consistently(func() bool { - var runtimeDetails odigosv1.InstrumentedApplication - err := k8sClient.Get(ctx, key, &runtimeDetails) + var ic odigosv1.InstrumentationConfig + err := k8sClient.Get(ctx, key, &ic) return err == nil }, duration, interval).Should(BeTrue()) } diff --git a/instrumentor/internal/testutil/helpers.go b/instrumentor/internal/testutil/helpers.go index c3bea5664..594b42cad 100644 --- a/instrumentor/internal/testutil/helpers.go +++ b/instrumentor/internal/testutil/helpers.go @@ -1,8 +1,6 @@ package testutil import ( - odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" - "github.com/odigos-io/odigos/common" "github.com/odigos-io/odigos/common/consts" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -33,20 +31,6 @@ func SetReportedNameAnnotation[W client.Object](obj W, reportedName string) W { return copy } -func SetInstrumentedApplicationContainer(obj *odigosv1.InstrumentedApplication, envName *string, envValue *string, language common.ProgrammingLanguage) *odigosv1.InstrumentedApplication { - copy := obj.DeepCopy() - copy.Spec.RuntimeDetails[0] = odigosv1.RuntimeDetailsByContainer{ - ContainerName: copy.Spec.RuntimeDetails[0].ContainerName, - Language: language, - } - - if envName != nil && envValue != nil { - copy.Spec.RuntimeDetails[0].EnvVars = []odigosv1.EnvVar{{Name: *envName, Value: *envValue}} - } - - return copy -} - func SetDeploymentContainerEnv(obj *appsv1.Deployment, envName string, envValue string) *appsv1.Deployment { copy := obj.DeepCopy() envVar := corev1.EnvVar{Name: envName, Value: envValue} diff --git a/instrumentor/internal/testutil/mocks.go b/instrumentor/internal/testutil/mocks.go index 87bd1cbab..1e0b982dc 100644 --- a/instrumentor/internal/testutil/mocks.go +++ b/instrumentor/internal/testutil/mocks.go @@ -137,9 +137,9 @@ func NewMockTestStatefulSet(ns *corev1.Namespace) *appsv1.StatefulSet { // givin a workload object (deployment, daemonset, statefulset) return a mock instrumented application // with a single container with the GoProgrammingLanguage -func NewMockInstrumentedApplication(workloadObject client.Object) *odigosv1.InstrumentedApplication { +func NewMockInstrumentationConfig(workloadObject client.Object) *odigosv1.InstrumentationConfig { gvk, _ := apiutil.GVKForObject(workloadObject, scheme.Scheme) - return &odigosv1.InstrumentedApplication{ + return &odigosv1.InstrumentationConfig{ ObjectMeta: metav1.ObjectMeta{ Name: workload.CalculateWorkloadRuntimeObjectName(workloadObject.GetName(), gvk.Kind), Namespace: workloadObject.GetNamespace(), @@ -152,8 +152,8 @@ func NewMockInstrumentedApplication(workloadObject client.Object) *odigosv1.Inst }, }, }, - Spec: odigosv1.InstrumentedApplicationSpec{ - RuntimeDetails: []odigosv1.RuntimeDetailsByContainer{ + Status: odigosv1.InstrumentationConfigStatus{ + RuntimeDetailsByContainer: []odigosv1.RuntimeDetailsByContainer{ { ContainerName: "test", Language: common.GoProgrammingLanguage, diff --git a/instrumentor/main.go b/instrumentor/main.go index 55a6dc6e7..b979f7bb0 100644 --- a/instrumentor/main.go +++ b/instrumentor/main.go @@ -43,7 +43,7 @@ import ( odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" - "github.com/odigos-io/odigos/instrumentor/controllers/deleteinstrumentedapplication" + "github.com/odigos-io/odigos/instrumentor/controllers/deleteinstrumentationconfig" "github.com/odigos-io/odigos/instrumentor/controllers/instrumentationdevice" "github.com/odigos-io/odigos/instrumentor/report" @@ -170,7 +170,7 @@ func main() { os.Exit(1) } - err = deleteinstrumentedapplication.SetupWithManager(mgr) + err = deleteinstrumentationconfig.SetupWithManager(mgr) if err != nil { setupLog.Error(err, "unable to create controller") os.Exit(1) diff --git a/k8sutils/pkg/describe/source.go b/k8sutils/pkg/describe/source.go index 1928162ac..8a1318b4d 100644 --- a/k8sutils/pkg/describe/source.go +++ b/k8sutils/pkg/describe/source.go @@ -55,12 +55,19 @@ func printInstrumentationConfigInfo(analyze *source.SourceAnalyze, sb *strings.B printProperty(sb, 2, &container.ContainerName) printProperty(sb, 3, &container.Language) printProperty(sb, 3, &container.RuntimeVersion) + printProperty(sb, 3, &container.CriError) if len(container.EnvVars) > 0 { describeText(sb, 3, "Relevant Environment Variables:") for _, envVar := range container.EnvVars { describeText(sb, 4, fmt.Sprintf("%s: %s", envVar.Name, envVar.Value)) } } + if len(container.ContainerRuntimeEnvs) > 0 { + describeText(sb, 3, "Relevant Container Runtime Environment Variables:") + for _, containerRuntimeEnvVar := range container.ContainerRuntimeEnvs { + describeText(sb, 4, fmt.Sprintf("%s: %s", containerRuntimeEnvVar.Name, containerRuntimeEnvVar.Value)) + } + } } } diff --git a/k8sutils/pkg/predicate/creation.go b/k8sutils/pkg/predicate/creation.go index 481418370..d2ee8f674 100644 --- a/k8sutils/pkg/predicate/creation.go +++ b/k8sutils/pkg/predicate/creation.go @@ -24,4 +24,4 @@ func (i CreationPredicate) Generic(e event.GenericEvent) bool { return false } -var _ predicate.Predicate = &DeletionPredicate{} +var _ predicate.Predicate = &CreationPredicate{} diff --git a/odiglet/pkg/kube/instrumentation_ebpf/shared.go b/odiglet/pkg/kube/instrumentation_ebpf/shared.go index 71f95dcc7..d23ad9e29 100644 --- a/odiglet/pkg/kube/instrumentation_ebpf/shared.go +++ b/odiglet/pkg/kube/instrumentation_ebpf/shared.go @@ -24,7 +24,7 @@ func cleanupEbpf(directors ebpf.DirectorsMap, name types.NamespacedName) { } } -func instrumentPodWithEbpf(ctx context.Context, pod *corev1.Pod, directors ebpf.DirectorsMap, runtimeDetails *odigosv1.InstrumentedApplication, podWorkload *workload.PodWorkload) (error, bool) { +func instrumentPodWithEbpf(ctx context.Context, pod *corev1.Pod, directors ebpf.DirectorsMap, runtimeDetails *odigosv1.InstrumentationConfig, podWorkload *workload.PodWorkload) (error, bool) { logger := log.FromContext(ctx) podUid := string(pod.UID) instrumentedEbpf := false @@ -82,9 +82,9 @@ func instrumentPodWithEbpf(ctx context.Context, pod *corev1.Pod, directors ebpf. return nil, instrumentedEbpf } -func getIgnoredContainers(instApp *odigosv1.InstrumentedApplication) map[string]struct{} { +func getIgnoredContainers(instConfig *odigosv1.InstrumentationConfig) map[string]struct{} { ignoredContainers := make(map[string]struct{}) - for _, container := range instApp.Spec.RuntimeDetails { + for _, container := range instConfig.Status.RuntimeDetailsByContainer { if container.Language == common.IgnoredProgrammingLanguage { ignoredContainers[container.ContainerName] = struct{}{} } diff --git a/odiglet/pkg/kube/runtime_details/inspection.go b/odiglet/pkg/kube/runtime_details/inspection.go index 454e13799..972270970 100644 --- a/odiglet/pkg/kube/runtime_details/inspection.go +++ b/odiglet/pkg/kube/runtime_details/inspection.go @@ -3,7 +3,6 @@ package runtime_details import ( "context" "encoding/json" - "errors" "fmt" "strings" @@ -13,67 +12,20 @@ import ( "github.com/odigos-io/odigos/odiglet/pkg/process" - "github.com/go-logr/logr" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" "github.com/odigos-io/odigos/common/envOverwrite" "github.com/odigos-io/odigos/common/utils" criwrapper "github.com/odigos-io/odigos/k8sutils/pkg/cri" - k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/utils" "github.com/odigos-io/odigos/k8sutils/pkg/workload" kubeutils "github.com/odigos-io/odigos/odiglet/pkg/kube/utils" "github.com/odigos-io/odigos/odiglet/pkg/log" "github.com/odigos-io/odigos/procdiscovery/pkg/inspectors" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) -var errNoPodsFound = errors.New("no pods found") - -func ignoreNoPodsFoundError(err error) error { - if err.Error() == errNoPodsFound.Error() { - return nil - } - return err -} - -func inspectRuntimesOfRunningPods(ctx context.Context, logger *logr.Logger, labels map[string]string, - kubeClient client.Client, scheme *runtime.Scheme, object client.Object) error { - pods, err := kubeutils.GetRunningPods(ctx, labels, object.GetNamespace(), kubeClient) - if err != nil { - logger.Error(err, "error fetching running pods") - return err - } - - if len(pods) == 0 { - return errNoPodsFound - } - - odigosConfig, err := k8sutils.GetCurrentOdigosConfig(ctx, kubeClient) - if err != nil { - logger.Error(err, "failed to get odigos config") - return err - } - - runtimeResults, err := runtimeInspection(ctx, pods, odigosConfig.IgnoredContainers, nil) - if err != nil { - logger.Error(err, "error inspecting pods") - return err - } - - err = persistRuntimeResults(ctx, runtimeResults, object, kubeClient, scheme) - if err != nil { - logger.Error(err, "error persisting runtime results") - return err - } - - return nil -} - func runtimeInspection(ctx context.Context, pods []corev1.Pod, ignoredContainers []string, criClient *criwrapper.CriClient) ([]odigosv1.RuntimeDetailsByContainer, error) { resultsMap := make(map[string]odigosv1.RuntimeDetailsByContainer) for _, pod := range pods { @@ -168,7 +120,7 @@ func runtimeInspection(ctx context.Context, pods []corev1.Pod, ignoredContainers LibCType: libcType, } - if criClient != nil && inspectProc != nil { // CriClient passed as nil in cases that will be deprecated in the future [InstrumentedApplication] + if inspectProc != nil { procEnvVars := inspectProc.Environments.OverwriteEnvs updateRuntimeDetailsWithContainerRuntimeEnvs(ctx, *criClient, pod, container, programLanguageDetails, &resultsMap, procEnvVars) } @@ -310,41 +262,10 @@ func persistRuntimeDetailsToInstrumentationConfig(ctx context.Context, kubeclien return nil } -func persistRuntimeResults(ctx context.Context, results []odigosv1.RuntimeDetailsByContainer, owner client.Object, kubeClient client.Client, scheme *runtime.Scheme) error { - updatedIa := &odigosv1.InstrumentedApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: workload.CalculateWorkloadRuntimeObjectName(owner.GetName(), owner.GetObjectKind().GroupVersionKind().Kind), - Namespace: owner.GetNamespace(), - }, - } - - err := controllerutil.SetControllerReference(owner, updatedIa, scheme) - if err != nil { - log.Logger.Error(err, "Failed to set controller reference") - return err - } - - operationResult, err := controllerutil.CreateOrPatch(ctx, kubeClient, updatedIa, func() error { - updatedIa.Spec.RuntimeDetails = results - return nil - }) - - if err != nil { - log.Logger.Error(err, "Failed to update runtime info", "name", owner.GetName(), "kind", - owner.GetObjectKind().GroupVersionKind().Kind, "namespace", owner.GetNamespace()) - } - - if operationResult != controllerutil.OperationResultNone { - log.Logger.V(0).Info("updated runtime info", "result", operationResult, "name", owner.GetName(), "kind", - owner.GetObjectKind().GroupVersionKind().Kind, "namespace", owner.GetNamespace()) - } - return nil -} - -func GetRuntimeDetails(ctx context.Context, kubeClient client.Client, podWorkload *workload.PodWorkload) (*odigosv1.InstrumentedApplication, error) { +func GetRuntimeDetails(ctx context.Context, kubeClient client.Client, podWorkload *workload.PodWorkload) (*odigosv1.InstrumentationConfig, error) { instrumentedApplicationName := workload.CalculateWorkloadRuntimeObjectName(podWorkload.Name, podWorkload.Kind) - var runtimeDetails odigosv1.InstrumentedApplication + var runtimeDetails odigosv1.InstrumentationConfig err := kubeClient.Get(ctx, client.ObjectKey{ Namespace: podWorkload.Namespace, Name: instrumentedApplicationName, diff --git a/odiglet/pkg/kube/runtime_details/instconfig_controller.go b/odiglet/pkg/kube/runtime_details/instconfig_controller.go deleted file mode 100644 index 0358212cb..000000000 --- a/odiglet/pkg/kube/runtime_details/instconfig_controller.go +++ /dev/null @@ -1,103 +0,0 @@ -package runtime_details - -import ( - "context" - "errors" - "fmt" - - "github.com/odigos-io/odigos/k8sutils/pkg/workload" - - appsv1 "k8s.io/api/apps/v1" - - odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/log" - - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -// replaced by odiglet/pkg/kube/runtime_details/instrumentationconfigs_controller.go -// which does not rely on the RuntimeDetailsInvalidated flag -// left here until the migration is complete -// Deprecated: the new runtime inspection logic is found in odiglet/pkg/kube/runtime_details/instrumentationconfigs_controller.go -type DeprecatedInstrumentationConfigReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -func (i *DeprecatedInstrumentationConfigReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { - logger := log.FromContext(ctx) - - var instConfig odigosv1.InstrumentationConfig - err := i.Get(ctx, request.NamespacedName, &instConfig) - if err != nil { - if client.IgnoreNotFound(err) != nil { - logger.Error(err, "Failed to get InstrumentationConfig") - return reconcile.Result{}, err - } - return reconcile.Result{}, nil - } - - // This reconciler is only interested in InstrumentationConfig objects that have their RuntimeDetailsInvalidated field set to true - if !instConfig.Spec.RuntimeDetailsInvalidated { - return reconcile.Result{}, nil - } - - if len(instConfig.OwnerReferences) != 1 { - return reconcile.Result{}, fmt.Errorf("InstrumentationConfig %s/%s has %d owner references, expected 1", instConfig.Namespace, instConfig.Name, len(instConfig.OwnerReferences)) - } - - workload, labels, err := getWorkloadAndLabelsfromOwner(ctx, i.Client, instConfig.Namespace, instConfig.OwnerReferences[0]) - if err != nil { - logger.Error(err, "Failed to get workload and labels from owner") - return reconcile.Result{}, err - } - err = inspectRuntimesOfRunningPods(ctx, &logger, labels, i.Client, i.Scheme, workload) - if err != nil { - return reconcile.Result{}, ignoreNoPodsFoundError(err) - } - - // Patch RuntimeDetailsInvalidated to false after runtime details have been recalculated - updated := instConfig.DeepCopy() - updated.Spec.RuntimeDetailsInvalidated = false - patch := client.MergeFrom(&instConfig) - err = i.Patch(ctx, updated, patch) - return reconcile.Result{}, err -} - -func getWorkloadAndLabelsfromOwner(ctx context.Context, k8sClient client.Client, ns string, ownerReference metav1.OwnerReference) (client.Object, map[string]string, error) { - workloadName, workloadKind, err := workload.GetWorkloadFromOwnerReference(ownerReference) - if err != nil { - return nil, nil, err - } - - switch workloadKind { - case "Deployment": - var dep appsv1.Deployment - err := k8sClient.Get(ctx, client.ObjectKey{Namespace: ns, Name: workloadName}, &dep) - if err != nil { - return nil, nil, err - } - return &dep, dep.Spec.Selector.MatchLabels, nil - case "DaemonSet": - var ds appsv1.DaemonSet - err := k8sClient.Get(ctx, client.ObjectKey{Namespace: ns, Name: workloadName}, &ds) - if err != nil { - return nil, nil, err - } - - return &ds, ds.Spec.Selector.MatchLabels, nil - case "StatefulSet": - var sts appsv1.StatefulSet - err := k8sClient.Get(ctx, client.ObjectKey{Namespace: ns, Name: workloadName}, &sts) - if err != nil { - return nil, nil, err - } - - return &sts, sts.Spec.Selector.MatchLabels, nil - } - - return nil, nil, errors.New("workload kind not supported") -} diff --git a/odiglet/pkg/kube/runtime_details/instrumentationconfigs_controller.go b/odiglet/pkg/kube/runtime_details/instrumentationconfigs_controller.go index 886519ca8..23b11576c 100644 --- a/odiglet/pkg/kube/runtime_details/instrumentationconfigs_controller.go +++ b/odiglet/pkg/kube/runtime_details/instrumentationconfigs_controller.go @@ -2,13 +2,17 @@ package runtime_details import ( "context" + "errors" "fmt" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" criwrapper "github.com/odigos-io/odigos/k8sutils/pkg/cri" k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/utils" kubeutils "github.com/odigos-io/odigos/odiglet/pkg/kube/utils" corev1 "k8s.io/api/core/v1" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" @@ -105,3 +109,39 @@ func (r *InstrumentationConfigReconciler) Reconcile(ctx context.Context, request logger.Info("Completed runtime detection for new instrumentation config", "namespace", request.Namespace, "name", request.Name) return reconcile.Result{}, nil } + +func getWorkloadAndLabelsfromOwner(ctx context.Context, k8sClient client.Client, ns string, ownerReference metav1.OwnerReference) (client.Object, map[string]string, error) { + workloadName, workloadKind, err := workload.GetWorkloadFromOwnerReference(ownerReference) + if err != nil { + return nil, nil, err + } + + switch workloadKind { + case "Deployment": + var dep appsv1.Deployment + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: ns, Name: workloadName}, &dep) + if err != nil { + return nil, nil, err + } + return &dep, dep.Spec.Selector.MatchLabels, nil + case "DaemonSet": + var ds appsv1.DaemonSet + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: ns, Name: workloadName}, &ds) + if err != nil { + return nil, nil, err + } + + return &ds, ds.Spec.Selector.MatchLabels, nil + case "StatefulSet": + var sts appsv1.StatefulSet + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: ns, Name: workloadName}, &sts) + if err != nil { + return nil, nil, err + } + + return &sts, sts.Spec.Selector.MatchLabels, nil + } + + return nil, nil, errors.New("workload kind not supported") +} + diff --git a/odiglet/pkg/kube/runtime_details/manager.go b/odiglet/pkg/kube/runtime_details/manager.go index a05ab61f1..1658f339e 100644 --- a/odiglet/pkg/kube/runtime_details/manager.go +++ b/odiglet/pkg/kube/runtime_details/manager.go @@ -13,18 +13,6 @@ import ( func SetupWithManager(mgr ctrl.Manager, clientset *kubernetes.Clientset, criClient *criwrapper.CriClient) error { err := builder. - ControllerManagedBy(mgr). - For(&odigosv1.InstrumentationConfig{}). - Owns(&odigosv1.InstrumentedApplication{}). - Complete(&DeprecatedInstrumentationConfigReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - }) - if err != nil { - return err - } - - err = builder. ControllerManagedBy(mgr). Named("Odiglet-RuntimeDetails-Pods"). For(&corev1.Pod{}). diff --git a/tests/common/assert/simple-demo-runtime-detected.yaml b/tests/common/assert/simple-demo-runtime-detected.yaml index 92f893f6a..86b293707 100644 --- a/tests/common/assert/simple-demo-runtime-detected.yaml +++ b/tests/common/assert/simple-demo-runtime-detected.yaml @@ -1,84 +1,4 @@ apiVersion: odigos.io/v1alpha1 -kind: InstrumentedApplication -metadata: - name: deployment-coupon - namespace: default - ownerReferences: - - apiVersion: apps/v1 - blockOwnerDeletion: true - controller: true - kind: Deployment - name: coupon -spec: - runtimeDetails: - - containerName: coupon - language: javascript ---- -apiVersion: odigos.io/v1alpha1 -kind: InstrumentedApplication -metadata: - name: deployment-frontend - namespace: default - ownerReferences: - - apiVersion: apps/v1 - blockOwnerDeletion: true - controller: true - kind: Deployment - name: frontend -spec: - runtimeDetails: - - containerName: frontend - language: java ---- -apiVersion: odigos.io/v1alpha1 -kind: InstrumentedApplication -metadata: - name: deployment-inventory - namespace: default - ownerReferences: - - apiVersion: apps/v1 - blockOwnerDeletion: true - controller: true - kind: Deployment - name: inventory -spec: - runtimeDetails: - - containerName: inventory - language: python ---- -apiVersion: odigos.io/v1alpha1 -kind: InstrumentedApplication -metadata: - name: deployment-membership - namespace: default - ownerReferences: - - apiVersion: apps/v1 - blockOwnerDeletion: true - controller: true - kind: Deployment - name: membership -spec: - runtimeDetails: - - containerName: membership - language: go ---- -apiVersion: odigos.io/v1alpha1 -kind: InstrumentedApplication -metadata: - name: deployment-pricing - namespace: default - ownerReferences: - - apiVersion: apps/v1 - blockOwnerDeletion: true - controller: true - kind: Deployment - name: pricing -spec: - runtimeDetails: - - containerName: pricing - language: dotnet ---- -apiVersion: odigos.io/v1alpha1 kind: InstrumentationConfig metadata: name: deployment-coupon diff --git a/tests/e2e/workload-lifecycle/services/dotnet-http-server/net6-glibc.Dockerfile b/tests/e2e/workload-lifecycle/services/dotnet-http-server/net6-glibc.Dockerfile index 0f3594d90..916185c82 100644 --- a/tests/e2e/workload-lifecycle/services/dotnet-http-server/net6-glibc.Dockerfile +++ b/tests/e2e/workload-lifecycle/services/dotnet-http-server/net6-glibc.Dockerfile @@ -1,7 +1,7 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY . . -ENV USE_DOTNET6 true +ENV USE_DOTNET6=true RUN dotnet restore RUN dotnet publish -c Release -o /app diff --git a/tests/e2e/workload-lifecycle/services/dotnet-http-server/net6-musl.Dockerfile b/tests/e2e/workload-lifecycle/services/dotnet-http-server/net6-musl.Dockerfile index 46c7f32c0..32c30c8ba 100644 --- a/tests/e2e/workload-lifecycle/services/dotnet-http-server/net6-musl.Dockerfile +++ b/tests/e2e/workload-lifecycle/services/dotnet-http-server/net6-musl.Dockerfile @@ -1,7 +1,7 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build WORKDIR /src COPY . . -ENV USE_DOTNET6 true +ENV USE_DOTNET6=true RUN dotnet restore RUN dotnet publish -c Release -o /app From a9fd8bfa9b159614c3c4d5222551984f758460f3 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Wed, 8 Jan 2025 11:03:49 +0200 Subject: [PATCH 202/259] chore: wip --- autoscaler/controllers/common/processors.go | 41 ++++--- autoscaler/controllers/gateway/configmap.go | 8 +- common/config/axiom.go | 11 +- common/config/elasticsearch.go | 12 +- common/config/jaeger.go | 17 +-- common/config/root.go | 120 +++++++++++--------- 6 files changed, 119 insertions(+), 90 deletions(-) diff --git a/autoscaler/controllers/common/processors.go b/autoscaler/controllers/common/processors.go index ccf87351d..4eb113418 100644 --- a/autoscaler/controllers/common/processors.go +++ b/autoscaler/controllers/common/processors.go @@ -5,11 +5,13 @@ import ( "encoding/json" "fmt" "sort" + "strings" "github.com/go-logr/logr" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/autoscaler/utils" "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/common/config" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -84,10 +86,16 @@ type MatchCondition struct { Kind string `mapstructure:"kind"` } -func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProcessors *odigosv1.ProcessorList, dests *odigosv1.DestinationList) { +func GenerateRoutingProcessors( + ctx context.Context, + kubeClient client.Client, + dests *odigosv1.DestinationList, +) map[string]config.GenericMap { + logger := log.FromContext(ctx) + routingProcessors := make(map[string]config.GenericMap) + for _, dest := range dests.Items { - logger := log.Log.WithValues("destination", dest.Name) - logger.Info("Processing destination for filter processor") + logger.Info("Processing destination for routing processor", "destination", dest.Name) if dest.Spec.SourceSelector == nil || utils.Contains(dest.Spec.SourceSelector.Modes, "all") { continue @@ -103,32 +111,21 @@ func AddFilterProcessors(ctx context.Context, kubeClient client.Client, allProce matchConditions := make(map[string]bool) for _, source := range matchedSources { - logger.Info("Adding match condition for source", "sourceName", source.Spec.Workload.Name, "namespace", source.Spec.Workload.Namespace, "kind", source.Spec.Workload.Kind) - + //TODO: cahnge the key key := fmt.Sprintf("%s/%s/%s", source.Spec.Workload.Namespace, source.Spec.Workload.Name, source.Spec.Workload.Kind) matchConditions[key] = true } - filterConfig := map[string]interface{}{ + sanitizedProcessorName := strings.ReplaceAll(dest.GetID(), ".", "-") + processorName := fmt.Sprintf("odigosroutingfilterprocessor/%s", sanitizedProcessorName) + fmt.Println("dest.GetID() sanitizedProcessorName: ", sanitizedProcessorName) + fmt.Println("processorName: ", processorName) + routingProcessors[processorName] = config.GenericMap{ "match_conditions": matchConditions, } - - allProcessors.Items = append(allProcessors.Items, odigosv1.Processor{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("odigosroutingfilterprocessor-%s", dest.Name), - }, - Spec: odigosv1.ProcessorSpec{ - Type: "odigosroutingfilterprocessor", - ProcessorConfig: runtime.RawExtension{Raw: utils.MarshalConfig(filterConfig, logger)}, - CollectorRoles: []odigosv1.CollectorsGroupRole{ - odigosv1.CollectorsGroupRoleClusterGateway, - }, - OrderHint: len(allProcessors.Items) + 1, - Signals: dest.Spec.Signals, - }, - }) - } + + return routingProcessors } func fetchSourcesByNamespaces(ctx context.Context, kubeClient client.Client, namespaces []string, logger logr.Logger) []odigosv1.Source { diff --git a/autoscaler/controllers/gateway/configmap.go b/autoscaler/controllers/gateway/configmap.go index 43936026f..30805ef54 100644 --- a/autoscaler/controllers/gateway/configmap.go +++ b/autoscaler/controllers/gateway/configmap.go @@ -114,9 +114,13 @@ func addSelfTelemetryPipeline(c *config.Config, ownTelemetryPort int32) error { func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.ProcessorList, gateway *odigosv1.CollectorsGroup, ctx context.Context, c client.Client, scheme *runtime.Scheme) ([]odigoscommon.ObservabilitySignal, error) { logger := log.FromContext(ctx) memoryLimiterConfiguration := common.GetMemoryLimiterConfig(gateway.Spec.ResourcesSettings) - common.AddFilterProcessors(ctx, c, allProcessors, dests) + + routingProcessors := common.GenerateRoutingProcessors(ctx, c, dests) processors := common.FilterAndSortProcessorsByOrderHint(allProcessors, odigosv1.CollectorsGroupRoleClusterGateway) + logger.V(0).Info("Calculating config") + logger.V(0).Info("routingProcessors", "routingProcessors", routingProcessors) + desiredData, err, status, signals := config.Calculate( common.ToExporterConfigurerArray(dests), common.ToProcessorConfigurerArray(processors), @@ -124,7 +128,9 @@ func syncConfigMap(dests *odigosv1.DestinationList, allProcessors *odigosv1.Proc func(c *config.Config) error { return addSelfTelemetryPipeline(c, gateway.Spec.CollectorOwnMetricsPort) }, + routingProcessors, ) + if err != nil { logger.Error(err, "Failed to calculate config") return nil, err diff --git a/common/config/axiom.go b/common/config/axiom.go index 8ffcffdc6..91297c122 100644 --- a/common/config/axiom.go +++ b/common/config/axiom.go @@ -10,11 +10,14 @@ const ( type Axiom struct{} +// compile time checks +var _ Configer = (*Axiom)(nil) + func (a *Axiom) DestType() common.DestinationType { return common.AxiomDestinationType } -func (a *Axiom) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error { +func (a *Axiom) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) ([]string, error) { dataset, exists := dest.GetConfig()[axiomDatasetKey] if !exists { dataset = "default" @@ -31,11 +34,14 @@ func (a *Axiom) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) err }, } + pipelineNames := []string{} + if isTracingEnabled(dest) { tracesPipelineName := "traces/axiom-" + dest.GetID() currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ Exporters: []string{axiomExporterName}, } + pipelineNames = append(pipelineNames, tracesPipelineName) } if isLoggingEnabled(dest) { @@ -43,7 +49,8 @@ func (a *Axiom) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) err currentConfig.Service.Pipelines[logsPipelineName] = Pipeline{ Exporters: []string{axiomExporterName}, } + pipelineNames = append(pipelineNames, logsPipelineName) } - return nil + return pipelineNames, nil } diff --git a/common/config/elasticsearch.go b/common/config/elasticsearch.go index ef0c8ebc7..2dd81627f 100644 --- a/common/config/elasticsearch.go +++ b/common/config/elasticsearch.go @@ -27,15 +27,15 @@ func (e *Elasticsearch) DestType() common.DestinationType { return common.ElasticsearchDestinationType } -func (e *Elasticsearch) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error { +func (e *Elasticsearch) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) ([]string, error) { rawURL, exists := dest.GetConfig()[ElasticsearchUrlKey] if !exists { - return errors.New("ElasticSearch url not specified, gateway will not be configured for ElasticSearch") + return nil, errors.New("ElasticSearch url not specified, gateway will not be configured for ElasticSearch") } parsedURL, err := e.SanitizeURL(rawURL) if err != nil { - return errors.Join(err, errors.New(fmt.Sprintf("failed to sanitize URL. elasticsearch-url: %s", rawURL))) + return nil, errors.Join(err, errors.New(fmt.Sprintf("failed to sanitize URL. elasticsearch-url: %s", rawURL))) } traceIndexVal, exists := dest.GetConfig()[esTracesIndexKey] @@ -70,11 +70,14 @@ func (e *Elasticsearch) ModifyConfig(dest ExporterConfigurer, currentConfig *Con exporterName := "elasticsearch/" + dest.GetID() currentConfig.Exporters[exporterName] = exporterConfig + pipelineNames := []string{} + if isTracingEnabled(dest) { tracesPipelineName := "traces/elasticsearch-" + dest.GetID() currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ Exporters: []string{exporterName}, } + pipelineNames = append(pipelineNames, tracesPipelineName) } if isLoggingEnabled(dest) { @@ -82,9 +85,10 @@ func (e *Elasticsearch) ModifyConfig(dest ExporterConfigurer, currentConfig *Con currentConfig.Service.Pipelines[logsPipelineName] = Pipeline{ Exporters: []string{exporterName}, } + pipelineNames = append(pipelineNames, logsPipelineName) } - return nil + return pipelineNames, nil } // SanitizeURL will check whether URL is correct by utilizing url.ParseRequestURI diff --git a/common/config/jaeger.go b/common/config/jaeger.go index c6fb7596e..87de50b70 100644 --- a/common/config/jaeger.go +++ b/common/config/jaeger.go @@ -28,13 +28,13 @@ func (j *Jaeger) DestType() common.DestinationType { return common.JaegerDestinationType } -func (j *Jaeger) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error { +func (j *Jaeger) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) ([]string, error) { config := dest.GetConfig() uniqueUri := "jaeger-" + dest.GetID() url, urlExists := config[JaegerUrlKey] if !urlExists { - return ErrorJaegerMissingURL + return nil, ErrorJaegerMissingURL } tls := dest.GetConfig()[JaegerTlsKey] @@ -42,7 +42,7 @@ func (j *Jaeger) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) er endpoint, err := parseOtlpGrpcUrl(url, tlsEnabled) if err != nil { - return err + return nil, err } exporterName := "otlp/" + uniqueUri @@ -59,23 +59,24 @@ func (j *Jaeger) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) er exporterConfig["tls"] = tlsConfig currentConfig.Exporters[exporterName] = exporterConfig - + pipelineNames := []string{} if isTracingEnabled(dest) { tracesPipelineName := "traces/" + uniqueUri currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ Exporters: []string{exporterName}, } + pipelineNames = append(pipelineNames, tracesPipelineName) } else { - return ErrorJaegerTracingDisabled + return nil, ErrorJaegerTracingDisabled } if isMetricsEnabled(dest) { - return ErrorJaegerMetricsNotAllowed + return nil, ErrorJaegerMetricsNotAllowed } if isLoggingEnabled(dest) { - return ErrorJaegerLogsNotAllowed + return nil, ErrorJaegerLogsNotAllowed } - return nil + return pipelineNames, nil } diff --git a/common/config/root.go b/common/config/root.go index fbbebb5bf..d2b9458ad 100644 --- a/common/config/root.go +++ b/common/config/root.go @@ -15,58 +15,58 @@ const ( ) var availableConfigers = []Configer{ - &AppDynamics{}, + // &AppDynamics{}, &Axiom{}, - &AWSS3{}, - &AzureBlobStorage{}, - &BetterStack{}, - &Causely{}, - &Chronosphere{}, - &Clickhouse{}, - &Coralogix{}, - &Dash0{}, - &Datadog{}, - &Debug{}, - &Dynatrace{}, - &ElasticAPM{}, - &Elasticsearch{}, - &GenericOTLP{}, - &GoogleCloud{}, - &GoogleCloudStorage{}, - &GrafanaCloudLoki{}, - &GrafanaCloudPrometheus{}, - &GrafanaCloudTempo{}, - &Groundcover{}, - &Honeycomb{}, - &HyperDX{}, + // &AWSS3{}, + // &AzureBlobStorage{}, + // &BetterStack{}, + // &Causely{}, + // &Chronosphere{}, + // &Clickhouse{}, + // &Coralogix{}, + // &Dash0{}, + // &Datadog{}, + // &Debug{}, + // &Dynatrace{}, + // &ElasticAPM{}, + // &Elasticsearch{}, + // &GenericOTLP{}, + // &GoogleCloud{}, + // &GoogleCloudStorage{}, + // &GrafanaCloudLoki{}, + // &GrafanaCloudPrometheus{}, + // &GrafanaCloudTempo{}, + // &Groundcover{}, + // &Honeycomb{}, + // &HyperDX{}, &Jaeger{}, - &KloudMate{}, - &Last9{}, - &Lightstep{}, - &Logzio{}, - &Loki{}, - &Lumigo{}, - &Middleware{}, - &Mock{}, - &NewRelic{}, - &Nop{}, - &OpsVerse{}, - &OTLPHttp{}, - &Prometheus{}, - &Qryn{}, - &QrynOSS{}, - &Quickwit{}, - &Sentry{}, - &Signoz{}, - &Splunk{}, - &SumoLogic{}, - &Tempo{}, - &Uptrace{}, + // &KloudMate{}, + // &Last9{}, + // &Lightstep{}, + // &Logzio{}, + // &Loki{}, + // &Lumigo{}, + // &Middleware{}, + // &Mock{}, + // &NewRelic{}, + // &Nop{}, + // &OpsVerse{}, + // &OTLPHttp{}, + // &Prometheus{}, + // &Qryn{}, + // &QrynOSS{}, + // &Quickwit{}, + // &Sentry{}, + // &Signoz{}, + // &Splunk{}, + // &SumoLogic{}, + // &Tempo{}, + // &Uptrace{}, } type Configer interface { DestType() common.DestinationType - ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error + ModifyConfig(dest ExporterConfigurer, currentConfig *Config) ([]string, error) } type ResourceStatuses struct { @@ -74,12 +74,12 @@ type ResourceStatuses struct { Processor map[string]error } -func Calculate(dests []ExporterConfigurer, processors []ProcessorConfigurer, memoryLimiterConfig GenericMap, applySelfTelemetry func(c *Config) error) (string, error, *ResourceStatuses, []common.ObservabilitySignal) { +func Calculate(dests []ExporterConfigurer, processors []ProcessorConfigurer, memoryLimiterConfig GenericMap, applySelfTelemetry func(c *Config) error, routingProcessors map[string]GenericMap) (string, error, *ResourceStatuses, []common.ObservabilitySignal) { currentConfig, prefixProcessors := getBasicConfig(memoryLimiterConfig) - return CalculateWithBase(currentConfig, prefixProcessors, dests, processors, applySelfTelemetry) + return CalculateWithBase(currentConfig, prefixProcessors, dests, processors, applySelfTelemetry, routingProcessors) } -func CalculateWithBase(currentConfig *Config, prefixProcessors []string, dests []ExporterConfigurer, processors []ProcessorConfigurer, applySelfTelemetry func(c *Config) error) (string, error, *ResourceStatuses, []common.ObservabilitySignal) { +func CalculateWithBase(currentConfig *Config, prefixProcessors []string, dests []ExporterConfigurer, processors []ProcessorConfigurer, applySelfTelemetry func(c *Config) error, routingProcessors map[string]GenericMap) (string, error, *ResourceStatuses, []common.ObservabilitySignal) { configers, err := LoadConfigers() if err != nil { return "", err, nil, nil @@ -101,19 +101,33 @@ func CalculateWithBase(currentConfig *Config, prefixProcessors []string, dests [ return "", fmt.Errorf("missing required receiver 'otlp' on config"), status, nil } + // for processorKey, processorCfg := range routingProcessors { + // currentConfig.Processors[processorKey] = processorCfg + // } + fmt.Println("dests: ", dests) for _, dest := range dests { configer, exists := configers[dest.GetType()] if !exists { status.Destination[dest.GetID()] = fmt.Errorf("no configer for %s", dest.GetType()) continue } + sanitizedProcessorName := fmt.Sprintf("odigosroutingfilterprocessor/%s", strings.ReplaceAll(dest.GetID(), ".", "-")) - err := configer.ModifyConfig(dest, currentConfig) + pipelineNames, err := configer.ModifyConfig(dest, currentConfig) status.Destination[dest.GetID()] = err - // If configurer ran without errors, but there were no signals enabled, warn the user - if len(dest.GetSignals()) == 0 && err == nil { - status.Destination[dest.GetID()] = fmt.Errorf("no signals enabled for %s(%s)", dest.GetID(), dest.GetType()) + for _, pipelineName := range pipelineNames { + if pipeline, ok := currentConfig.Service.Pipelines[pipelineName]; ok { + // Add routing processor only if it exists + if routingProcessor, ok := routingProcessors[sanitizedProcessorName]; ok { + currentConfig.Processors[sanitizedProcessorName] = routingProcessor + pipeline.Processors = append(pipeline.Processors, sanitizedProcessorName) + } + + currentConfig.Service.Pipelines[pipelineName] = pipeline + } else { + status.Destination[dest.GetID()] = fmt.Errorf("pipeline %s not found", pipelineName) + } } } From f496a5270b8917cf18e00e8c2d39369894a6cc75 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Wed, 8 Jan 2025 13:39:30 -0500 Subject: [PATCH 203/259] Sync main into feature/source-crd (#2168) Co-authored-by: Ron Federman <73110295+RonFed@users.noreply.github.com> Co-authored-by: Tamir David Co-authored-by: Ben Elferink Co-authored-by: Avihu Hanya <34632558+AvihuHenya@users.noreply.github.com> Co-authored-by: Eden Federman Co-authored-by: Amir Blum Co-authored-by: Alon Braymok <138359965+alonkeyval@users.noreply.github.com> Co-authored-by: alonkeyval --- .../workflows/trigger-odigos-enterprise.yaml | 6 +- .vscode/launch.json | 29 +- CONTRIBUTING.md | 2 +- DEVELOPMENT.md | 82 +++++- agents/python/setup.py | 2 +- api/DEVELOPMENT.md | 2 +- cli/cmd/pro.go | 88 ++++++ cli/cmd/resources/instrumentor.go | 110 ++------ cli/cmd/resources/odiglet.go | 8 +- cli/cmd/resources/odigospro/consts.go | 1 - cli/cmd/resources/odigospro/manifests.go | 3 +- cli/cmd/resources/odigospro/utils.go | 7 +- cli/go.mod | 2 - cli/go.sum | 4 - common/Makefile | 2 +- common/consts/consts.go | 1 + frontend/gqlgen.yml | 2 +- frontend/graph/generated.go | 266 +++++++++++++++--- frontend/graph/model/models_gen.go | 7 +- frontend/graph/schema.graphqls | 7 +- frontend/graph/schema.resolvers.go | 28 +- frontend/kube/watchers/destination_watcher.go | 6 +- .../instrumentation_config_watcher.go | 4 +- .../instrumentation_instance_watcher.go | 2 +- .../destination_finder.go | 48 ++-- .../webapp/app/(overview)/overview/page.tsx | 6 +- .../components/notification/toast-list.tsx | 2 +- .../overview/all-drawers/describe-drawer.tsx | 22 +- .../custom-fields/error-sampler.tsx | 10 +- .../custom-fields/probabilistic-sampler.tsx | 10 +- .../main/actions/action-form-body/index.tsx | 13 +- .../configured-destinations-list/index.tsx | 7 +- .../destination-drawer/build-card.ts | 2 +- .../destination-drawer/build-drawer-item.ts | 9 +- .../destinations-list/index.tsx | 18 +- .../potential-destinations-list/index.tsx | 10 +- .../choose-destination-body/index.tsx | 4 +- .../rule-form-body/index.tsx | 13 +- .../overview/multi-source-control/index.tsx | 8 +- .../search/search-results/index.tsx | 4 +- .../overview-data-flow/build-source-nodes.ts | 6 +- .../overview-data-flow/get-entity-counts.ts | 17 +- .../overview/overview-data-flow/index.tsx | 27 +- .../source-controls/index.tsx | 2 +- .../sources/source-drawer-container/index.tsx | 20 +- frontend/webapp/cypress/constants/index.ts | 6 +- .../webapp/cypress/e2e/02-onboarding.cy.ts | 4 +- frontend/webapp/cypress/e2e/03-sources.cy.ts | 8 +- .../webapp/cypress/e2e/04-destinations.cy.ts | 8 +- .../graphql/queries/compute-platform.ts | 51 ++-- .../webapp/hooks/actions/useActionFormData.ts | 20 +- .../webapp/hooks/common/useGenericForm.ts | 41 ++- .../webapp/hooks/compute-platform/index.ts | 1 + .../compute-platform/useComputePlatform.ts | 54 ++-- .../compute-platform/usePaginatedSources.ts | 57 ++++ .../destinations/useDestinationFormData.ts | 2 +- .../hooks/destinations/useDestinationTypes.ts | 2 +- .../destinations/usePotentialDestinations.ts | 82 +++--- .../useInstrumentationRuleFormData.ts | 2 +- .../hooks/notification/useClickNotif.ts | 7 +- frontend/webapp/hooks/notification/useSSE.ts | 16 +- frontend/webapp/hooks/overview/useMetrics.ts | 8 +- .../hooks/overview/useNodeDataFlowHandlers.ts | 43 ++- .../webapp/hooks/sources/useSourceCRUD.ts | 44 ++- .../reuseable-components/code/index.tsx | 4 +- .../condition-details/index.tsx | 2 +- frontend/webapp/reuseable-components/index.ts | 2 +- .../input-table/index.tsx | 9 +- .../nodes-data-flow/nodes/header-node.tsx | 10 +- .../nodes-data-flow/nodes/scroll-node.tsx | 50 +++- .../reuseable-components/segment/index.tsx | 110 ++++++++ .../toggle-buttons/index.tsx | 93 ------ frontend/webapp/store/index.ts | 2 + frontend/webapp/store/useAppStore.ts | 9 +- frontend/webapp/store/usePaginatedStore.ts | 60 ++++ frontend/webapp/tsconfig.json | 3 +- frontend/webapp/types/compute-platform.ts | 8 +- frontend/webapp/types/destinations.ts | 2 +- .../webapp/utils/functions/entities/index.ts | 24 ++ .../formatters/extract-monitors/index.ts | 2 +- .../get-id-from-sse-target/index.ts | 2 +- .../functions/icons/get-action-icon/index.ts | 2 +- .../functions/icons/get-monitor-icon/index.ts | 2 +- frontend/webapp/utils/functions/index.ts | 1 + .../webapp/utils/functions/resolvers/index.ts | 1 + .../functions/resolvers/is-emtpy/index.ts | 12 + helm/odigos/templates/_helpers.tpl | 17 -- .../templates/instrumentor/certificates.yaml | 36 --- .../templates/instrumentor/deployment.yaml | 2 +- .../templates/instrumentor/webhook.yaml | 13 +- helm/odigos/values.yaml | 8 +- instrumentation/manager.go | 37 ++- k8sutils/pkg/consts/consts.go | 7 + k8sutils/pkg/utils/tiers.go | 4 +- odiglet/odiglet.go | 14 +- 95 files changed, 1298 insertions(+), 635 deletions(-) create mode 100644 cli/cmd/pro.go create mode 100644 frontend/webapp/hooks/compute-platform/usePaginatedSources.ts create mode 100644 frontend/webapp/reuseable-components/segment/index.tsx delete mode 100644 frontend/webapp/reuseable-components/toggle-buttons/index.tsx create mode 100644 frontend/webapp/store/usePaginatedStore.ts create mode 100644 frontend/webapp/utils/functions/entities/index.ts create mode 100644 frontend/webapp/utils/functions/resolvers/is-emtpy/index.ts delete mode 100644 helm/odigos/templates/_helpers.tpl delete mode 100644 helm/odigos/templates/instrumentor/certificates.yaml diff --git a/.github/workflows/trigger-odigos-enterprise.yaml b/.github/workflows/trigger-odigos-enterprise.yaml index ce834a178..3f9ccbe75 100644 --- a/.github/workflows/trigger-odigos-enterprise.yaml +++ b/.github/workflows/trigger-odigos-enterprise.yaml @@ -6,9 +6,9 @@ on: - 'main' paths: - 'common/**' - - 'k8sUtils/**' + - 'k8sutils/**' - 'procdiscovery/**' - - 'opamserver/**' + - 'opampserver/**' - 'instrumentation/**' jobs: trigger-odigos-enterprise: @@ -20,4 +20,4 @@ jobs: -H "Accept: application/vnd.github.v3+json" \ -H "Authorization: token ${{ secrets.RELEASE_BOT_TOKEN }}" \ https://api.github.com/repos/odigos-io/odigos-enterprise/dispatches \ - -d '{"event_type": "process_oss_pr", "client_payload": {"pr_creator": "${{ github.event.pull_request.user.login }}"}}' + -d '{"event_type": "process_update_dependencies_pr", "client_payload": {"pr_creator": "${{ github.event.pull_request.user.login }}"}}' diff --git a/.vscode/launch.json b/.vscode/launch.json index d43a9f019..bc3e44bf8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,7 @@ { "version": "0.2.0", "configurations": [ + { "name": "Remote Odiglet", "type": "go", @@ -64,7 +65,33 @@ "cwd": "${workspaceFolder}/cli", "args": ["uninstall", "--yes"], "buildFlags": "-tags=embed_manifests" + }, + { + "name": "cli install", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}/cli", + "cwd": "${workspaceFolder}/cli", + "args": ["install", "--version", "ODIGOS_VERSION"], + "buildFlags": "-tags=embed_manifests" + }, + { + "name": "cli pro", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}/cli", + "cwd": "${workspaceFolder}/cli", + "args": ["pro", "--onprem-token", "${input:onprem_token}"], + "buildFlags": "-tags=embed_manifests" } - ] + ], + "inputs": [ + { + "id": "onprem_token", + "type": "promptString", + "description": "Enter your onprem token" + }] } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0cd9b84b7..dcdab1ca0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -232,7 +232,7 @@ If the Mutating Webhook is enabled, follow these steps: Create a local directory and extract the certificate and key by running the following command: ``` -mkdir -p serving-certs && kubectl get secret instrumentor-webhook-cert -n odigos-system -o jsonpath='{.data.tls\.crt}' | base64 -d > serving-certs/tls.crt && kubectl get secret instrumentor-webhook-cert -n odigos-system -o jsonpath='{.data.tls\.key}' | base64 -d > serving-certs/tls.key +mkdir -p serving-certs && kubectl get secret webhook-cert -n odigos-system -o jsonpath='{.data.tls\.crt}' | base64 -d > serving-certs/tls.crt && kubectl get secret webhook-cert -n odigos-system -o jsonpath='{.data.tls\.key}' | base64 -d > serving-certs/tls.key ``` 2. Apply this service to the cluster, it will replace the existing `odigos-instrumentor` service: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 0ac8ac087..e6d8b0239 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -9,35 +9,97 @@ This guide provides advanced instructions for contributors and maintainers, cove ### Step 1: Port Forward the Gateway or Data Collection Pod Forward the relevant pod to your local machine to enable profiling access: +```bash kubectl port-forward pod/ -n odigos-system 1777:1777 - +``` ### Step 2: Collect Profiling Data - **CPU Profile** - Captures data about the amount of time your application spends executing functions. Use this profile to identify performance bottlenecks, optimize CPU-intensive operations, and analyze which parts of the code consume the most CPU resources. + Captures data about the time your application spends executing functions. Use this profile to identify performance bottlenecks, optimize CPU-intensive operations, and analyze which parts of the code consume the most CPU resources. - ``` bash - curl -o cpu_profile.prof http://localhost:1777/debug/pprof/profile?seconds=30 + ```bash + curl -o cpu_profile.prof http://localhost:1777/debug/pprof/profile?seconds=30 ``` - **Heap Memory Profile** - Captures a snapshot of memory currently in use by your application after the latest garbage collection. Use this profile to identify memory leaks, track high memory usage, and analyze memory consumption by specific parts of the code. - ``` bash + Captures a snapshot of memory currently in use by your application after the latest garbage collection. Use this profile to identify memory leaks, track high memory usage, and analyze memory consumption by specific parts of the code. + + ```bash curl -o heap.out http://localhost:1777/debug/pprof/heap ``` - **Historical Memory Allocation** - Provides insights into all memory allocations made by the program since it started running, including memory that has already been freed by the garbage collector (GC). This is useful for understanding memory allocation patterns and optimizing allocation behavior. - ``` bash + Provides insights into all memory allocations made by the program since it started running, including memory that has already been freed by the garbage collector (GC). This is useful for understanding memory allocation patterns and optimizing allocation behavior. + + ```bash curl -o allocs.out http://localhost:1777/debug/pprof/allocs ``` ### Step 3: Analyze the Profiles After collecting the profiling data, use the `go tool pprof` command to analyze the profiles visually in your web browser. Replace `` with the appropriate file (`cpu_profile.prof`, `heap.out`, or `allocs.out`): -``` bash + +```bash go tool pprof -http=:8080 ``` + This opens an interactive interface in your browser where you can: - **Visualize Hotspots**: View flame graphs or directed graphs for easy identification of bottlenecks. -- **Drill Down**: Explore specific functions or memory allocations for detailed insights. \ No newline at end of file +- **Drill Down**: Explore specific functions or memory allocations for detailed insights. + +--- + +## Debugging CLI Commands + +### Debugging the `cli pro` Command + +To debug the `cli pro` command in Visual Studio Code, use the following configuration in your `.vscode/launch.json` file: + +```jsonc +{ + "name": "cli pro", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}/cli", + "cwd": "${workspaceFolder}/cli", + "args": ["pro", "--onprem-token", "${input:onprem_token}"], + "buildFlags": "-tags=embed_manifests" +} +``` + +#### How to Use +1. Open the **Run and Debug** view in Visual Studio Code: + - Press `Ctrl+Shift+D` (Windows/Linux) or `Cmd+Shift+D` (macOS). +2. Select the `cli pro` configuration from the dropdown menu. +3. Click the green **Play** button to start debugging. +4. When prompted, enter your `onprem-token` value. +5. The debugger will start the `cli pro` command with the provided token and attach to the process for debugging. + +--- + +### Debugging the `cli install` Command + +To debug the `cli install` command in Visual Studio Code, use the following configuration in your `launch.json` file: + +```jsonc +{ + "name": "cli install", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}/cli", + "cwd": "${workspaceFolder}/cli", + "args": ["install", "--version", "ODIGOS_VERSION"], + "buildFlags": "-tags=embed_manifests" +} +``` + +#### How to Use +1. Open the **Run and Debug** view in Visual Studio Code: + - Press `Ctrl+Shift+D` (Windows/Linux) or `Cmd+Shift+D` (macOS). +2. Select the `cli install` configuration from the dropdown menu. +3. Replace `"ODIGOS_VERSION"` in the `args` section with the desired version number. +4. Click the green **Play** button to start debugging. +5. The debugger will start the `cli install` command with the specified version. + diff --git a/agents/python/setup.py b/agents/python/setup.py index 51f84aacd..8d49dbaa4 100644 --- a/agents/python/setup.py +++ b/agents/python/setup.py @@ -6,7 +6,7 @@ # index_url = 'http://host.docker.internal:8080/packages/odigos_opentelemetry_python-0.1.1-py3-none-any.whl' install_requires = [ - f"odigos-opentelemetry-python @ {index_url}" if index_url else "odigos-opentelemetry-python==1.0.23" + f"odigos-opentelemetry-python @ {index_url}" if index_url else "odigos-opentelemetry-python==1.0.24" ] setup( diff --git a/api/DEVELOPMENT.md b/api/DEVELOPMENT.md index ac5730700..4aacb338e 100644 --- a/api/DEVELOPMENT.md +++ b/api/DEVELOPMENT.md @@ -8,7 +8,7 @@ 4. Run `make generate-client` to update the auto generated files under `api/generated`. 5. Run `make sync-helm-crd` to sync the CRD yaml files to the helm chart. -you can run `make all` to combine steps 2-5 instead of running them individually. +you can run `make all` to combine steps 2-5 instead of running them individually. ## CRD versioning diff --git a/cli/cmd/pro.go b/cli/cmd/pro.go new file mode 100644 index 000000000..775c520ff --- /dev/null +++ b/cli/cmd/pro.go @@ -0,0 +1,88 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/odigos-io/odigos/cli/cmd/resources" + cmdcontext "github.com/odigos-io/odigos/cli/pkg/cmd_context" + "github.com/odigos-io/odigos/cli/pkg/kube" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" + odigosconsts "github.com/odigos-io/odigos/common/consts" + "github.com/spf13/cobra" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var proCmd = &cobra.Command{ + Use: "pro", + Short: "manage odigos pro", + Long: `The pro command provides various operations and functionalities specifically designed for enterprise users. Use this command to access advanced features and manage your pro account.`, + Run: func(cmd *cobra.Command, args []string) { + ctx := cmd.Context() + client := cmdcontext.KubeClientFromContextOrExit(ctx) + ns, err := resources.GetOdigosNamespace(client, ctx) + if resources.IsErrNoOdigosNamespaceFound(err) { + fmt.Println("\033[31mERROR\033[0m no odigos installation found in the current cluster") + os.Exit(1) + } else if err != nil { + fmt.Printf("\033[31mERROR\033[0m Failed to check if Odigos is already installed: %s\n", err) + os.Exit(1) + } + onPremToken := cmd.Flag("onprem-token").Value.String() + err = updateOdigosToken(ctx, client, ns, onPremToken) + if err != nil { + fmt.Println("\033[31mERROR\033[0m Failed to update token:") + fmt.Println(err) + os.Exit(1) + } + + fmt.Println() + fmt.Println("\u001B[32mSUCCESS:\u001B[0m Token updated successfully") + }, +} + +func updateOdigosToken(ctx context.Context, client *kube.Client, namespace string, onPremToken string) error { + secret, err := client.CoreV1().Secrets(namespace).Get(ctx, consts.OdigosProSecretName, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return fmt.Errorf("Tokens are not available in the open-source version of Odigos. Please contact Odigos team to inquire about pro version.") + } + return err + } + secret.Data[consts.OdigosOnpremTokenSecretKey] = []byte(onPremToken) + + _, err = client.CoreV1().Secrets(namespace).Update(ctx, secret, metav1.UpdateOptions{}) + if err != nil { + return err + } + + daemonSet, err := client.AppsV1().DaemonSets(namespace).Get(ctx, "odiglet", metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("failed to get DaemonSet odiglet in namespace %s: %v", namespace, err) + } + + // Modify the DaemonSet spec.template to trigger a rollout + if daemonSet.Spec.Template.Annotations == nil { + daemonSet.Spec.Template.Annotations = make(map[string]string) + } + daemonSet.Spec.Template.Annotations[odigosconsts.RolloutTriggerAnnotation] = time.Now().Format(time.RFC3339) + + _, err = client.AppsV1().DaemonSets(namespace).Update(ctx, daemonSet, metav1.UpdateOptions{}) + if err != nil { + fmt.Printf("Failed to restart Odiglets. Reason: %s\n", err) + fmt.Printf("To trigger a restart manually, run the following command:\n") + fmt.Printf("kubectl rollout restart daemonset odiglet -n %s\n", daemonSet.Namespace) + } + + return nil +} + +func init() { + rootCmd.AddCommand(proCmd) + + proCmd.Flags().String("onprem-token", "", "On-prem token for Odigos") + proCmd.MarkFlagRequired("onprem-token") +} diff --git a/cli/cmd/resources/instrumentor.go b/cli/cmd/resources/instrumentor.go index 01ce7bae4..69c90babd 100644 --- a/cli/cmd/resources/instrumentor.go +++ b/cli/cmd/resources/instrumentor.go @@ -10,8 +10,6 @@ import ( "github.com/odigos-io/odigos/cli/pkg/kube" "github.com/odigos-io/odigos/common" - certv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" - cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" "github.com/odigos-io/odigos/k8sutils/pkg/consts" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" @@ -35,7 +33,7 @@ const ( InstrumentorCertificateName = InstrumentorDeploymentName InstrumentorMutatingWebhookName = "mutating-webhook-configuration" InstrumentorContainerName = "manager" - InstrumentorWebhookSecretName = "instrumentor-webhook-cert" + InstrumentorWebhookSecretName = "webhook-cert" InstrumentorWebhookVolumeName = "webhook-cert" ) @@ -229,72 +227,6 @@ func NewInstrumentorClusterRoleBinding(ns string) *rbacv1.ClusterRoleBinding { } } -func isCertManagerInstalled(ctx context.Context, c *kube.Client) bool { - // Check if CRD is installed - _, err := c.ApiExtensions.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, "issuers.cert-manager.io", metav1.GetOptions{}) - if err != nil { - return false - } - - return true -} - -func NewInstrumentorIssuer(ns string) *certv1.Issuer { - return &certv1.Issuer{ - TypeMeta: metav1.TypeMeta{ - Kind: "Issuer", - APIVersion: "cert-manager.io/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "selfsigned-issuer", - Namespace: ns, - Labels: map[string]string{ - "app.kubernetes.io/name": "issuer", - "app.kubernetes.io/instance": "selfsigned-issuer", - "app.kubernetes.io/component": "certificate", - "app.kubernetes.io/created-by": "instrumentor", - "app.kubernetes.io/part-of": "odigos", - }, - }, - Spec: certv1.IssuerSpec{ - IssuerConfig: certv1.IssuerConfig{ - SelfSigned: &certv1.SelfSignedIssuer{}, - }, - }, - } -} - -func NewInstrumentorCertificate(ns string) *certv1.Certificate { - return &certv1.Certificate{ - TypeMeta: metav1.TypeMeta{ - Kind: "Certificate", - APIVersion: "cert-manager.io/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "serving-cert", - Namespace: ns, - Labels: map[string]string{ - "app.kubernetes.io/name": "instrumentor-cert", - "app.kubernetes.io/instance": "instrumentor-cert", - "app.kubernetes.io/component": "certificate", - "app.kubernetes.io/created-by": "instrumentor", - "app.kubernetes.io/part-of": "odigos", - }, - }, - Spec: certv1.CertificateSpec{ - DNSNames: []string{ - fmt.Sprintf("odigos-instrumentor.%s.svc", ns), - fmt.Sprintf("odigos-instrumentor.%s.svc.cluster.local", ns), - }, - IssuerRef: cmmeta.ObjectReference{ - Kind: "Issuer", - Name: "selfsigned-issuer", - }, - SecretName: InstrumentorWebhookSecretName, - }, - } -} - func NewInstrumentorService(ns string) *corev1.Service { return &corev1.Service{ TypeMeta: metav1.TypeMeta{ @@ -595,7 +527,6 @@ func NewInstrumentorResourceManager(client *kube.Client, ns string, config *comm func (a *instrumentorResourceManager) Name() string { return "Instrumentor" } func (a *instrumentorResourceManager) InstallFromScratch(ctx context.Context) error { - certManagerInstalled := isCertManagerInstalled(ctx, a.client) resources := []kube.Object{ NewInstrumentorServiceAccount(a.ns), NewInstrumentorLeaderElectionRoleBinding(a.ns), @@ -606,33 +537,26 @@ func (a *instrumentorResourceManager) InstallFromScratch(ctx context.Context) er NewInstrumentorDeployment(a.ns, a.odigosVersion, a.config.TelemetryEnabled, a.config.ImagePrefix, a.config.InstrumentorImage), NewInstrumentorService(a.ns), } - if certManagerInstalled && a.config.SkipWebhookIssuerCreation != true { - resources = append([]kube.Object{NewInstrumentorIssuer(a.ns), - NewInstrumentorCertificate(a.ns), - NewMutatingWebhookConfiguration(a.ns, nil), - }, - resources...) - } else { - ca, err := crypto.GenCA(InstrumentorCertificateName, 365) - if err != nil { - return fmt.Errorf("failed to generate CA: %w", err) - } - altNames := []string{ - fmt.Sprintf("%s.%s.svc", InstrumentorServiceName, a.ns), - fmt.Sprintf("%s.%s.svc.cluster.local", InstrumentorServiceName, a.ns), - } + ca, err := crypto.GenCA(InstrumentorCertificateName, 365) + if err != nil { + return fmt.Errorf("failed to generate CA: %w", err) + } - cert, err := crypto.GenerateSignedCertificate("serving-cert", nil, altNames, 365, ca) - if err != nil { - return fmt.Errorf("failed to generate signed certificate: %w", err) - } + altNames := []string{ + fmt.Sprintf("%s.%s.svc", InstrumentorServiceName, a.ns), + fmt.Sprintf("%s.%s.svc.cluster.local", InstrumentorServiceName, a.ns), + } - resources = append([]kube.Object{NewInstrumentorTLSSecret(a.ns, &cert), - NewMutatingWebhookConfiguration(a.ns, []byte(cert.Cert)), - }, - resources...) + cert, err := crypto.GenerateSignedCertificate("serving-cert", nil, altNames, 365, ca) + if err != nil { + return fmt.Errorf("failed to generate signed certificate: %w", err) } + resources = append([]kube.Object{NewInstrumentorTLSSecret(a.ns, &cert), + NewMutatingWebhookConfiguration(a.ns, []byte(cert.Cert)), + }, + resources...) + return a.client.ApplyResources(ctx, a.config.ConfigVersion, resources) } diff --git a/cli/cmd/resources/odiglet.go b/cli/cmd/resources/odiglet.go index e8f553ad9..fdfdba5c4 100644 --- a/cli/cmd/resources/odiglet.go +++ b/cli/cmd/resources/odiglet.go @@ -363,10 +363,10 @@ func NewOdigletDaemonSet(ns string, version string, imagePrefix string, imageNam }, Tolerations: []corev1.Toleration{ { - Key: "node.kubernetes.io/os", - Operator: corev1.TolerationOpEqual, - Value: "windows", - Effect: corev1.TaintEffectNoSchedule, + // This toleration with 'Exists' operator and no key/effect specified + // will match ALL taints, allowing pods to be scheduled on any node + // regardless of its taints (including master/control-plane nodes) + Operator: corev1.TolerationOpExists, }, }, Volumes: append([]corev1.Volume{ diff --git a/cli/cmd/resources/odigospro/consts.go b/cli/cmd/resources/odigospro/consts.go index f50e4ccd5..28a9d0533 100644 --- a/cli/cmd/resources/odigospro/consts.go +++ b/cli/cmd/resources/odigospro/consts.go @@ -1,7 +1,6 @@ package odigospro const ( - odigosProSecretName = "odigos-pro" odigosCloudTokenEnvName = "ODIGOS_CLOUD_TOKEN" odigosCloudApiKeySecretKey = "odigos-cloud-api-key" odigosOnpremTokenEnvName = "ODIGOS_ONPREM_TOKEN" diff --git a/cli/cmd/resources/odigospro/manifests.go b/cli/cmd/resources/odigospro/manifests.go index 9b7618291..e2c4c232c 100644 --- a/cli/cmd/resources/odigospro/manifests.go +++ b/cli/cmd/resources/odigospro/manifests.go @@ -1,6 +1,7 @@ package odigospro import ( + "github.com/odigos-io/odigos/k8sutils/pkg/consts" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -21,7 +22,7 @@ func newOdigosProSecret(ns string, cloudApiKey string, onpremToken string) *core APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: odigosProSecretName, + Name: consts.OdigosProSecretName, Namespace: ns, }, StringData: data, diff --git a/cli/cmd/resources/odigospro/utils.go b/cli/cmd/resources/odigospro/utils.go index 2b22112f4..bd702c46c 100644 --- a/cli/cmd/resources/odigospro/utils.go +++ b/cli/cmd/resources/odigospro/utils.go @@ -5,6 +5,7 @@ import ( "github.com/odigos-io/odigos/cli/pkg/kube" "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -35,7 +36,7 @@ func CloudTokenAsEnvVar() corev1.EnvVar { ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: odigosProSecretName, + Name: consts.OdigosProSecretName, }, Key: odigosCloudApiKeySecretKey, }, @@ -50,7 +51,7 @@ func OnPremTokenAsEnvVar() corev1.EnvVar { ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: odigosProSecretName, + Name: consts.OdigosProSecretName, }, Key: odigosOnpremTokenSecretKey, }, @@ -59,7 +60,7 @@ func OnPremTokenAsEnvVar() corev1.EnvVar { } func getCurrentOdigosProSecret(ctx context.Context, client *kube.Client, ns string) (*corev1.Secret, error) { - secret, err := client.CoreV1().Secrets(ns).Get(ctx, odigosProSecretName, metav1.GetOptions{}) + secret, err := client.CoreV1().Secrets(ns).Get(ctx, consts.OdigosProSecretName, metav1.GetOptions{}) if apierrors.IsNotFound(err) { return nil, nil } diff --git a/cli/go.mod b/cli/go.mod index 72073c3c8..d6be24b11 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -3,7 +3,6 @@ module github.com/odigos-io/odigos/cli go 1.23.0 require ( - github.com/cert-manager/cert-manager v1.16.2 github.com/google/uuid v1.6.0 github.com/hashicorp/go-version v1.7.0 github.com/odigos-io/odigos/api v0.0.0 @@ -32,7 +31,6 @@ require ( golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect sigs.k8s.io/controller-runtime v0.19.0 // indirect - sigs.k8s.io/gateway-api v1.1.0 // indirect ) require ( diff --git a/cli/go.sum b/cli/go.sum index 341f2ee71..cde7cb444 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -1,7 +1,5 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/cert-manager/cert-manager v1.16.2 h1:c9UU2E+8XWGruyvC/mdpc1wuLddtgmNr8foKdP7a8Jg= -github.com/cert-manager/cert-manager v1.16.2/go.mod h1:MfLVTL45hFZsqmaT1O0+b2ugaNNQQZttSFV9hASHUb0= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -197,8 +195,6 @@ k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6J k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= -sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM= -sigs.k8s.io/gateway-api v1.1.0/go.mod h1:ZH4lHrL2sDi0FHZ9jjneb8kKnGzFWyrTya35sWUTrRs= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= diff --git a/common/Makefile b/common/Makefile index 06936494f..598bcc310 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,4 +1,4 @@ -.PHONY: test +.PHONY: test test: go test ./... \ No newline at end of file diff --git a/common/consts/consts.go b/common/consts/consts.go index a98d34179..540088447 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -19,6 +19,7 @@ const ( OdigosWorkloadKindAnnotation = "odigos.io/workload-kind" OdigosWorkloadNameAnnotation = "odigos.io/workload-name" OdigosReportedNameAnnotation = "odigos.io/reported-name" + RolloutTriggerAnnotation = "rollout-trigger" // GatewayMaxConnectionAge and GatewayMaxConnectionAgeGrace are the default values for the gateway collector. GatewayMaxConnectionAge = "15s" diff --git a/frontend/gqlgen.yml b/frontend/gqlgen.yml index f5dedfb41..110b3471b 100644 --- a/frontend/gqlgen.yml +++ b/frontend/gqlgen.yml @@ -48,7 +48,7 @@ models: resolver: true k8sActualNamespaces: resolver: true - k8sActualSource: + sources: resolver: true k8sActualSources: resolver: true diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index dc7c1e41a..06e2a490f 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -85,7 +85,7 @@ type ComplexityRoot struct { InstrumentationRules func(childComplexity int) int K8sActualNamespace func(childComplexity int, name string) int K8sActualNamespaces func(childComplexity int) int - K8sActualSources func(childComplexity int) int + Sources func(childComplexity int, nextPage string) int } Condition struct { @@ -336,6 +336,11 @@ type ComplexityRoot struct { Sources func(childComplexity int) int } + PaginatedSources struct { + Items func(childComplexity int) int + NextPage func(childComplexity int) int + } + PayloadCollection struct { DbQuery func(childComplexity int) int HTTPRequest func(childComplexity int) int @@ -467,7 +472,7 @@ type ComplexityRoot struct { type ComputePlatformResolver interface { K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) - K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) + Sources(ctx context.Context, obj *model.ComputePlatform, nextPage string) (*model.PaginatedSources, error) Destinations(ctx context.Context, obj *model.ComputePlatform) ([]*model.Destination, error) Actions(ctx context.Context, obj *model.ComputePlatform) ([]*model.PipelineAction, error) InstrumentationRules(ctx context.Context, obj *model.ComputePlatform) ([]*model.InstrumentationRule, error) @@ -705,12 +710,17 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ComputePlatform.K8sActualNamespaces(childComplexity), true - case "ComputePlatform.k8sActualSources": - if e.complexity.ComputePlatform.K8sActualSources == nil { + case "ComputePlatform.sources": + if e.complexity.ComputePlatform.Sources == nil { break } - return e.complexity.ComputePlatform.K8sActualSources(childComplexity), true + args, err := ec.field_ComputePlatform_sources_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.ComputePlatform.Sources(childComplexity, args["nextPage"].(string)), true case "Condition.lastTransitionTime": if e.complexity.Condition.LastTransitionTime == nil { @@ -1799,6 +1809,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.OverviewMetricsResponse.Sources(childComplexity), true + case "PaginatedSources.items": + if e.complexity.PaginatedSources.Items == nil { + break + } + + return e.complexity.PaginatedSources.Items(childComplexity), true + + case "PaginatedSources.nextPage": + if e.complexity.PaginatedSources.NextPage == nil { + break + } + + return e.complexity.PaginatedSources.NextPage(childComplexity), true + case "PayloadCollection.dbQuery": if e.complexity.PayloadCollection.DbQuery == nil { break @@ -2519,6 +2543,21 @@ func (ec *executionContext) field_ComputePlatform_k8sActualNamespace_args(ctx co return args, nil } +func (ec *executionContext) field_ComputePlatform_sources_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["nextPage"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nextPage")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["nextPage"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_createAction_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -3937,8 +3976,8 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualNamespace(ctx return fc, nil } -func (ec *executionContext) _ComputePlatform_k8sActualSources(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) +func (ec *executionContext) _ComputePlatform_sources(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ComputePlatform_sources(ctx, field) if err != nil { return graphql.Null } @@ -3951,7 +3990,7 @@ func (ec *executionContext) _ComputePlatform_k8sActualSources(ctx context.Contex }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.ComputePlatform().K8sActualSources(rctx, obj) + return ec.resolvers.ComputePlatform().Sources(rctx, obj, fc.Args["nextPage"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -3963,12 +4002,12 @@ func (ec *executionContext) _ComputePlatform_k8sActualSources(ctx context.Contex } return graphql.Null } - res := resTmp.([]*model.K8sActualSource) + res := resTmp.(*model.PaginatedSources) fc.Result = res - return ec.marshalNK8sActualSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualSource(ctx, field.Selections, res) + return ec.marshalNPaginatedSources2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPaginatedSources(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ComputePlatform_sources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ComputePlatform", Field: field, @@ -3976,26 +4015,25 @@ func (ec *executionContext) fieldContext_ComputePlatform_k8sActualSources(_ cont IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "namespace": - return ec.fieldContext_K8sActualSource_namespace(ctx, field) - case "name": - return ec.fieldContext_K8sActualSource_name(ctx, field) - case "kind": - return ec.fieldContext_K8sActualSource_kind(ctx, field) - case "numberOfInstances": - return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) - case "selected": - return ec.fieldContext_K8sActualSource_selected(ctx, field) - case "reportedName": - return ec.fieldContext_K8sActualSource_reportedName(ctx, field) - case "containers": - return ec.fieldContext_K8sActualSource_containers(ctx, field) - case "conditions": - return ec.fieldContext_K8sActualSource_conditions(ctx, field) + case "nextPage": + return ec.fieldContext_PaginatedSources_nextPage(ctx, field) + case "items": + return ec.fieldContext_PaginatedSources_items(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PaginatedSources", field.Name) }, } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_ComputePlatform_sources_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } @@ -11195,6 +11233,112 @@ func (ec *executionContext) fieldContext_OverviewMetricsResponse_destinations(_ return fc, nil } +func (ec *executionContext) _PaginatedSources_nextPage(ctx context.Context, field graphql.CollectedField, obj *model.PaginatedSources) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PaginatedSources_nextPage(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.NextPage, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PaginatedSources_nextPage(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PaginatedSources", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PaginatedSources_items(ctx context.Context, field graphql.CollectedField, obj *model.PaginatedSources) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PaginatedSources_items(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Items, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.K8sActualSource) + fc.Result = res + return ec.marshalNK8sActualSource2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐK8sActualSource(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PaginatedSources_items(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PaginatedSources", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "namespace": + return ec.fieldContext_K8sActualSource_namespace(ctx, field) + case "name": + return ec.fieldContext_K8sActualSource_name(ctx, field) + case "kind": + return ec.fieldContext_K8sActualSource_kind(ctx, field) + case "numberOfInstances": + return ec.fieldContext_K8sActualSource_numberOfInstances(ctx, field) + case "selected": + return ec.fieldContext_K8sActualSource_selected(ctx, field) + case "reportedName": + return ec.fieldContext_K8sActualSource_reportedName(ctx, field) + case "containers": + return ec.fieldContext_K8sActualSource_containers(ctx, field) + case "conditions": + return ec.fieldContext_K8sActualSource_conditions(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type K8sActualSource", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _PayloadCollection_httpRequest(ctx context.Context, field graphql.CollectedField, obj *model.PayloadCollection) (ret graphql.Marshaler) { fc, err := ec.fieldContext_PayloadCollection_httpRequest(ctx, field) if err != nil { @@ -12721,8 +12865,8 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context return ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) case "k8sActualNamespace": return ec.fieldContext_ComputePlatform_k8sActualNamespace(ctx, field) - case "k8sActualSources": - return ec.fieldContext_ComputePlatform_k8sActualSources(ctx, field) + case "sources": + return ec.fieldContext_ComputePlatform_sources(ctx, field) case "destinations": return ec.fieldContext_ComputePlatform_destinations(ctx, field) case "actions": @@ -17858,7 +18002,7 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "k8sActualSources": + case "sources": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -17867,7 +18011,7 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._ComputePlatform_k8sActualSources(ctx, field, obj) + res = ec._ComputePlatform_sources(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -19903,6 +20047,50 @@ func (ec *executionContext) _OverviewMetricsResponse(ctx context.Context, sel as return out } +var paginatedSourcesImplementors = []string{"PaginatedSources"} + +func (ec *executionContext) _PaginatedSources(ctx context.Context, sel ast.SelectionSet, obj *model.PaginatedSources) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, paginatedSourcesImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PaginatedSources") + case "nextPage": + out.Values[i] = ec._PaginatedSources_nextPage(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "items": + out.Values[i] = ec._PaginatedSources_items(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var payloadCollectionImplementors = []string{"PayloadCollection"} func (ec *executionContext) _PayloadCollection(ctx context.Context, sel ast.SelectionSet, obj *model.PayloadCollection) graphql.Marshaler { @@ -22233,6 +22421,20 @@ func (ec *executionContext) marshalNOverviewMetricsResponse2ᚖgithubᚗcomᚋod return ec._OverviewMetricsResponse(ctx, sel, v) } +func (ec *executionContext) marshalNPaginatedSources2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPaginatedSources(ctx context.Context, sel ast.SelectionSet, v model.PaginatedSources) graphql.Marshaler { + return ec._PaginatedSources(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPaginatedSources2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPaginatedSources(ctx context.Context, sel ast.SelectionSet, v *model.PaginatedSources) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PaginatedSources(ctx, sel, v) +} + func (ec *executionContext) unmarshalNPatchSourceRequestInput2githubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐPatchSourceRequestInput(ctx context.Context, v interface{}) (model.PatchSourceRequestInput, error) { res, err := ec.unmarshalInputPatchSourceRequestInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index 3e2b61810..fd895d00b 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -76,7 +76,7 @@ type ComputePlatform struct { ComputePlatformType ComputePlatformType `json:"computePlatformType"` K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` K8sActualNamespace *K8sActualNamespace `json:"k8sActualNamespace,omitempty"` - K8sActualSources []*K8sActualSource `json:"k8sActualSources"` + Sources *PaginatedSources `json:"sources"` Destinations []*Destination `json:"destinations"` Actions []*PipelineAction `json:"actions"` InstrumentationRules []*InstrumentationRule `json:"instrumentationRules"` @@ -399,6 +399,11 @@ type OverviewMetricsResponse struct { Destinations []*SingleDestinationMetricsResponse `json:"destinations"` } +type PaginatedSources struct { + NextPage string `json:"nextPage"` + Items []*K8sActualSource `json:"items"` +} + type PatchSourceRequestInput struct { ReportedName *string `json:"reportedName,omitempty"` } diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index d59133ada..268511f3f 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -84,6 +84,11 @@ type K8sActualSource { conditions: [Condition!] } +type PaginatedSources { + nextPage: String! + items: [K8sActualSource]! +} + input K8sDesiredSourceInput { serviceName: String autoInstrument: Boolean @@ -188,7 +193,7 @@ type ComputePlatform { computePlatformType: ComputePlatformType! k8sActualNamespaces: [K8sActualNamespace]! k8sActualNamespace(name: String!): K8sActualNamespace - k8sActualSources: [K8sActualSource]! + sources(nextPage: String!): PaginatedSources! destinations: [Destination!]! actions: [PipelineAction!]! instrumentationRules: [InstrumentationRule!]! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 541ebae81..9dae1d295 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -61,24 +61,30 @@ func (r *computePlatformResolver) K8sActualNamespace(ctx context.Context, obj *m }, nil } -// K8sActualSources is the resolver for the k8sActualSources field. -func (r *computePlatformResolver) K8sActualSources(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualSource, error) { - // Initialize an empty list of K8sActualSource - var actualSources []*model.K8sActualSource +// Sources is the resolver for the sources field. +func (r *computePlatformResolver) Sources(ctx context.Context, obj *model.ComputePlatform, nextPage string) (*model.PaginatedSources, error) { + list, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs("").List(ctx, metav1.ListOptions{ + Limit: int64(10), + Continue: nextPage, + }) - instrumentationConfigs, err := kube.DefaultClient.OdigosClient.InstrumentationConfigs("").List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } - // Convert each instrumented application to the K8sActualSource type - for _, instruConfig := range instrumentationConfigs.Items { - actualSource := instrumentationConfigToActualSource(instruConfig) - services.AddHealthyInstrumentationInstancesCondition(ctx, &instruConfig, actualSource) - actualSources = append(actualSources, actualSource) + var actualSources []*model.K8sActualSource + + // Convert each InstrumentationConfig to the K8sActualSource type + for _, ic := range list.Items { + src := instrumentationConfigToActualSource(ic) + services.AddHealthyInstrumentationInstancesCondition(ctx, &ic, src) + actualSources = append(actualSources, src) } - return actualSources, nil + return &model.PaginatedSources{ + NextPage: list.GetContinue(), + Items: actualSources, + }, nil } // Destinations is the resolver for the destinations field. diff --git a/frontend/kube/watchers/destination_watcher.go b/frontend/kube/watchers/destination_watcher.go index b290e275d..6e22ba84b 100644 --- a/frontend/kube/watchers/destination_watcher.go +++ b/frontend/kube/watchers/destination_watcher.go @@ -21,7 +21,7 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { destinationAddedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, - Duration: 10 * time.Second, + Duration: 5 * time.Second, Event: sse.MessageEventAdded, CRDType: consts.Destination, SuccessBatchMessageFunc: func(count int, crdType string) string { @@ -36,7 +36,7 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { destinationModifiedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, - Duration: 10 * time.Second, + Duration: 5 * time.Second, Event: sse.MessageEventModified, CRDType: consts.Destination, SuccessBatchMessageFunc: func(batchSize int, crd string) string { @@ -51,7 +51,7 @@ func StartDestinationWatcher(ctx context.Context, namespace string) error { destinationDeletedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, - Duration: 10 * time.Second, + Duration: 5 * time.Second, Event: sse.MessageEventDeleted, CRDType: consts.Destination, SuccessBatchMessageFunc: func(count int, crdType string) string { diff --git a/frontend/kube/watchers/instrumentation_config_watcher.go b/frontend/kube/watchers/instrumentation_config_watcher.go index e3c0514ce..e6a54a150 100644 --- a/frontend/kube/watchers/instrumentation_config_watcher.go +++ b/frontend/kube/watchers/instrumentation_config_watcher.go @@ -21,7 +21,7 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er instrumentationConfigAddedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, - Duration: 10 * time.Second, + Duration: 5 * time.Second, Event: sse.MessageEventAdded, CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { @@ -36,7 +36,7 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er instrumentationConfigDeletedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, - Duration: 10 * time.Second, + Duration: 5 * time.Second, Event: sse.MessageEventDeleted, CRDType: consts.InstrumentationConfig, SuccessBatchMessageFunc: func(count int, crdType string) string { diff --git a/frontend/kube/watchers/instrumentation_instance_watcher.go b/frontend/kube/watchers/instrumentation_instance_watcher.go index 0992faf63..f4df83504 100644 --- a/frontend/kube/watchers/instrumentation_instance_watcher.go +++ b/frontend/kube/watchers/instrumentation_instance_watcher.go @@ -20,7 +20,7 @@ func StartInstrumentationInstanceWatcher(ctx context.Context, namespace string) instrumentationInstanceModifiedEventBatcher = NewEventBatcher( EventBatcherConfig{ MinBatchSize: 1, - Duration: 10 * time.Second, + Duration: 5 * time.Second, MessageType: sse.MessageTypeError, Event: sse.MessageEventModified, CRDType: consts.InstrumentationInstance, diff --git a/frontend/services/destination_recognition/destination_finder.go b/frontend/services/destination_recognition/destination_finder.go index 20258eff7..681027266 100644 --- a/frontend/services/destination_recognition/destination_finder.go +++ b/frontend/services/destination_recognition/destination_finder.go @@ -2,6 +2,7 @@ package destination_recognition import ( "context" + "strings" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" @@ -11,8 +12,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var SupportedDestinationType = []common.DestinationType{common.JaegerDestinationType, common.ElasticsearchDestinationType} - type DestinationDetails struct { Type common.DestinationType `json:"type"` Fields map[string]string `json:"fields"` @@ -25,43 +24,42 @@ type IDestinationFinder interface { } func GetAllPotentialDestinationDetails(ctx context.Context, namespaces []k8s.Namespace, dests *odigosv1.DestinationList) ([]DestinationDetails, error) { - var destinationFinder IDestinationFinder var destinationDetails []DestinationDetails - var err error for _, ns := range namespaces { - err = client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.CoreV1().Services(ns.Name).List, - ctx, metav1.ListOptions{}, func(services *k8s.ServiceList) error { - for _, service := range services.Items { - for _, destinationType := range SupportedDestinationType { - destinationFinder = getDestinationFinder(destinationType) - - if destinationFinder.isPotentialService(service) { - potentialDestination := destinationFinder.fetchDestinationDetails(service) - - if !destinationExist(dests, potentialDestination, destinationFinder) { - destinationDetails = append(destinationDetails, potentialDestination) - } - break + err := client.ListWithPages(client.DefaultPageSize, kube.DefaultClient.CoreV1().Services(ns.Name).List, ctx, metav1.ListOptions{}, + func(svc *k8s.ServiceList) error { + for _, service := range svc.Items { + df := getDestinationFinder(service.Name) + + if df != nil && df.isPotentialService(service) { + pd := df.fetchDestinationDetails(service) + + if !destinationExist(dests, pd, df) { + destinationDetails = append(destinationDetails, pd) } + break } } + return nil - }) - } + }, + ) - if err != nil { - return nil, err + if err != nil { + return nil, err + } } return destinationDetails, nil } -func getDestinationFinder(destinationType common.DestinationType) IDestinationFinder { - switch destinationType { - case common.JaegerDestinationType: +func getDestinationFinder(serviceName string) IDestinationFinder { + if strings.Contains(serviceName, string(common.JaegerDestinationType)) { return &JaegerDestinationFinder{} - case common.ElasticsearchDestinationType: + } + + if strings.Contains(serviceName, string(common.ElasticsearchDestinationType)) { return &ElasticSearchDestinationFinder{} } diff --git a/frontend/webapp/app/(overview)/overview/page.tsx b/frontend/webapp/app/(overview)/overview/page.tsx index 5d971be73..c0d926df4 100644 --- a/frontend/webapp/app/(overview)/overview/page.tsx +++ b/frontend/webapp/app/(overview)/overview/page.tsx @@ -1,7 +1,7 @@ 'use client'; import React from 'react'; import dynamic from 'next/dynamic'; -import { useSSE } from '@/hooks'; +import { usePaginatedSources, useSSE } from '@/hooks'; const ToastList = dynamic(() => import('@/components/notification/toast-list'), { ssr: false }); const AllDrawers = dynamic(() => import('@/components/overview/all-drawers'), { ssr: false }); @@ -11,6 +11,10 @@ const OverviewDataFlowContainer = dynamic(() => import('@/containers/main/overvi export default function MainPage() { useSSE(); + // "usePaginatedSources" is here to fetch sources just once + // (hooks run on every mount, we don't want that for pagination) + usePaginatedSources(); + return ( <> diff --git a/frontend/webapp/components/notification/toast-list.tsx b/frontend/webapp/components/notification/toast-list.tsx index 08b8c7940..5fb614122 100644 --- a/frontend/webapp/components/notification/toast-list.tsx +++ b/frontend/webapp/components/notification/toast-list.tsx @@ -36,7 +36,7 @@ const Toast: React.FC = (props) => { const { markAsDismissed, markAsSeen } = useNotificationStore(); const clickNotif = useClickNotif(); - const onClose = ({ asSeen }) => { + const onClose = ({ asSeen }: { asSeen: boolean }) => { markAsDismissed(id); if (asSeen) markAsSeen(id); }; diff --git a/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx b/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx index a5d1702ff..2a176a1cc 100644 --- a/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx +++ b/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx @@ -1,11 +1,10 @@ import React, { useState } from 'react'; import styled from 'styled-components'; -import { CodeBracketsIcon } from '@/assets'; import { useDescribeOdigos } from '@/hooks'; import { DATA_CARDS, safeJsonStringify } from '@/utils'; -import { ToggleCodeComponent } from '@/components/common'; -import { DataCard, DataCardFieldTypes } from '@/reuseable-components'; +import { CodeBracketsIcon, CodeIcon, ListIcon } from '@/assets'; import OverviewDrawer from '@/containers/main/overview/overview-drawer'; +import { DataCard, DataCardFieldTypes, Segment } from '@/reuseable-components'; interface Props {} @@ -17,21 +16,30 @@ const DataContainer = styled.div` export const DescribeDrawer: React.FC = () => { const { data: describe, restructureForPrettyMode } = useDescribeOdigos(); - const [isCodeMode, setIsCodeMode] = useState(false); + const [isPrettyMode, setIsPrettyMode] = useState(true); return ( } + action={ + + } data={[ { type: DataCardFieldTypes.CODE, value: JSON.stringify({ language: 'json', - code: safeJsonStringify(isCodeMode ? describe : restructureForPrettyMode(describe)), - pretty: !isCodeMode, + code: safeJsonStringify(isPrettyMode ? restructureForPrettyMode(describe) : describe), + pretty: isPrettyMode, }), width: 'inherit', }, diff --git a/frontend/webapp/containers/main/actions/action-form-body/custom-fields/error-sampler.tsx b/frontend/webapp/containers/main/actions/action-form-body/custom-fields/error-sampler.tsx index bc78c4a7b..429778633 100644 --- a/frontend/webapp/containers/main/actions/action-form-body/custom-fields/error-sampler.tsx +++ b/frontend/webapp/containers/main/actions/action-form-body/custom-fields/error-sampler.tsx @@ -1,6 +1,6 @@ -import React, { useMemo } from 'react'; -import { safeJsonParse } from '@/utils'; +import React, { useEffect, useMemo } from 'react'; import { Input } from '@/reuseable-components'; +import { isEmpty, safeJsonParse } from '@/utils'; import type { ErrorSamplerSpec } from '@/types'; type Props = { @@ -24,11 +24,15 @@ const ErrorSampler: React.FC = ({ value, setValue, errorMessage }) => { fallback_sampling_ratio: num, }; - const str = !!payload.fallback_sampling_ratio ? JSON.stringify(payload) : ''; + const str = isEmpty(payload.fallback_sampling_ratio) ? '' : JSON.stringify(payload); setValue(str); }; + useEffect(() => { + if (isEmpty(safeJsonParse(value, {}))) handleChange('0'); + }, [value]); + return handleChange(v)} errorMessage={errorMessage} />; }; diff --git a/frontend/webapp/containers/main/actions/action-form-body/custom-fields/probabilistic-sampler.tsx b/frontend/webapp/containers/main/actions/action-form-body/custom-fields/probabilistic-sampler.tsx index 8e87c2cb4..3efbaf2c4 100644 --- a/frontend/webapp/containers/main/actions/action-form-body/custom-fields/probabilistic-sampler.tsx +++ b/frontend/webapp/containers/main/actions/action-form-body/custom-fields/probabilistic-sampler.tsx @@ -1,6 +1,6 @@ -import React, { useMemo } from 'react'; -import { safeJsonParse } from '@/utils'; +import React, { useEffect, useMemo } from 'react'; import { Input } from '@/reuseable-components'; +import { isEmpty, safeJsonParse } from '@/utils'; import type { ProbabilisticSamplerSpec } from '@/types'; type Props = { @@ -24,11 +24,15 @@ const ProbabilisticSampler: React.FC = ({ value, setValue, errorMessage } sampling_percentage: String(num), }; - const str = !!payload.sampling_percentage ? JSON.stringify(payload) : ''; + const str = isEmpty(payload.sampling_percentage) ? '' : JSON.stringify(payload); setValue(str); }; + useEffect(() => { + if (isEmpty(safeJsonParse(value, {}))) handleChange('0'); + }, [value]); + return handleChange(v)} errorMessage={errorMessage} />; }; diff --git a/frontend/webapp/containers/main/actions/action-form-body/index.tsx b/frontend/webapp/containers/main/actions/action-form-body/index.tsx index 8ab6df68f..3730ab810 100644 --- a/frontend/webapp/containers/main/actions/action-form-body/index.tsx +++ b/frontend/webapp/containers/main/actions/action-form-body/index.tsx @@ -1,9 +1,11 @@ import React from 'react'; +import theme from '@/styles/theme'; import styled from 'styled-components'; import { type ActionInput } from '@/types'; import ActionCustomFields from './custom-fields'; +import { CheckCircledIcon, CrossCircledIcon } from '@/assets'; import { type ActionOption } from '../action-modal/action-options'; -import { DocsButton, Input, Text, TextArea, MonitoringCheckboxes, SectionTitle, ToggleButtons } from '@/reuseable-components'; +import { DocsButton, Input, Text, TextArea, MonitoringCheckboxes, SectionTitle, Segment } from '@/reuseable-components'; interface Props { isUpdate?: boolean; @@ -30,7 +32,14 @@ export const ActionFormBody: React.FC = ({ isUpdate, action, formData, fo {isUpdate && (
Status - handleFormChange('disable', !bool)} /> + handleFormChange('disable', bool)} + />
)} diff --git a/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx index 12724ab7b..68750cb58 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx @@ -19,7 +19,7 @@ const Container = styled.div` overflow-y: scroll; `; -const ListItem: React.FC<{ item: ConfiguredDestination; isLastItem: boolean }> = ({ item, isLastItem }) => { +const ListItem: React.FC<{ item: ConfiguredDestination; isLastItem: boolean }> = ({ item, isLastItem, ...props }) => { const { removeConfiguredDestination } = useAppStore((state) => state); const [deleteWarning, setDeleteWarning] = useState(false); @@ -37,6 +37,7 @@ const ListItem: React.FC<{ item: ConfiguredDestination; isLastItem: boolean }> = )} + {...props} /> = export const ConfiguredDestinationsList: React.FC<{ data: IAppState['configuredDestinations'] }> = ({ data }) => { return ( - {data.map(({ stored }) => ( - + {data.map(({ stored }, idx) => ( + ))} ); diff --git a/frontend/webapp/containers/main/destinations/destination-drawer/build-card.ts b/frontend/webapp/containers/main/destinations/destination-drawer/build-card.ts index 47929808e..4e7c3eab7 100644 --- a/frontend/webapp/containers/main/destinations/destination-drawer/build-card.ts +++ b/frontend/webapp/containers/main/destinations/destination-drawer/build-card.ts @@ -4,7 +4,7 @@ import type { ActualDestination, DestinationDetailsResponse, ExportedSignals } f const buildMonitorsList = (exportedSignals: ExportedSignals): string => Object.keys(exportedSignals) - .filter((key) => exportedSignals[key]) + .filter((key) => exportedSignals[key as keyof ExportedSignals]) .join(', '); const buildCard = (destination: ActualDestination, destinationTypeDetails?: DestinationDetailsResponse['destinationTypeDetails']) => { diff --git a/frontend/webapp/containers/main/destinations/destination-drawer/build-drawer-item.ts b/frontend/webapp/containers/main/destinations/destination-drawer/build-drawer-item.ts index cdceb8fb4..df2e721f7 100644 --- a/frontend/webapp/containers/main/destinations/destination-drawer/build-drawer-item.ts +++ b/frontend/webapp/containers/main/destinations/destination-drawer/build-drawer-item.ts @@ -4,9 +4,12 @@ const buildDrawerItem = (id: string, formData: DestinationInput, drawerItem: Act const { name, exportedSignals, fields } = formData; const { destinationType, conditions } = drawerItem || {}; - let fieldsStringified: string | Record = {}; - fields.forEach(({ key, value }) => (fieldsStringified[key] = value)); - fieldsStringified = JSON.stringify(fieldsStringified); + const fieldsObject: Record = {}; + fields.forEach(({ key, value }) => { + fieldsObject[key] = value; + }); + + const fieldsStringified = JSON.stringify(fieldsObject); return { id, diff --git a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx index ae906d20d..d509bb79b 100644 --- a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/index.tsx @@ -1,10 +1,10 @@ import React from 'react'; import styled from 'styled-components'; -import { DestinationTypeItem } from '@/types'; import { IDestinationListItem } from '@/hooks'; import { capitalizeFirstLetter } from '@/utils'; -import { DataTab, NoDataFound, SectionTitle } from '@/reuseable-components'; +import type { SupportedSignals, DestinationTypeItem } from '@/types'; import { PotentialDestinationsList } from './potential-destinations-list'; +import { DataTab, NoDataFound, SectionTitle } from '@/reuseable-components'; const Container = styled.div` display: flex; @@ -48,16 +48,16 @@ export const DestinationsList: React.FC = ({ items, setSe return ( - {categoryItem.items.map((destinationItem) => ( + {categoryItem.items.map((item, idx) => ( destinationItem.supportedSignals[signal].supported)} + monitors={Object.keys(item.supportedSignals).filter((signal) => item.supportedSignals[signal as keyof SupportedSignals].supported)} monitorsWithLabels - onClick={() => setSelectedItems(destinationItem)} + onClick={() => setSelectedItems(item)} /> ))} diff --git a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/potential-destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/potential-destinations-list/index.tsx index 171250074..4b097bbdd 100644 --- a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/potential-destinations-list/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/potential-destinations-list/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { OdigosLogo } from '@/assets'; import styled from 'styled-components'; -import { DestinationTypeItem } from '@/types'; +import type { DestinationTypeItem, SupportedSignals } from '@/types'; import { usePotentialDestinations } from '@/hooks'; import { DataTab, SectionTitle, SkeletonLoader } from '@/reuseable-components'; @@ -31,14 +31,14 @@ export const PotentialDestinationsList: React.FC = ({ setSelectedItems }) {loading ? ( ) : ( - data.map((item) => ( + data.map((item, idx) => ( item.supportedSignals[signal].supported)} + monitors={Object.keys(item.supportedSignals).filter((signal: keyof SupportedSignals) => item.supportedSignals[signal].supported)} monitorsWithLabels onClick={() => setSelectedItems(item)} /> diff --git a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/index.tsx b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/index.tsx index de382af29..7faac33bb 100644 --- a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/index.tsx @@ -3,8 +3,8 @@ import { SearchIcon } from '@/assets'; import styled from 'styled-components'; import { SignalUppercase } from '@/utils'; import { useDestinationTypes } from '@/hooks'; -import type { DestinationTypeItem } from '@/types'; import { DestinationsList } from './destinations-list'; +import type { DestinationTypeItem, SupportedSignals } from '@/types'; import { Divider, Dropdown, Input, MonitoringCheckboxes, SectionTitle } from '@/reuseable-components'; interface Props { @@ -51,7 +51,7 @@ export const ChooseDestinationBody: React.FC = ({ onSelect, hidden }) => const filteredItems = category.items.filter((item) => { const matchesSearch = !search || item.displayName.toLowerCase().includes(search.toLowerCase()); const matchesCategory = selectedCategory.id === 'all' || selectedCategory.id === category.name; - const matchesMonitor = selectedMonitors.some((monitor) => item.supportedSignals[monitor.toLowerCase()]?.supported); + const matchesMonitor = selectedMonitors.some((monitor) => item.supportedSignals[monitor.toLowerCase() as keyof SupportedSignals]?.supported); return matchesSearch && matchesCategory && matchesMonitor; }); diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-form-body/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-form-body/index.tsx index 44a94c99d..9a233e66c 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-form-body/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-form-body/index.tsx @@ -1,9 +1,11 @@ import React from 'react'; +import theme from '@/styles/theme'; import styled from 'styled-components'; import RuleCustomFields from './custom-fields'; import type { InstrumentationRuleInput } from '@/types'; import type { RuleOption } from '../rule-modal/rule-options'; -import { DocsButton, Input, Text, TextArea, SectionTitle, ToggleButtons } from '@/reuseable-components'; +import { CheckCircledIcon, CrossCircledIcon } from '@/assets'; +import { DocsButton, Input, Text, TextArea, SectionTitle, Segment } from '@/reuseable-components'; interface Props { isUpdate?: boolean; @@ -30,7 +32,14 @@ export const RuleFormBody: React.FC = ({ isUpdate, rule, formData, formEr {isUpdate && (
Status - handleFormChange('disabled', !bool)} /> + handleFormChange('disabled', bool)} + />
)} diff --git a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx index a3a550475..bb8043ed0 100644 --- a/frontend/webapp/containers/main/overview/multi-source-control/index.tsx +++ b/frontend/webapp/containers/main/overview/multi-source-control/index.tsx @@ -5,7 +5,7 @@ import { TrashIcon } from '@/assets'; import { useAppStore } from '@/store'; import styled from 'styled-components'; import { DeleteWarning } from '@/components'; -import { OVERVIEW_ENTITY_TYPES } from '@/types'; +import { K8sActualSource, OVERVIEW_ENTITY_TYPES } from '@/types'; import { useSourceCRUD, useTransition } from '@/hooks'; import { Badge, Button, Divider, Text } from '@/reuseable-components'; @@ -50,10 +50,10 @@ export const MultiSourceControl = () => { }; const onDelete = () => { - const payload = {}; + const payload: Record = {}; - Object.entries(configuredSources).forEach(([namespace, sources]) => { - payload[namespace] = sources.map(({ name, kind }) => ({ name, kind, selected: false })); + Object.entries(configuredSources).forEach(([namespace, sources]: [string, K8sActualSource[]]) => { + payload[namespace] = sources.map((source) => ({ ...source, selected: false })); }); persistSources(payload, {}); diff --git a/frontend/webapp/containers/main/overview/overview-actions-menu/search/search-results/index.tsx b/frontend/webapp/containers/main/overview/overview-actions-menu/search/search-results/index.tsx index 254c81b2c..01b8c254b 100644 --- a/frontend/webapp/containers/main/overview/overview-actions-menu/search/search-results/index.tsx +++ b/frontend/webapp/containers/main/overview/overview-actions-menu/search/search-results/index.tsx @@ -3,7 +3,7 @@ import theme from '@/styles/theme'; import styled from 'styled-components'; import { OVERVIEW_ENTITY_TYPES, WorkloadId } from '@/types'; import { AbsoluteContainer } from '../../styled'; -import { getEntityIcon, getEntityLabel } from '@/utils'; +import { getEntityIcon, getEntityItemId, getEntityLabel } from '@/utils'; import { buildSearchResults, type Category } from './builder'; import { Divider, SelectionButton, Text } from '@/reuseable-components'; import { useActionCRUD, useDestinationCRUD, useInstrumentationRuleCRUD, useNodeDataFlowHandlers, useSourceCRUD } from '@/hooks'; @@ -71,7 +71,7 @@ export const SearchResults = ({ searchText, onClose }: Props) => { icon={getEntityIcon(category as OVERVIEW_ENTITY_TYPES)} label={getEntityLabel(item, category as OVERVIEW_ENTITY_TYPES, { extended: true })} onClick={() => { - const id = item.id || item.ruleId || { kind: item.kind, name: item.name, namespace: item.namespace }; + const id = getEntityItemId(item); // @ts-ignore handleNodeClick(null, { data: { type: category, id } }); onClose(); diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts b/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts index 03777a101..ba912b139 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/build-source-nodes.ts @@ -4,11 +4,11 @@ import { type EntityCounts } from './get-entity-counts'; import { type NodePositions } from './get-node-positions'; import { getMainContainerLanguage } from '@/utils/constants/programming-languages'; import { getEntityIcon, getEntityLabel, getHealthStatus, getProgrammingLanguageIcon } from '@/utils'; -import { NODE_TYPES, OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; +import { type K8sActualSource, NODE_TYPES, OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES } from '@/types'; interface Params { loading: boolean; - entities: ComputePlatformMapped['computePlatform']['k8sActualSources']; + entities: K8sActualSource[]; positions: NodePositions; unfilteredCounts: EntityCounts; containerHeight: number; @@ -52,7 +52,7 @@ export const buildSourceNodes = ({ loading, entities, positions, unfilteredCount nodeWidth, title: 'Sources', icon: getEntityIcon(OVERVIEW_ENTITY_TYPES.SOURCE), - tagValue: unfilteredCounts[OVERVIEW_ENTITY_TYPES.SOURCE], + tagValue: unfilteredCount, }, }); diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/get-entity-counts.ts b/frontend/webapp/containers/main/overview/overview-data-flow/get-entity-counts.ts index bee138503..d2fc66c1a 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/get-entity-counts.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/get-entity-counts.ts @@ -1,17 +1,20 @@ -import { type ComputePlatformMapped, OVERVIEW_ENTITY_TYPES } from '@/types'; +import { OVERVIEW_ENTITY_TYPES } from '@/types'; interface Params { - computePlatform?: ComputePlatformMapped['computePlatform']; + sources?: any[]; + destinations?: any[]; + actions?: any[]; + instrumentationRules?: any[]; } export type EntityCounts = Record; -export const getEntityCounts = ({ computePlatform }: Params) => { +export const getEntityCounts = ({ sources, destinations, actions, instrumentationRules }: Params) => { const unfilteredCounts: EntityCounts = { - [OVERVIEW_ENTITY_TYPES.RULE]: computePlatform?.instrumentationRules.length || 0, - [OVERVIEW_ENTITY_TYPES.SOURCE]: computePlatform?.k8sActualSources.length || 0, - [OVERVIEW_ENTITY_TYPES.ACTION]: computePlatform?.actions.length || 0, - [OVERVIEW_ENTITY_TYPES.DESTINATION]: computePlatform?.destinations.length || 0, + [OVERVIEW_ENTITY_TYPES.SOURCE]: sources?.length || 0, + [OVERVIEW_ENTITY_TYPES.DESTINATION]: destinations?.length || 0, + [OVERVIEW_ENTITY_TYPES.ACTION]: actions?.length || 0, + [OVERVIEW_ENTITY_TYPES.RULE]: instrumentationRules?.length || 0, }; return unfilteredCounts; diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx b/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx index 77acdda75..64335e180 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx +++ b/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx @@ -6,7 +6,7 @@ import { NodeDataFlow } from '@/reuseable-components'; import { MultiSourceControl } from '../multi-source-control'; import { OverviewActionsMenu } from '../overview-actions-menu'; import { type Edge, useEdgesState, useNodesState, type Node, applyNodeChanges } from '@xyflow/react'; -import { useComputePlatform, useContainerSize, useMetrics, useNodeDataFlowHandlers } from '@/hooks'; +import { useComputePlatform, useContainerSize, useMetrics, useNodeDataFlowHandlers, useSourceCRUD } from '@/hooks'; import { buildEdges } from './build-edges'; import { getEntityCounts } from './get-entity-counts'; @@ -35,8 +35,19 @@ export default function OverviewDataFlowContainer() { const positions = useMemo(() => getNodePositions({ containerWidth }), [containerWidth]); const { metrics } = useMetrics(); - const { data, filteredData, loading } = useComputePlatform(); - const unfilteredCounts = useMemo(() => getEntityCounts({ computePlatform: data?.computePlatform }), [data]); + const { sources, filteredSources } = useSourceCRUD(); + const { data, filteredData, loading } = useComputePlatform(); // TODO: remove this in favor of CRUD hooks + + const unfilteredCounts = useMemo( + () => + getEntityCounts({ + sources, + destinations: data?.computePlatform.destinations, + actions: data?.computePlatform.actions, + instrumentationRules: data?.computePlatform.instrumentationRules, + }), + [sources, data], + ); const ruleNodes = useMemo( () => @@ -46,7 +57,7 @@ export default function OverviewDataFlowContainer() { positions, unfilteredCounts, }), - [loading, filteredData?.computePlatform.instrumentationRules, positions, unfilteredCounts], + [loading, filteredData?.computePlatform.instrumentationRules, positions, unfilteredCounts.rule], ); const actionNodes = useMemo( () => @@ -56,7 +67,7 @@ export default function OverviewDataFlowContainer() { positions, unfilteredCounts, }), - [loading, filteredData?.computePlatform.actions, positions, unfilteredCounts], + [loading, filteredData?.computePlatform.actions, positions, unfilteredCounts.action], ); const destinationNodes = useMemo( () => @@ -66,19 +77,19 @@ export default function OverviewDataFlowContainer() { positions, unfilteredCounts, }), - [loading, filteredData?.computePlatform.destinations, positions, unfilteredCounts], + [loading, filteredData?.computePlatform.destinations, positions, unfilteredCounts.destination], ); const sourceNodes = useMemo( () => buildSourceNodes({ loading, - entities: filteredData?.computePlatform.k8sActualSources || [], + entities: filteredSources, positions, unfilteredCounts, containerHeight, onScroll: ({ scrollTop }) => setScrollYOffset(scrollTop), }), - [loading, filteredData?.computePlatform.k8sActualSources, positions, unfilteredCounts, containerHeight], + [loading, filteredSources, positions, unfilteredCounts.source, containerHeight], ); const [nodes, setNodes, onNodesChange] = useNodesState(([] as Node[]).concat(actionNodes, ruleNodes, sourceNodes, destinationNodes)); diff --git a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/source-controls/index.tsx b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/source-controls/index.tsx index 6dcb96291..c9c2e1ec7 100644 --- a/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/source-controls/index.tsx +++ b/frontend/webapp/containers/main/sources/choose-sources/choose-sources-body/choose-sources-body-fast/source-controls/index.tsx @@ -20,7 +20,7 @@ const SearchWrapper = styled.div` `; export const SourceControls: React.FC = ({ selectedSources, searchText, setSearchText, showSelectedOnly, setShowSelectedOnly }) => { - const selectedAppsCount = Object.values(selectedSources).reduce((prev, curr) => prev + curr.length, 0); + const selectedAppsCount = Object.values(selectedSources).reduce((prev, curr) => prev + curr.filter((s) => s.selected).length, 0); return ( <> diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx index 3e710b159..22880e5bd 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import buildCard from './build-card'; import styled from 'styled-components'; import { useDrawerStore } from '@/store'; +import { CodeIcon, ListIcon } from '@/assets'; import buildDrawerItem from './build-drawer-item'; import { ToggleCodeComponent } from '@/components'; import { UpdateSourceBody } from '../update-source-body'; @@ -9,7 +10,7 @@ import { useDescribeSource, useSourceCRUD } from '@/hooks'; import OverviewDrawer from '../../overview/overview-drawer'; import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type K8sActualSource } from '@/types'; import { ACTION, BACKEND_BOOLEAN, DATA_CARDS, getEntityIcon, safeJsonStringify } from '@/utils'; -import { ConditionDetails, DataCard, DataCardRow, DataCardFieldTypes } from '@/reuseable-components'; +import { ConditionDetails, DataCard, DataCardRow, DataCardFieldTypes, Segment } from '@/reuseable-components'; interface Props {} @@ -50,7 +51,7 @@ export const SourceDrawer: React.FC = () => { }, }); - const [isCodeMode, setIsCodeMode] = useState(false); // for "describe source" + const [isPrettyMode, setIsPrettyMode] = useState(true); // for "describe source" const [isEditing, setIsEditing] = useState(false); const [isFormDirty, setIsFormDirty] = useState(false); const [formData, setFormData] = useState({ ...EMPTY_FORM }); @@ -153,14 +154,23 @@ export const SourceDrawer: React.FC = () => { } + action={ + + } data={[ { type: DataCardFieldTypes.CODE, value: JSON.stringify({ language: 'json', - code: safeJsonStringify(isCodeMode ? describe : restructureForPrettyMode(describe)), - pretty: !isCodeMode, + code: safeJsonStringify(isPrettyMode ? restructureForPrettyMode(describe) : describe), + pretty: isPrettyMode, }), width: 'inherit', }, diff --git a/frontend/webapp/cypress/constants/index.ts b/frontend/webapp/cypress/constants/index.ts index 26cf3016f..4c385e863 100644 --- a/frontend/webapp/cypress/constants/index.ts +++ b/frontend/webapp/cypress/constants/index.ts @@ -27,15 +27,17 @@ export const NAMESPACES = { export const SELECTED_ENTITIES = { NAMESPACE: NAMESPACES.DEFAULT, SOURCE: 'frontend', - DESTINATION: 'Jaeger', + DESTINATION_TYPE: 'jaeger', + DESTINATION_DISPLAY_NAME: 'Jaeger', DESTINATION_AUTOFILL_FIELD: 'JAEGER_URL', + DESTINATION_AUTOFILL_VALUE: 'jaeger.tracing:4317', ACTION: 'PiiMasking', INSTRUMENTATION_RULE: 'PayloadCollection', }; export const DATA_IDS = { SELECT_NAMESPACE: `[data-id=namespace-${SELECTED_ENTITIES.NAMESPACE}]`, - SELECT_DESTINATION: `[data-id=destination-${SELECTED_ENTITIES.DESTINATION}]`, + SELECT_DESTINATION: `[data-id=select-potential-destination-${SELECTED_ENTITIES.DESTINATION_TYPE}]`, SELECT_DESTINATION_AUTOFILL_FIELD: `[data-id=${SELECTED_ENTITIES.DESTINATION_AUTOFILL_FIELD}]`, ADD_ENTITY: '[data-id=add-entity]', diff --git a/frontend/webapp/cypress/e2e/02-onboarding.cy.ts b/frontend/webapp/cypress/e2e/02-onboarding.cy.ts index b4604b567..e7eab6a6d 100644 --- a/frontend/webapp/cypress/e2e/02-onboarding.cy.ts +++ b/frontend/webapp/cypress/e2e/02-onboarding.cy.ts @@ -14,8 +14,8 @@ describe('Onboarding', () => { cy.visit(ROUTES.CHOOSE_DESTINATION); cy.contains('button', BUTTONS.ADD_DESTINATION).click(); cy.wait('@gql').then(() => { - cy.get(DATA_IDS.SELECT_DESTINATION).contains(SELECTED_ENTITIES.DESTINATION).should('exist').click(); - expect(DATA_IDS.SELECT_DESTINATION_AUTOFILL_FIELD).to.not.be.empty; + cy.get(DATA_IDS.SELECT_DESTINATION).contains(SELECTED_ENTITIES.DESTINATION_DISPLAY_NAME).should('exist').click(); + cy.get(DATA_IDS.SELECT_DESTINATION_AUTOFILL_FIELD).should('have.value', SELECTED_ENTITIES.DESTINATION_AUTOFILL_VALUE); }); }); diff --git a/frontend/webapp/cypress/e2e/03-sources.cy.ts b/frontend/webapp/cypress/e2e/03-sources.cy.ts index 7ab0250e0..2a4fcf0e4 100644 --- a/frontend/webapp/cypress/e2e/03-sources.cy.ts +++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts @@ -26,8 +26,8 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { - // Wait for 10 seconds to allow the backend to batch an SSE notification - cy.wait(10000).then(() => { + // Wait for 5 seconds to allow the backend to batch an SSE notification + cy.wait(5000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_CREATED).should('exist'); }); @@ -73,8 +73,8 @@ describe('Sources CRUD', () => { cy.wait('@gql').then(() => { getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { - // Wait for 10 seconds to allow the backend to batch an SSE notification - cy.wait(10000).then(() => { + // Wait for 5 seconds to allow the backend to batch an SSE notification + cy.wait(5000).then(() => { cy.get(DATA_IDS.NOTIF_MANAGER_BUTTON).click(); cy.get(DATA_IDS.NOTIF_MANAGER_CONTENR).contains(TEXTS.NOTIF_SOURCES_DELETED).should('exist'); }); diff --git a/frontend/webapp/cypress/e2e/04-destinations.cy.ts b/frontend/webapp/cypress/e2e/04-destinations.cy.ts index a510d03c7..02a20544e 100644 --- a/frontend/webapp/cypress/e2e/04-destinations.cy.ts +++ b/frontend/webapp/cypress/e2e/04-destinations.cy.ts @@ -18,8 +18,8 @@ describe('Destinations CRUD', () => { cy.get(DATA_IDS.ADD_ENTITY).click(); cy.get(DATA_IDS.ADD_DESTINATION).click(); cy.get(DATA_IDS.MODAL_ADD_DESTINATION).should('exist'); - cy.get(DATA_IDS.SELECT_DESTINATION).contains(SELECTED_ENTITIES.DESTINATION).click(); - expect(DATA_IDS.SELECT_DESTINATION_AUTOFILL_FIELD).to.not.be.empty; + cy.get(DATA_IDS.SELECT_DESTINATION).contains(SELECTED_ENTITIES.DESTINATION_DISPLAY_NAME).should('exist').click(); + cy.get(DATA_IDS.SELECT_DESTINATION_AUTOFILL_FIELD).should('have.value', SELECTED_ENTITIES.DESTINATION_AUTOFILL_VALUE); cy.get('button').contains(BUTTONS.DONE).click(); cy.wait('@gql').then(() => { @@ -35,7 +35,7 @@ describe('Destinations CRUD', () => { updateEntity( { nodeId: DATA_IDS.DESTINATION_NODE, - nodeContains: SELECTED_ENTITIES.DESTINATION, + nodeContains: SELECTED_ENTITIES.DESTINATION_DISPLAY_NAME, fieldKey: DATA_IDS.TITLE, fieldValue: TEXTS.UPDATED_NAME, }, @@ -58,7 +58,7 @@ describe('Destinations CRUD', () => { deleteEntity( { nodeId: DATA_IDS.DESTINATION_NODE, - nodeContains: SELECTED_ENTITIES.DESTINATION, + nodeContains: SELECTED_ENTITIES.DESTINATION_DISPLAY_NAME, warnModalTitle: TEXTS.DESTINATION_WARN_MODAL_TITLE, warnModalNote: TEXTS.DESTINATION_WARN_MODAL_NOTE, }, diff --git a/frontend/webapp/graphql/queries/compute-platform.ts b/frontend/webapp/graphql/queries/compute-platform.ts index f39a4fe59..a9a44539e 100644 --- a/frontend/webapp/graphql/queries/compute-platform.ts +++ b/frontend/webapp/graphql/queries/compute-platform.ts @@ -7,27 +7,6 @@ export const GET_COMPUTE_PLATFORM = gql` name selected } - k8sActualSources { - namespace - name - kind - numberOfInstances - selected - reportedName - containers { - containerName - language - runtimeVersion - otherAgent - } - conditions { - status - type - reason - message - lastTransitionTime - } - } destinations { id name @@ -119,3 +98,33 @@ export const GET_NAMESPACES = gql` } } `; + +export const GET_SOURCES = gql` + query GetSources($nextPage: String!) { + computePlatform { + sources(nextPage: $nextPage) { + nextPage + items { + namespace + name + kind + selected + reportedName + containers { + containerName + language + runtimeVersion + otherAgent + } + conditions { + status + type + reason + message + lastTransitionTime + } + } + } + } + } +`; diff --git a/frontend/webapp/hooks/actions/useActionFormData.ts b/frontend/webapp/hooks/actions/useActionFormData.ts index 5f6aed157..6c2ff3585 100644 --- a/frontend/webapp/hooks/actions/useActionFormData.ts +++ b/frontend/webapp/hooks/actions/useActionFormData.ts @@ -1,6 +1,6 @@ import { useGenericForm } from '@/hooks'; -import { FORM_ALERTS, safeJsonParse } from '@/utils'; import { DrawerItem, useNotificationStore } from '@/store'; +import { FORM_ALERTS, isEmpty, safeJsonParse } from '@/utils'; import { ActionsType, LatencySamplerSpec, NOTIFICATION_TYPE, type ActionDataParsed, type ActionInput } from '@/types'; const INITIAL: ActionInput = { @@ -13,31 +13,33 @@ const INITIAL: ActionInput = { details: '', }; +type Errors = { + type?: string; + signals?: string; + details?: string; +}; + export function useActionFormData() { const { addNotification } = useNotificationStore(); const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL); const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { - const errors = {}; + const errors: Errors = {}; let ok = true; Object.entries(formData).forEach(([k, v]) => { switch (k) { case 'type': case 'signals': - if (Array.isArray(v) ? !v.length : !v) { - errors[k] = FORM_ALERTS.FIELD_IS_REQUIRED; - } + if (isEmpty(v)) errors[k as keyof Errors] = FORM_ALERTS.FIELD_IS_REQUIRED; break; case 'details': - if (Array.isArray(v) ? !v.length : !v) { - errors[k] = FORM_ALERTS.FIELD_IS_REQUIRED; - } + if (isEmpty(v)) errors[k as keyof Errors] = FORM_ALERTS.FIELD_IS_REQUIRED; if (formData.type === ActionsType.LATENCY_SAMPLER) { (safeJsonParse(v as string, { endpoints_filters: [] }) as LatencySamplerSpec).endpoints_filters.forEach((endpoint) => { if (endpoint.http_route.charAt(0) !== '/') { - errors[k] = FORM_ALERTS.LATENCY_HTTP_ROUTE; + errors[k as keyof Errors] = FORM_ALERTS.LATENCY_HTTP_ROUTE; } }); } diff --git a/frontend/webapp/hooks/common/useGenericForm.ts b/frontend/webapp/hooks/common/useGenericForm.ts index 4b60a5e6a..de4431018 100644 --- a/frontend/webapp/hooks/common/useGenericForm.ts +++ b/frontend/webapp/hooks/common/useGenericForm.ts @@ -1,7 +1,7 @@ import { useState } from 'react'; -export const useGenericForm =
>(initialFormData: Form) => { - function copyInitial() { +export const useGenericForm = >(initialFormData: Form) => { + function copyInitial(): Form { // this is to avoid reference issues with the initial form data, // when an object has arrays or objects as part of it's values, a simple spread operator won't work, the children would act as references, // so we use JSON.parse(JSON.stringify()) to create a deep copy of the object without affecting the original @@ -11,27 +11,38 @@ export const useGenericForm = >(initialFormData: Form const [formData, setFormData] = useState(copyInitial()); const [formErrors, setFormErrors] = useState>>({}); - const handleFormChange = (key?: keyof typeof formData, val?: any, obj?: typeof formData) => { - if (!!key) { + const handleFormChange = (key?: keyof Form | string, val?: any, obj?: Form) => { + if (key) { // this is for cases where the form contains objects such as "exportedSignals", // the object's child is targeted with a ".dot" for example: "exportedSignals.logs" - const [parentKey, childKey] = (key as string).split('.'); - - if (!!childKey) { - setFormData((prev) => ({ ...prev, [parentKey]: { ...prev[parentKey], [childKey]: val } })); - } else { - setFormData((prev) => ({ ...prev, [parentKey]: val })); - } - } else if (!!obj) { + const [parentKey, childKey] = key.toString().split('.'); + + setFormData((prev) => { + if (childKey) { + return { + ...prev, + [parentKey]: { + ...(prev[parentKey] as Record), + [childKey]: val, + }, + }; + } else { + return { + ...prev, + [parentKey]: val, + }; + } + }); + } else if (obj) { setFormData({ ...obj }); } }; - const handleErrorChange = (key?: keyof typeof formErrors, val?: string, obj?: typeof formErrors) => { - if (!!key) { + const handleErrorChange = (key?: keyof Form | string, val?: string, obj?: Partial>) => { + if (key) { setFormErrors((prev) => ({ ...prev, [key]: val })); - } else if (!!obj) { + } else if (obj) { setFormErrors({ ...obj }); } }; diff --git a/frontend/webapp/hooks/compute-platform/index.ts b/frontend/webapp/hooks/compute-platform/index.ts index 6023020f7..c26404596 100644 --- a/frontend/webapp/hooks/compute-platform/index.ts +++ b/frontend/webapp/hooks/compute-platform/index.ts @@ -1,2 +1,3 @@ export * from './useComputePlatform'; export * from './useNamespace'; +export * from './usePaginatedSources'; diff --git a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts index be53749ad..e48a3b391 100644 --- a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts +++ b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts @@ -3,8 +3,8 @@ import { useQuery } from '@apollo/client'; import { useNotificationStore } from '@/store'; import { GET_COMPUTE_PLATFORM } from '@/graphql'; import { useFilterStore } from '@/store/useFilterStore'; -import { ACTION, BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils'; -import { NOTIFICATION_TYPE, type ActionItem, type ComputePlatform, type ComputePlatformMapped } from '@/types'; +import { ACTION, deriveTypeFromRule, safeJsonParse } from '@/utils'; +import { NOTIFICATION_TYPE, SupportedSignals, type ActionItem, type ComputePlatform, type ComputePlatformMapped } from '@/types'; type UseComputePlatformHook = { data?: ComputePlatformMapped; @@ -16,6 +16,8 @@ type UseComputePlatformHook = { export const useComputePlatform = (): UseComputePlatformHook => { const { addNotification } = useNotificationStore(); + + // TODO: move filters to CRUD hooks const filters = useFilterStore(); const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM, { @@ -27,22 +29,28 @@ export const useComputePlatform = (): UseComputePlatformHook => { }), }); - const mappedData = useMemo(() => { + const mappedCP = useMemo(() => { if (!data) return undefined; return { computePlatform: { ...data.computePlatform, + + // sources are now paginated, refer to "usePaginatedSources" hook & "usePaginatedStore" store + sources: undefined, + actions: data.computePlatform.actions.map((item) => { const parsedSpec = typeof item.spec === 'string' ? safeJsonParse(item.spec, {} as ActionItem) : item.spec; return { ...item, spec: parsedSpec }; }), + instrumentationRules: data.computePlatform.instrumentationRules.map((item) => { const type = deriveTypeFromRule(item); return { ...item, type }; }), + destinations: data.computePlatform.destinations.map((item) => { // Replace deprecated string values, with boolean values const fields = @@ -68,42 +76,32 @@ export const useComputePlatform = (): UseComputePlatformHook => { }; }, [data]); - const filteredData = useMemo(() => { - if (!mappedData) return undefined; + // TODO: move filters to CRUD hooks + const filteredCP = useMemo(() => { + if (!mappedCP) return undefined; - let k8sActualSources = [...mappedData.computePlatform.k8sActualSources]; - let destinations = [...mappedData.computePlatform.destinations]; - let actions = [...mappedData.computePlatform.actions]; + let destinations = [...mappedCP.computePlatform.destinations]; + let actions = [...mappedCP.computePlatform.actions]; - if (!!filters.namespace) { - k8sActualSources = k8sActualSources.filter((source) => filters.namespace?.id === source.namespace); - } - if (!!filters.types.length) { - k8sActualSources = k8sActualSources.filter((source) => !!filters.types.find((type) => type.id === source.kind)); - } - if (!!filters.onlyErrors) { - k8sActualSources = k8sActualSources.filter((source) => !!source.conditions?.find((cond) => cond.status === BACKEND_BOOLEAN.FALSE)); - } - if (!!filters.errors.length) { - k8sActualSources = k8sActualSources.filter((source) => !!filters.errors.find((error) => !!source.conditions?.find((cond) => cond.message === error.id))); - } - if (!!filters.languages.length) { - k8sActualSources = k8sActualSources.filter((source) => !!filters.languages.find((language) => !!source.containers?.find((cont) => cont.language === language.id))); - } if (!!filters.monitors.length) { - destinations = destinations.filter((destination) => !!filters.monitors.find((metric) => destination.exportedSignals[metric.id])); + destinations = destinations.filter((destination) => !!filters.monitors.find((metric) => destination.exportedSignals[metric.id as keyof SupportedSignals])); actions = actions.filter((action) => !!filters.monitors.find((metric) => action.spec.signals.find((str) => str.toLowerCase() === metric.id))); } return { computePlatform: { - ...mappedData.computePlatform, - k8sActualSources, + ...mappedCP.computePlatform, destinations, actions, }, }; - }, [mappedData, filters]); + }, [mappedCP, filters]); - return { data: mappedData, filteredData, loading, error, refetch }; + return { + data: mappedCP, + filteredData: filteredCP, + loading, + error, + refetch, + }; }; diff --git a/frontend/webapp/hooks/compute-platform/usePaginatedSources.ts b/frontend/webapp/hooks/compute-platform/usePaginatedSources.ts new file mode 100644 index 000000000..7d14f9a83 --- /dev/null +++ b/frontend/webapp/hooks/compute-platform/usePaginatedSources.ts @@ -0,0 +1,57 @@ +import { useEffect } from 'react'; +import { ACTION } from '@/utils'; +import { GET_SOURCES } from '@/graphql'; +import { useLazyQuery } from '@apollo/client'; +import { useNotificationStore, usePaginatedStore } from '@/store'; +import { NOTIFICATION_TYPE, type ComputePlatform } from '@/types'; + +export const usePaginatedSources = () => { + const { addNotification } = useNotificationStore(); + const { sources, addSources, setSources, sourcesNotFinished, setSourcesNotFinished, sourcesFetching, setSourcesFetching } = usePaginatedStore(); + + const [getSources, { loading }] = useLazyQuery<{ computePlatform: { sources: ComputePlatform['computePlatform']['sources'] } }>(GET_SOURCES, { + onError: (error) => + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: error.name || ACTION.FETCH, + message: error.cause?.message || error.message, + }), + }); + + const fetchSources = async (getAll: boolean = false, nextPage: string = '') => { + if (nextPage === '') setSources([]); + setSourcesFetching(true); + const { data } = await getSources({ variables: { nextPage } }); + + if (!!data?.computePlatform.sources) { + const { nextPage, items } = data.computePlatform.sources; + + addSources(items); + + if (getAll) { + if (!!nextPage) { + // This timeout is to prevent react-flow from flickering on re-renders + setTimeout(() => fetchSources(true, nextPage), 10); + } else { + setSourcesNotFinished(false); + setSourcesFetching(false); + } + } else if (!!nextPage) { + setSourcesNotFinished(true); + setSourcesFetching(false); + } + } + }; + + // Fetch 1 batch on initial mount + useEffect(() => { + if (!sources.length && !loading && !sourcesFetching) fetchSources(true); + }, []); + + return { + sources, + fetchSources, + sourcesNotFinished, + sourcesFetching, + }; +}; diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 916bece0d..c490a1b1f 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -156,7 +156,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp }, [supportedSignals]); const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { - const errors = {}; + const errors: Record = {}; let ok = true; dynamicFields.forEach(({ name, value, required }) => { diff --git a/frontend/webapp/hooks/destinations/useDestinationTypes.ts b/frontend/webapp/hooks/destinations/useDestinationTypes.ts index 0b4ac1867..83da137ba 100644 --- a/frontend/webapp/hooks/destinations/useDestinationTypes.ts +++ b/frontend/webapp/hooks/destinations/useDestinationTypes.ts @@ -21,7 +21,7 @@ export function useDestinationTypes() { setDestinations( data.destinationTypes.categories.map((category) => ({ name: category.name, - description: CATEGORIES_DESCRIPTION[category.name], + description: CATEGORIES_DESCRIPTION[category.name as keyof typeof CATEGORIES_DESCRIPTION], items: category.items, })), ); diff --git a/frontend/webapp/hooks/destinations/usePotentialDestinations.ts b/frontend/webapp/hooks/destinations/usePotentialDestinations.ts index d4ab2d837..fe2efa35c 100644 --- a/frontend/webapp/hooks/destinations/usePotentialDestinations.ts +++ b/frontend/webapp/hooks/destinations/usePotentialDestinations.ts @@ -1,56 +1,74 @@ import { useMemo } from 'react'; import { safeJsonParse } from '@/utils'; import { useQuery } from '@apollo/client'; +import { IAppState, useAppStore } from '@/store'; import { GetDestinationTypesResponse } from '@/types'; import { GET_DESTINATION_TYPE, GET_POTENTIAL_DESTINATIONS } from '@/graphql'; -interface DestinationDetails { +interface PotentialDestination { type: string; fields: string; } interface GetPotentialDestinationsData { - potentialDestinations: DestinationDetails[]; + potentialDestinations: PotentialDestination[]; } +const checkIfConfigured = (configuredDest: IAppState['configuredDestinations'][0], potentialDest: PotentialDestination, autoFilledFields: Record) => { + const typesMatch = configuredDest.stored.type === potentialDest.type; + if (!typesMatch) return false; + + let fieldsMatch = false; + + for (const { key, value } of configuredDest.form.fields) { + if (Object.hasOwn(autoFilledFields, key)) { + if (autoFilledFields[key] === value) { + fieldsMatch = true; + } else { + fieldsMatch = false; + break; + } + } + } + + return fieldsMatch; +}; + export const usePotentialDestinations = () => { - const { data: destinationTypesData } = - useQuery(GET_DESTINATION_TYPE); - const { loading, error, data } = useQuery( - GET_POTENTIAL_DESTINATIONS - ); + const { configuredDestinations } = useAppStore(); + const { data: { destinationTypes } = {} } = useQuery(GET_DESTINATION_TYPE); + const { loading, error, data: { potentialDestinations } = {} } = useQuery(GET_POTENTIAL_DESTINATIONS); const mappedPotentialDestinations = useMemo(() => { - if (!destinationTypesData || !data) return []; + if (!destinationTypes || !potentialDestinations) return []; // Create a deep copy of destination types to manipulate - const destinationTypesCopy = JSON.parse( - JSON.stringify(destinationTypesData.destinationTypes.categories) - ); + const categories: GetDestinationTypesResponse['destinationTypes']['categories'] = JSON.parse(JSON.stringify(destinationTypes.categories)); // Map over the potential destinations - return data.potentialDestinations.map((destination) => { - for (const category of destinationTypesCopy) { - const index = category.items.findIndex( - (item) => item.type === destination.type - ); - if (index !== -1) { - // Spread the matched destination type data into the potential destination - const matchedType = category.items[index]; - category.items.splice(index, 1); // Remove the matched item from destination types - return { - ...destination, - ...matchedType, - fields: safeJsonParse<{ [key: string]: string }>( - destination.fields, - {} - ), - }; + return potentialDestinations + .map((pd) => { + for (const category of categories) { + const autoFilledFields = safeJsonParse<{ [key: string]: string }>(pd.fields, {}); + const alreadyConfigured = !!configuredDestinations.find((cd) => checkIfConfigured(cd, pd, autoFilledFields)); + + if (!alreadyConfigured) { + const idx = category.items.findIndex((item) => item.type === pd.type); + + if (idx !== -1) { + return { + // Spread the matched destination type data into the potential destination + ...category.items[idx], + fields: autoFilledFields, + }; + } + } } - } - return destination; - }); - }, [destinationTypesData, data]); + + return null; + }) + .filter((pd) => !!pd); + }, [configuredDestinations, destinationTypes, potentialDestinations]); return { loading, diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts index e430d7ff7..8fbd13b12 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts @@ -22,7 +22,7 @@ export function useInstrumentationRuleFormData() { const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL); const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { - const errors = {}; + const errors: Partial> = {}; let ok = true; Object.entries(formData).forEach(([k, v]) => { diff --git a/frontend/webapp/hooks/notification/useClickNotif.ts b/frontend/webapp/hooks/notification/useClickNotif.ts index 0be9bc130..ffb16fc52 100644 --- a/frontend/webapp/hooks/notification/useClickNotif.ts +++ b/frontend/webapp/hooks/notification/useClickNotif.ts @@ -2,7 +2,7 @@ import { useSourceCRUD } from '../sources'; import { useActionCRUD } from '../actions'; import { getIdFromSseTarget } from '@/utils'; import { useDestinationCRUD } from '../destinations'; -import { type Notification, OVERVIEW_ENTITY_TYPES } from '@/types'; +import { type Notification, OVERVIEW_ENTITY_TYPES, WorkloadId } from '@/types'; import { useInstrumentationRuleCRUD } from '../instrumentation-rules'; import { DrawerItem, useDrawerStore, useNotificationStore } from '@/store'; @@ -33,7 +33,10 @@ export const useClickNotif = () => { case 'InstrumentationInstance': drawerItem['type'] = OVERVIEW_ENTITY_TYPES.SOURCE; drawerItem['id'] = getIdFromSseTarget(target, OVERVIEW_ENTITY_TYPES.SOURCE); - drawerItem['item'] = sources.find((item) => item.kind === drawerItem['id']?.['kind'] && item.name === drawerItem['id']?.['name'] && item.namespace === drawerItem['id']?.['namespace']); + drawerItem['item'] = sources.find( + (item) => item.kind === (drawerItem['id'] as WorkloadId).kind && item.name === (drawerItem['id'] as WorkloadId).name && item.namespace === (drawerItem['id'] as WorkloadId).namespace, + ); + break; case OVERVIEW_ENTITY_TYPES.ACTION: diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts index 463e89d9d..1a906eb8b 100644 --- a/frontend/webapp/hooks/notification/useSSE.ts +++ b/frontend/webapp/hooks/notification/useSSE.ts @@ -1,11 +1,12 @@ import { useEffect, useRef } from 'react'; import { API } from '@/utils'; import { NOTIFICATION_TYPE } from '@/types'; -import { useComputePlatform } from '../compute-platform'; +import { useComputePlatform, usePaginatedSources } from '../compute-platform'; import { type NotifyPayload, useConnectionStore, useNotificationStore, usePendingStore } from '@/store'; export const useSSE = () => { const { setPendingItems } = usePendingStore(); + const { fetchSources } = usePaginatedSources(); const { addNotification } = useNotificationStore(); const { setConnectionStore } = useConnectionStore(); const { refetch: refetchComputePlatform } = useComputePlatform(); @@ -30,7 +31,18 @@ export const useSSE = () => { }; addNotification(notification); - refetchComputePlatform(); + + if (notification.crdType === 'InstrumentationConfig') { + // We handle update in CRUD hook, refetch only on create + if (['Added', 'Deleted'].includes(notification.title || '')) fetchSources(true); + } else { + refetchComputePlatform(); + } + + // This works for now, + // but in the future we might have to change this to "removePendingItems", + // and remove the specific pending items based on their entityType and entityId + setPendingItems([]); // This works for now, // but in the future we might have to change this to "removePendingItems", diff --git a/frontend/webapp/hooks/overview/useMetrics.ts b/frontend/webapp/hooks/overview/useMetrics.ts index 2f6be9934..93c262aaa 100644 --- a/frontend/webapp/hooks/overview/useMetrics.ts +++ b/frontend/webapp/hooks/overview/useMetrics.ts @@ -1,9 +1,15 @@ import { useQuery } from '@apollo/client'; -import { GET_METRICS } from '@/graphql/mutations/metrics'; +import { useSourceCRUD } from '../sources'; +import { useDestinationCRUD } from '../destinations'; import type { OverviewMetricsResponse } from '@/types'; +import { GET_METRICS } from '@/graphql/mutations/metrics'; export const useMetrics = () => { + const { sources } = useSourceCRUD(); + const { destinations } = useDestinationCRUD(); + const { data } = useQuery(GET_METRICS, { + skip: !sources.length && !destinations.length, pollInterval: 3000, }); diff --git a/frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts b/frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts index 59bd4ed80..ffa3361ef 100644 --- a/frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts +++ b/frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts @@ -31,19 +31,36 @@ export const useNodeDataFlowHandlers = () => { data: { id, type }, } = object; - const entities = { - sources, - actions, - destinations, - rules: instrumentationRules, - }; - if (type === OVERVIEW_ENTITY_TYPES.SOURCE) { const { kind, name, namespace } = id as WorkloadId; - const selectedDrawerItem = entities['sources'].find((item) => item.kind === kind && item.name === name && item.namespace === namespace); + const selectedDrawerItem = sources.find((item) => item.kind === kind && item.name === name && item.namespace === namespace); + if (!selectedDrawerItem) { + console.warn('Selected item not found', { id, sourcesCount: sources.length }); + return; + } + + setSelectedItem({ + id, + type, + item: selectedDrawerItem, + }); + } else if (type === OVERVIEW_ENTITY_TYPES.ACTION) { + const selectedDrawerItem = actions.find((item) => item.id === id); + if (!selectedDrawerItem) { + console.warn('Selected item not found', { id, actionsCount: actions.length }); + return; + } + + setSelectedItem({ + id, + type, + item: selectedDrawerItem, + }); + } else if (type === OVERVIEW_ENTITY_TYPES.DESTINATION) { + const selectedDrawerItem = destinations.find((item) => item.id === id); if (!selectedDrawerItem) { - console.warn('Selected item not found', { id, [`${type}sCount`]: entities[`${type}s`].length }); + console.warn('Selected item not found', { id, destinationsCount: destinations.length }); return; } @@ -52,16 +69,16 @@ export const useNodeDataFlowHandlers = () => { type, item: selectedDrawerItem, }); - } else if ([OVERVIEW_ENTITY_TYPES.RULE, OVERVIEW_ENTITY_TYPES.ACTION, OVERVIEW_ENTITY_TYPES.DESTINATION].includes(type as OVERVIEW_ENTITY_TYPES)) { - const selectedDrawerItem = entities[`${type}s`].find((item) => id && [item.id, item.ruleId].includes(id)); + } else if (type === OVERVIEW_ENTITY_TYPES.RULE) { + const selectedDrawerItem = instrumentationRules.find((item) => item.ruleId === id); if (!selectedDrawerItem) { - console.warn('Selected item not found', { id, [`${type}sCount`]: entities[`${type}s`].length }); + console.warn('Selected item not found', { id, rulesCount: instrumentationRules.length }); return; } setSelectedItem({ id, - type: type as OVERVIEW_ENTITY_TYPES, + type, item: selectedDrawerItem, }); } else if (type === OVERVIEW_NODE_TYPES.ADD_RULE) { diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index 11a69ac66..bbbde5378 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -1,8 +1,9 @@ +import { useMemo } from 'react'; import { useMutation } from '@apollo/client'; -import { ACTION, getSseTargetFromId } from '@/utils'; +import { useNamespace } from '../compute-platform'; import { PERSIST_SOURCE, UPDATE_K8S_ACTUAL_SOURCE } from '@/graphql'; -import { useComputePlatform, useNamespace } from '../compute-platform'; -import { type PendingItem, useAppStore, useNotificationStore, usePendingStore } from '@/store'; +import { ACTION, BACKEND_BOOLEAN, getSseTargetFromId } from '@/utils'; +import { type PendingItem, useAppStore, useFilterStore, useNotificationStore, usePaginatedStore, usePendingStore } from '@/store'; import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type PatchSourceRequestInput, NOTIFICATION_TYPE, type K8sActualSource } from '@/types'; interface Params { @@ -12,7 +13,9 @@ interface Params { export const useSourceCRUD = (params?: Params) => { const { persistNamespace } = useNamespace(); - const { data, refetch } = useComputePlatform(); + + const filters = useFilterStore(); + const { sources, updateSource, removeSource } = usePaginatedStore(); const { setConfiguredSources } = useAppStore(); const { addPendingItems, removePendingItems } = usePendingStore(); const { addNotification, removeNotifications } = useNotificationStore(); @@ -38,13 +41,13 @@ export const useSourceCRUD = (params?: Params) => { params?.onSuccess?.(actionType); }; - const [createOrDeleteSources, cdState] = useMutation<{ persistK8sSources: boolean }>(PERSIST_SOURCE, { + const [persistSources, cdState] = useMutation<{ persistK8sSources: boolean }>(PERSIST_SOURCE, { onError: (error) => handleError('', error.message), onCompleted: (res, req) => { const namespace = req?.variables?.namespace; const count = req?.variables?.sources.length; - req?.variables?.sources.forEach(({ name, kind, selected }) => { + req?.variables?.sources.forEach(({ name, kind, selected }: { name: string; kind: string; selected: boolean }) => { if (!selected) removeNotifications(getSseTargetFromId({ namespace, name, kind }, OVERVIEW_ENTITY_TYPES.SOURCE)); }); @@ -57,7 +60,7 @@ export const useSourceCRUD = (params?: Params) => { }, }); - const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { + const [updateSourceName, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, { onError: (error) => handleError(ACTION.UPDATE, error.message), onCompleted: (res, req) => { handleComplete(ACTION.UPDATE); @@ -68,18 +71,31 @@ export const useSourceCRUD = (params?: Params) => { // Not that there's anything about a watcher that would break the UI, it's just that we would receive unexpected events with ridiculous amounts, // (example: instrument 5 apps, update the name of 2, then uninstrument the other 3, we would get an SSE with minimum 10 updated sources, when we expect it to show only 2 due to name change). setTimeout(() => { - const id = req?.variables?.sourceId; + const { sourceId, patchSourceRequest } = req?.variables || {}; - refetch(); - notifyUser(NOTIFICATION_TYPE.SUCCESS, ACTION.UPDATE, 'Successfully updated 1 source', id); - removePendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: id }]); + updateSource(sourceId, patchSourceRequest); + notifyUser(NOTIFICATION_TYPE.SUCCESS, ACTION.UPDATE, 'Successfully updated 1 source', sourceId); + removePendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: sourceId }]); }, 2000); }, }); + const filtered = useMemo(() => { + let arr = [...sources]; + + if (!!filters.namespace) arr = arr.filter((source) => filters.namespace?.id === source.namespace); + if (!!filters.types.length) arr = arr.filter((source) => !!filters.types.find((type) => type.id === source.kind)); + if (!!filters.onlyErrors) arr = arr.filter((source) => !!source.conditions?.find((cond) => cond.status === BACKEND_BOOLEAN.FALSE)); + if (!!filters.errors.length) arr = arr.filter((source) => !!filters.errors.find((error) => !!source.conditions?.find((cond) => cond.message === error.id))); + if (!!filters.languages.length) arr = arr.filter((source) => !!filters.languages.find((language) => !!source.containers?.find((cont) => cont.language === language.id))); + + return arr; + }, [sources, filters]); + return { loading: cdState.loading || uState.loading, - sources: data?.computePlatform.k8sActualSources || [], + sources, + filteredSources: filtered, persistSources: async (selectAppsList: { [key: string]: K8sActualSource[] }, futureSelectAppsList: { [key: string]: boolean }) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Persisting sources...', undefined, true); @@ -99,7 +115,7 @@ export const useSourceCRUD = (params?: Params) => { if (!!sendToGql.length) hasSources = true; addPendingItems(addToPendingStore); - await createOrDeleteSources({ variables: { namespace, sources: sendToGql } }); + await persistSources({ variables: { namespace, sources: sendToGql } }); } for (const [namespace, futureSelected] of Object.entries(futureSelectAppsList)) { @@ -112,7 +128,7 @@ export const useSourceCRUD = (params?: Params) => { updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => { notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating source...', undefined, true); addPendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: sourceId }]); - await updateSource({ variables: { sourceId, patchSourceRequest } }); + await updateSourceName({ variables: { sourceId, patchSourceRequest } }); }, }; }; diff --git a/frontend/webapp/reuseable-components/code/index.tsx b/frontend/webapp/reuseable-components/code/index.tsx index e49e1c4be..63ebb39c0 100644 --- a/frontend/webapp/reuseable-components/code/index.tsx +++ b/frontend/webapp/reuseable-components/code/index.tsx @@ -123,7 +123,7 @@ const getComponentsFromPropertyString = (propertyString: string) => { const PrettyJsonCode: React.FC<{ str: string }> = ({ str }) => { const renderEmptyRows = (count: number = 2) => { - const rows = new Array(count).fill((props) => ( + const rows = new Array(count).fill((props: React.HTMLAttributes) => ( @@ -169,7 +169,7 @@ const PrettyJsonCode: React.FC<{ str: string }> = ({ str }) => { return ( - + {...components} {text} diff --git a/frontend/webapp/reuseable-components/condition-details/index.tsx b/frontend/webapp/reuseable-components/condition-details/index.tsx index 42ac4a9bf..e1ced0811 100644 --- a/frontend/webapp/reuseable-components/condition-details/index.tsx +++ b/frontend/webapp/reuseable-components/condition-details/index.tsx @@ -45,7 +45,7 @@ export const ConditionDetails: React.FC = ({ conditions = [] }) => { const loading = useMemo(() => !conditions.length, [conditions]); const errors = useMemo(() => conditions.filter(({ status }) => status === BACKEND_BOOLEAN.FALSE), [conditions]); const hasErrors = !!errors.length; - const headerText = loading ? 'Loading...' : hasErrors ? 'Operation Failed' : 'Operation Successful'; + const headerText = loading ? 'Loading...' : hasErrors ? 'Instrumentation Failed' : 'Instrumentation Successful'; const HeaderIcon = getStatusIcon(hasErrors ? NOTIFICATION_TYPE.ERROR : NOTIFICATION_TYPE.SUCCESS); return ( diff --git a/frontend/webapp/reuseable-components/index.ts b/frontend/webapp/reuseable-components/index.ts index e0a2e3346..54feac960 100644 --- a/frontend/webapp/reuseable-components/index.ts +++ b/frontend/webapp/reuseable-components/index.ts @@ -10,7 +10,6 @@ export * from './tooltip'; export * from './dropdown'; export * from './divider'; export * from './toggle'; -export * from './toggle-buttons'; export * from './checkbox'; export * from './modal'; export * from './modal/warning-modal'; @@ -39,3 +38,4 @@ export * from './data-tab'; export * from './code'; export * from './icon-button'; export * from './icon-wrapped'; +export * from './segment'; diff --git a/frontend/webapp/reuseable-components/input-table/index.tsx b/frontend/webapp/reuseable-components/input-table/index.tsx index b1f4e7297..6e3046838 100644 --- a/frontend/webapp/reuseable-components/input-table/index.tsx +++ b/frontend/webapp/reuseable-components/input-table/index.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef, useMemo, type KeyboardEventHandler import styled from 'styled-components'; import { PlusIcon, TrashIcon } from '@/assets'; import { Button, FieldError, FieldLabel, Input, Text } from '@/reuseable-components'; +import { isEmpty } from '@/utils'; type Row = { [key: string]: any; @@ -62,7 +63,7 @@ export const InputTable: React.FC = ({ columns, initialValues = [], value useEffect(() => { if (!rows.length) { - const init = {}; + const init: Row = {}; columns.forEach(({ keyName }) => (init[keyName] = '')); setInitialRow(init); setRows([{ ...init }]); @@ -70,7 +71,7 @@ export const InputTable: React.FC = ({ columns, initialValues = [], value }, []); // Filter out rows where either key or value is empty - const validRows = useMemo(() => rows.filter((row) => !Object.values(row).filter((val) => !val).length), [rows]); + const validRows = useMemo(() => rows.filter((row) => !Object.values(row).filter((val) => isEmpty(val)).length), [rows]); const recordedRows = useRef(JSON.stringify(validRows)); useEffect(() => { @@ -140,9 +141,9 @@ export const InputTable: React.FC = ({ columns, initialValues = [], value placeholder={placeholder} value={value} onChange={({ target: { value: val } }) => handleChange(keyName, type === 'number' ? Number(val) : val, idx)} - autoFocus={!value && rows.length > 1 && idx === rows.length - 1 && innerIdx === 0} + autoFocus={isEmpty(value) && rows.length > 1 && idx === rows.length - 1 && innerIdx === 0} style={{ maxWidth, paddingLeft: 10 }} - hasError={!!errorMessage && (!required || (required && !value))} + hasError={!!errorMessage && (!required || (required && isEmpty(value)))} /> ); diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/header-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/header-node.tsx index 5a9b1c1b4..21a5cd5ea 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/header-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/header-node.tsx @@ -1,10 +1,10 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; -import { useSourceCRUD } from '@/hooks'; +import { usePaginatedSources, useSourceCRUD } from '@/hooks'; import type { Node, NodeProps } from '@xyflow/react'; import { useAppStore, usePendingStore } from '@/store'; -import { NODE_TYPES, OVERVIEW_ENTITY_TYPES } from '@/types'; -import { Badge, Checkbox, Text } from '@/reuseable-components'; +import { K8sActualSource, NODE_TYPES, OVERVIEW_ENTITY_TYPES } from '@/types'; +import { Badge, Checkbox, FadeLoader, Text } from '@/reuseable-components'; interface Props extends NodeProps< @@ -42,6 +42,7 @@ const HeaderNode: React.FC = ({ data }) => { const isSources = title === 'Sources'; const { configuredSources, setConfiguredSources } = useAppStore(); + const { sourcesFetching } = usePaginatedSources(); const { isThisPending } = usePendingStore(); const { sources } = useSourceCRUD(); @@ -60,7 +61,7 @@ const HeaderNode: React.FC = ({ data }) => { const onSelect = (bool: boolean) => { if (bool) { - const payload = {}; + const payload: Record = {}; sources.forEach((source) => { const id = { namespace: source.namespace, name: source.name, kind: source.kind }; @@ -93,6 +94,7 @@ const HeaderNode: React.FC = ({ data }) => { {Icon && } {title} + {isSources && sourcesFetching && } {renderActions()} diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/scroll-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/scroll-node.tsx index 1d21add70..1a0ab0eae 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/scroll-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/scroll-node.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { SVG } from '@/assets'; import BaseNode from './base-node'; import styled from 'styled-components'; @@ -36,6 +36,7 @@ interface Props > {} const Container = styled.div<{ $nodeWidth: number; $nodeHeight: number }>` + position: relative; width: ${({ $nodeWidth }) => $nodeWidth}px; height: ${({ $nodeHeight }) => $nodeHeight}px; background: transparent; @@ -47,11 +48,38 @@ const BaseNodeWrapper = styled.div<{ $framePadding: number }>` margin: ${({ $framePadding }) => $framePadding}px 0; `; +const LoadMoreWrapper = styled.div<{ $hide?: boolean }>` + position: fixed; + bottom: 0; + left: 50%; + transform: translateX(-50%); + + width: 100%; + height: 100px; + padding-bottom: 12px; + + background: ${({ theme, $hide }) => ($hide ? 'transparent' : `linear-gradient(to top, ${theme.colors.primary}, transparent)`)}; + display: flex; + align-items: flex-end; + justify-content: center; + + pointer-events: none; +`; + +// const LoadMoreButton = styled(Button)` +// background: ${({ theme }) => theme.colors.primary} !important; +// &:hover { +// background: ${({ theme }) => theme.colors.dropdown_bg_2} !important; +// } +// `; + const ScrollNode: React.FC = ({ data, ...rest }) => { const { nodeWidth, nodeHeight, items, onScroll } = data; const { handleNodeClick } = useNodeDataFlowHandlers(); + // const { fetchSources, sourcesNotFinished } = usePaginatedSources(); const containerRef = useRef(null); + const [isBottomOfList, setIsBottomOfList] = useState(false); useEffect(() => { const handleScroll = (e: Event) => { @@ -62,14 +90,16 @@ const ScrollNode: React.FC = ({ data, ...rest }) => { if (!!onScroll) onScroll({ clientHeight, scrollHeight, scrollTop }); - // TODO: Use the following once we have to handle paginated API requests: + // TODO: Use the following if we have to handle paginated API requests using scroll: // const isTop = scrollTop === 0; - // const isBottom = scrollHeight - scrollTop <= clientHeight; + const isBottom = scrollHeight - scrollTop <= clientHeight; // if (isTop) { // console.log('Reached top of scroll-node'); // } else if (isBottom) { // console.log('Reached bottom of scroll-node'); // } + + setIsBottomOfList(isBottom); }; const { current } = containerRef; @@ -93,6 +123,20 @@ const ScrollNode: React.FC = ({ data, ...rest }) => { ))} + + + {/* {sourcesNotFinished && ( + { + e.stopPropagation(); + fetchSources(true); + }} + > + load more + + )} */} + ); }; diff --git a/frontend/webapp/reuseable-components/segment/index.tsx b/frontend/webapp/reuseable-components/segment/index.tsx new file mode 100644 index 000000000..c2b5fba2f --- /dev/null +++ b/frontend/webapp/reuseable-components/segment/index.tsx @@ -0,0 +1,110 @@ +import React, { CSSProperties, useEffect, useId, useRef, useState } from 'react'; +import { SVG } from '@/assets'; +import { Text } from '../text'; +import { FlexRow } from '@/styles'; +import styled from 'styled-components'; + +type SelectedValue = any; + +interface Props { + options: { + icon?: SVG; + label?: string; + value: SelectedValue; + selectedBgColor?: CSSProperties['backgroundColor']; + }[]; + selected: SelectedValue; + setSelected: (value: SelectedValue) => void; +} + +const Container = styled(FlexRow)` + position: relative; + gap: 0; +`; + +const Button = styled.button<{ $isFirstItem: boolean; $isLastItem: boolean }>` + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + padding: 6px 12px; + background-color: transparent; + border-radius: ${({ $isFirstItem, $isLastItem }) => ($isFirstItem ? '32px 0px 0px 32px' : $isLastItem ? '0px 32px 32px 0px' : '0')}; + border: 1px solid ${({ theme }) => theme.colors.border}; + cursor: pointer; + &:hover { + border: 1px solid ${({ theme }) => theme.colors.secondary}; + } +`; + +const Background = styled.div<{ $bgColor?: CSSProperties['backgroundColor']; $width: number; $height: number; $x: number; $y: number; $isFirstItem: boolean; $isLastItem: boolean }>` + position: absolute; + top: ${({ $y }) => $y}px; + left: ${({ $x }) => $x}px; + z-index: -1; + width: ${({ $width }) => $width}px; + height: ${({ $height }) => $height}px; + background-color: ${({ theme, $bgColor }) => $bgColor || theme.colors.white_opacity['008']}; + border-radius: ${({ $isFirstItem, $isLastItem }) => ($isFirstItem ? '32px 0px 0px 32px' : $isLastItem ? '0px 32px 32px 0px' : '0')}; + transition: all 0.3s; +`; + +export const Segment: React.FC = ({ options = [], selected, setSelected }) => { + const selectedIdx = options.findIndex((option) => option.value === selected); + const [bgColor, setBgColor] = useState(options[selectedIdx]?.selectedBgColor || ''); + const [bgSize, setBgSize] = useState({ width: 0, height: 0 }); + const [bgPosition, setBgPosition] = useState({ x: 0, y: 0 }); + const selectedRef = useRef(null); + + useEffect(() => { + if (!!selectedRef.current) { + setBgSize({ + width: selectedRef.current.offsetWidth, + height: selectedRef.current.offsetHeight, + }); + setBgPosition({ + x: selectedRef.current.offsetWidth * selectedIdx, + y: 0, + }); + } + }, [selected, selectedIdx]); + + return ( + + {options.map(({ icon: Icon, label, value, selectedBgColor }, idx) => { + const isSelected = selected === value; + + return ( + + ); + })} + + + + ); +}; diff --git a/frontend/webapp/reuseable-components/toggle-buttons/index.tsx b/frontend/webapp/reuseable-components/toggle-buttons/index.tsx deleted file mode 100644 index 388f30c34..000000000 --- a/frontend/webapp/reuseable-components/toggle-buttons/index.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Tooltip } from '../tooltip'; -import styled from 'styled-components'; -import { CheckCircledIcon, CrossCircledIcon } from '@/assets'; - -interface ToggleProps { - activeText?: string; - inactiveText?: string; - tooltip?: string; - initialValue?: boolean; - onChange?: (value: boolean) => void; - disabled?: boolean; -} - -const Container = styled.div` - width: 100%; - display: flex; - align-items: center; -`; - -const BaseButton = styled.button` - width: 100%; - padding: 12px; - gap: 4px; - display: flex; - align-items: center; - justify-content: center; - border: 1px solid ${({ theme }) => theme.colors.border}; - color: ${({ theme }) => theme.colors.secondary}; - font-family: ${({ theme }) => theme.font_family.secondary}; - font-size: 14px; - text-decoration: underline; - text-transform: uppercase; - cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; - opacity: ${({ disabled }) => (disabled ? 0.6 : 1)}; -`; - -const ActiveButton = styled(BaseButton)` - border-radius: 32px 0 0 32px; - background-color: ${({ theme }) => theme.colors.blank_background}; - &.colored { - background-color: ${({ theme }) => theme.colors.dark_green}; - } - &:hover { - border-color: ${({ theme }) => theme.colors.secondary}; - } - transition: background-color 0.3s; -`; - -const InactiveButton = styled(BaseButton)` - border-radius: 0 32px 32px 0; - background-color: ${({ theme }) => theme.colors.blank_background}; - &.colored { - background-color: ${({ theme }) => theme.colors.darker_red}; - } - &:hover { - border-color: ${({ theme }) => theme.colors.secondary}; - } - transition: background-color 0.3s; -`; - -export const ToggleButtons: React.FC = ({ activeText = 'Active', inactiveText = 'Inactive', tooltip, initialValue = false, onChange, disabled }) => { - const [isActive, setIsActive] = useState(initialValue); - useEffect(() => setIsActive(initialValue), [initialValue]); - - const handleToggle = (forcedBool?: boolean) => { - if (disabled) return; - - let newValue = initialValue; - - setIsActive((prev) => { - newValue = typeof forcedBool === 'boolean' ? forcedBool : !prev; - return newValue; - }); - - if (onChange) onChange(newValue); - }; - - return ( - - - handleToggle(true)} disabled={disabled}> - - {activeText} - - handleToggle(false)} disabled={disabled}> - - {inactiveText} - - - - ); -}; diff --git a/frontend/webapp/store/index.ts b/frontend/webapp/store/index.ts index 19c00c77b..a5d8b9e7c 100644 --- a/frontend/webapp/store/index.ts +++ b/frontend/webapp/store/index.ts @@ -1,6 +1,8 @@ export * from './useAppStore'; export * from './useConnectionStore'; export * from './useDrawerStore'; +export * from './useFilterStore'; export * from './useModalStore'; export * from './useNotificationStore'; +export * from './usePaginatedStore'; export * from './usePendingStore'; diff --git a/frontend/webapp/store/useAppStore.ts b/frontend/webapp/store/useAppStore.ts index e3013f0bf..cd9d49103 100644 --- a/frontend/webapp/store/useAppStore.ts +++ b/frontend/webapp/store/useAppStore.ts @@ -2,9 +2,14 @@ import { create } from 'zustand'; import type { ConfiguredDestination, DestinationInput, K8sActualSource } from '@/types'; export interface IAppState { + // in onboarding this is used to keep state of sources that are available for selection in a namespace, in-case user goes back a page (from destinations to sources) availableSources: { [key: string]: K8sActualSource[] }; + // in onboarding this is used to keep state of added sources, until end of onboarding + // in overview this is used to globally select sources for further actions (like uninstrument using multi-source-control component) configuredSources: { [key: string]: K8sActualSource[] }; + // in onboarding this is used to keep state of namespaces with future-apps selected, until end of onboarding configuredFutureApps: { [key: string]: boolean }; + // in onbaording this is used to keep state of added destinations, until end of onboarding configuredDestinations: { stored: ConfiguredDestination; form: DestinationInput }[]; } @@ -20,7 +25,7 @@ interface IAppStateSetters { resetState: () => void; } -const useAppStore = create((set) => ({ +export const useAppStore = create((set) => ({ availableSources: {}, configuredSources: {}, configuredFutureApps: {}, @@ -36,5 +41,3 @@ const useAppStore = create((set) => ({ resetState: () => set(() => ({ availableSources: {}, configuredSources: {}, configuredFutureApps: {}, configuredDestinations: [] })), })); - -export { useAppStore }; diff --git a/frontend/webapp/store/usePaginatedStore.ts b/frontend/webapp/store/usePaginatedStore.ts new file mode 100644 index 000000000..401c530d0 --- /dev/null +++ b/frontend/webapp/store/usePaginatedStore.ts @@ -0,0 +1,60 @@ +import { create } from 'zustand'; +import type { K8sActualSource, WorkloadId } from '@/types'; + +interface IPaginatedState { + sources: K8sActualSource[]; + sourcesNotFinished: boolean; + sourcesFetching: boolean; +} + +interface IPaginatedStateSetters { + setSources: (payload: IPaginatedState['sources']) => void; + addSources: (payload: IPaginatedState['sources']) => void; + updateSource: (id: WorkloadId, payload: Partial) => void; + removeSource: (id: WorkloadId) => void; + setSourcesNotFinished: (bool: boolean) => void; + setSourcesFetching: (bool: boolean) => void; +} + +export const usePaginatedStore = create((set) => ({ + sources: [], + setSources: (payload) => set({ sources: payload }), + addSources: (payload) => + set((state) => { + const prev = [...state.sources]; + const noDuplicates = [ + ...payload.filter((newItem) => !state.sources.find((existingItem) => existingItem.namespace === newItem.namespace && existingItem.name === newItem.name && existingItem.kind === newItem.kind)), + ]; + + prev.push(...noDuplicates); + return { sources: prev }; + }), + updateSource: (id, payload) => + set((state) => { + const prev = [...state.sources]; + const foundIdx = prev.findIndex(({ namespace, name, kind }) => namespace === id.namespace && name === id.name && kind === id.kind); + + if (foundIdx !== -1) { + prev[foundIdx] = { ...prev[foundIdx], ...payload }; + } + + return { sources: prev }; + }), + removeSource: (id) => + set((state) => { + const prev = [...state.sources]; + const foundIdx = prev.findIndex(({ namespace, name, kind }) => namespace === id.namespace && name === id.name && kind === id.kind); + + if (foundIdx !== -1) { + prev.splice(foundIdx, 1); + } + + return { sources: prev }; + }), + + sourcesNotFinished: false, + setSourcesNotFinished: (bool) => set({ sourcesNotFinished: bool }), + + sourcesFetching: false, + setSourcesFetching: (bool) => set({ sourcesFetching: bool }), +})); diff --git a/frontend/webapp/tsconfig.json b/frontend/webapp/tsconfig.json index 3c1d88153..fe3dbd37c 100644 --- a/frontend/webapp/tsconfig.json +++ b/frontend/webapp/tsconfig.json @@ -28,7 +28,8 @@ "name": "next" } ], - "strictNullChecks": true + "strictNullChecks": true, + "noImplicitAny": true }, "include": [ "**/*.ts", diff --git a/frontend/webapp/types/compute-platform.ts b/frontend/webapp/types/compute-platform.ts index 327fb31f7..93e43c56f 100644 --- a/frontend/webapp/types/compute-platform.ts +++ b/frontend/webapp/types/compute-platform.ts @@ -9,13 +9,18 @@ export type K8sActualNamespace = { k8sActualSources?: K8sActualSource[]; }; +interface PaginatedSources { + nextPage: string; + items: K8sActualSource[]; +} + interface ComputePlatformData { id: string; name: string; computePlatformType: string; k8sActualNamespaces: K8sActualNamespace[]; k8sActualNamespace: K8sActualNamespace; - k8sActualSources: K8sActualSource[]; + sources?: PaginatedSources; destinations: ActualDestination[]; actions: ActionData[]; instrumentationRules: InstrumentationRuleSpec[]; @@ -28,6 +33,7 @@ export type ComputePlatform = { interface ComputePlatformDataMapped extends ComputePlatformData { actions: ActionDataParsed[]; instrumentationRules: InstrumentationRuleSpecMapped[]; + sources: undefined; } export type ComputePlatformMapped = { diff --git a/frontend/webapp/types/destinations.ts b/frontend/webapp/types/destinations.ts index ea8d84895..753e6fd96 100644 --- a/frontend/webapp/types/destinations.ts +++ b/frontend/webapp/types/destinations.ts @@ -4,7 +4,7 @@ interface ObservabilitySignalSupport { supported: boolean; } -interface SupportedSignals { +export interface SupportedSignals { logs: ObservabilitySignalSupport; metrics: ObservabilitySignalSupport; traces: ObservabilitySignalSupport; diff --git a/frontend/webapp/utils/functions/entities/index.ts b/frontend/webapp/utils/functions/entities/index.ts new file mode 100644 index 000000000..2cfa6bd08 --- /dev/null +++ b/frontend/webapp/utils/functions/entities/index.ts @@ -0,0 +1,24 @@ +import { InstrumentationRuleSpec, K8sActualSource, ActionDataParsed, ActualDestination, WorkloadId } from '@/types'; + +type Item = InstrumentationRuleSpec | K8sActualSource | ActionDataParsed | ActualDestination; + +export const getEntityItemId = (item: Item): string | WorkloadId | undefined => { + if ('ruleId' in item) { + // InstrumentationRuleSpec + return item.ruleId; + } else if ('id' in item) { + // ActualDestination or ActionDataParsed + return item.id; + } else if ('kind' in item && 'name' in item && 'namespace' in item) { + // K8sActualSource + return { + kind: item.kind, + name: item.name, + namespace: item.namespace, + }; + } + + // If the type doesn't match any of the known ones, return undefined + console.error('Unhandled item type', item); + return undefined; +}; diff --git a/frontend/webapp/utils/functions/formatters/extract-monitors/index.ts b/frontend/webapp/utils/functions/formatters/extract-monitors/index.ts index e552ecfc3..740fa90bc 100644 --- a/frontend/webapp/utils/functions/formatters/extract-monitors/index.ts +++ b/frontend/webapp/utils/functions/formatters/extract-monitors/index.ts @@ -1,7 +1,7 @@ import type { ExportedSignals } from '@/types'; export const extractMonitors = (exportedSignals: ExportedSignals) => { - const filtered = Object.keys(exportedSignals).filter((signal) => exportedSignals[signal] === true); + const filtered = Object.keys(exportedSignals).filter((signal) => exportedSignals[signal as keyof ExportedSignals] === true); return filtered; }; diff --git a/frontend/webapp/utils/functions/formatters/get-id-from-sse-target/index.ts b/frontend/webapp/utils/functions/formatters/get-id-from-sse-target/index.ts index b43acf298..38e596d92 100644 --- a/frontend/webapp/utils/functions/formatters/get-id-from-sse-target/index.ts +++ b/frontend/webapp/utils/functions/formatters/get-id-from-sse-target/index.ts @@ -11,7 +11,7 @@ export const getIdFromSseTarget = (target: string, type: OVERVIEW_ENTITY_TYPES) target.split('&').forEach((str) => { const [key, value] = str.split('='); - id[key] = value; + id[key as keyof WorkloadId] = value; }); return id; diff --git a/frontend/webapp/utils/functions/icons/get-action-icon/index.ts b/frontend/webapp/utils/functions/icons/get-action-icon/index.ts index d237e2ded..0f23ef46a 100644 --- a/frontend/webapp/utils/functions/icons/get-action-icon/index.ts +++ b/frontend/webapp/utils/functions/icons/get-action-icon/index.ts @@ -19,5 +19,5 @@ export const getActionIcon = (type: ActionsType | 'sampler' | 'attributes') => { [ActionsType.LATENCY_SAMPLER]: SamplerIcon, }; - return LOGOS[type]; + return LOGOS[type as ActionsType]; }; diff --git a/frontend/webapp/utils/functions/icons/get-monitor-icon/index.ts b/frontend/webapp/utils/functions/icons/get-monitor-icon/index.ts index 5a9962003..40566e453 100644 --- a/frontend/webapp/utils/functions/icons/get-monitor-icon/index.ts +++ b/frontend/webapp/utils/functions/icons/get-monitor-icon/index.ts @@ -8,5 +8,5 @@ export const getMonitorIcon = (type: string) => { ['TRACES']: TracesIcon, }; - return LOGOS[type.toUpperCase()]; + return LOGOS[type.toUpperCase() as SignalUppercase]; }; diff --git a/frontend/webapp/utils/functions/index.ts b/frontend/webapp/utils/functions/index.ts index dcefed233..28c018186 100644 --- a/frontend/webapp/utils/functions/index.ts +++ b/frontend/webapp/utils/functions/index.ts @@ -2,3 +2,4 @@ export * from './formatters'; export * from './icons'; export * from './resolvers'; export * from './strings'; +export * from './entities'; diff --git a/frontend/webapp/utils/functions/resolvers/index.ts b/frontend/webapp/utils/functions/resolvers/index.ts index 6c3ae5d3c..39f8fff97 100644 --- a/frontend/webapp/utils/functions/resolvers/index.ts +++ b/frontend/webapp/utils/functions/resolvers/index.ts @@ -1,2 +1,3 @@ export * from './compare-condition'; export * from './get-value-for-range'; +export * from './is-emtpy'; diff --git a/frontend/webapp/utils/functions/resolvers/is-emtpy/index.ts b/frontend/webapp/utils/functions/resolvers/is-emtpy/index.ts new file mode 100644 index 000000000..a3f5e51cc --- /dev/null +++ b/frontend/webapp/utils/functions/resolvers/is-emtpy/index.ts @@ -0,0 +1,12 @@ +// Sometimes we need to allow "zero" values, and a simple "!val" check would result in false positives. +// This function is a strict check for empty values, permitting values like "0" and "false". + +export const isEmpty = (val: any) => { + if (Array.isArray(val)) { + return !val.length; + } else if (typeof val === 'object') { + return !Object.keys(val).length; + } else { + return [undefined, null, ''].includes(val); + } +}; diff --git a/helm/odigos/templates/_helpers.tpl b/helm/odigos/templates/_helpers.tpl deleted file mode 100644 index 44f59284c..000000000 --- a/helm/odigos/templates/_helpers.tpl +++ /dev/null @@ -1,17 +0,0 @@ -{{- define "utils.certManagerApiVersion" -}} -{{- if not (eq .Values.instrumentor.skipWebhookIssuerCreation true) -}} - {{- if .Capabilities.APIVersions.Has "cert-manager.io/v1" -}} -cert-manager.io/v1 - {{- else if .Capabilities.APIVersions.Has "cert-manager.io/v1beta1" -}} -cert-manager.io/v1beta1 - {{- else if .Capabilities.APIVersions.Has "cert-manager.io/v1alpha2" -}} -cert-manager.io/v1alpha2 - {{- else if .Capabilities.APIVersions.Has "certmanager.k8s.io/v1alpha1" -}} -certmanager.k8s.io/v1alpha1 - {{- else -}} -{{- print "" -}} - {{- end -}} -{{- else -}} -{{- print "" -}} -{{- end -}} -{{- end -}} \ No newline at end of file diff --git a/helm/odigos/templates/instrumentor/certificates.yaml b/helm/odigos/templates/instrumentor/certificates.yaml deleted file mode 100644 index ab55f6c51..000000000 --- a/helm/odigos/templates/instrumentor/certificates.yaml +++ /dev/null @@ -1,36 +0,0 @@ -{{- $certManagerApiVersion := include "utils.certManagerApiVersion" . -}} -{{- if $certManagerApiVersion }} -apiVersion: {{ $certManagerApiVersion }} -kind: Issuer -metadata: - name: selfsigned-issuer - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: issuer - app.kubernetes.io/instance: selfsigned-issuer - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: instrumentor - app.kubernetes.io/part-of: odigos -spec: - selfSigned: {} ---- -apiVersion: {{ $certManagerApiVersion }} -kind: Certificate -metadata: - name: serving-cert - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: instrumentor-cert - app.kubernetes.io/instance: instrumentor-cert - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: instrumentor - app.kubernetes.io/part-of: odigos -spec: - dnsNames: - - "odigos-instrumentor.{{ .Release.Namespace }}.svc" - - "odigos-instrumentor.{{ .Release.Namespace }}.svc.cluster.local" - issuerRef: - kind: Issuer - name: selfsigned-issuer - secretName: instrumentor-webhook-cert -{{- end }} \ No newline at end of file diff --git a/helm/odigos/templates/instrumentor/deployment.yaml b/helm/odigos/templates/instrumentor/deployment.yaml index db0422a1e..cc7b37c79 100644 --- a/helm/odigos/templates/instrumentor/deployment.yaml +++ b/helm/odigos/templates/instrumentor/deployment.yaml @@ -72,7 +72,7 @@ spec: volumes: - name: webhook-cert secret: - secretName: instrumentor-webhook-cert + secretName: webhook-cert defaultMode: 420 terminationGracePeriodSeconds: 10 {{- if .Values.imagePullSecrets }} diff --git a/helm/odigos/templates/instrumentor/webhook.yaml b/helm/odigos/templates/instrumentor/webhook.yaml index 41ed17f26..7361f21b4 100644 --- a/helm/odigos/templates/instrumentor/webhook.yaml +++ b/helm/odigos/templates/instrumentor/webhook.yaml @@ -1,4 +1,3 @@ -{{- $certManagerApiVersion := include "utils.certManagerApiVersion" . -}} {{- $altNames := list (printf "odigos-instrumentor.%s.svc" .Release.Namespace) (printf "odigos-instrumentor.%s.svc.cluster.local" .Release.Namespace) -}} {{- $ca := genCA "serving-cert" 365 -}} {{- $cert := genSignedCert "serving-cert" nil $altNames 365 $ca -}} @@ -12,16 +11,10 @@ metadata: app.kubernetes.io/component: webhook app.kubernetes.io/created-by: instrumentor app.kubernetes.io/part-of: odigos -{{- if $certManagerApiVersion }} - annotations: - cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/serving-cert -{{- end }} webhooks: - name: pod-mutating-webhook.odigos.io clientConfig: -{{- if not $certManagerApiVersion }} caBundle: {{ $ca.Cert | b64enc }} -{{- end }} service: name: odigos-instrumentor namespace: {{ .Release.Namespace }} @@ -44,12 +37,11 @@ webhooks: timeoutSeconds: 10 admissionReviewVersions: ["v1"] --- -{{- if not $certManagerApiVersion }} apiVersion: v1 kind: Secret type: kubernetes.io/tls metadata: - name: instrumentor-webhook-cert + name: webhook-cert namespace: {{ .Release.Namespace }} labels: app.kubernetes.io/name: instrumentor-cert @@ -62,5 +54,4 @@ metadata: "helm.sh/hook-delete-policy": "before-hook-creation" data: tls.crt: {{ $cert.Cert | b64enc }} - tls.key: {{ $cert.Key | b64enc }} -{{- end }} \ No newline at end of file + tls.key: {{ $cert.Key | b64enc }} \ No newline at end of file diff --git a/helm/odigos/values.yaml b/helm/odigos/values.yaml index bbb651c42..6c4d7322b 100644 --- a/helm/odigos/values.yaml +++ b/helm/odigos/values.yaml @@ -154,10 +154,10 @@ odiglet: nodeSelector: kubernetes.io/os: linux tolerations: - - effect: NoSchedule - key: node.kubernetes.io/os - operator: Equal - value: windows + ## This toleration with 'Exists' operator and no key/effect specified + ## will match ALL taints, allowing pods to be scheduled on any node + ## regardless of its taints (including master/control-plane nodes) + - operator: Exists affinity: {} cli: diff --git a/instrumentation/manager.go b/instrumentation/manager.go index f1444ea20..003cf2821 100644 --- a/instrumentation/manager.go +++ b/instrumentation/manager.go @@ -139,26 +139,33 @@ func NewManager[processDetails ProcessDetails, configGroup ConfigGroup](options } func (m *manager[ProcessDetails, ConfigGroup]) runEventLoop(ctx context.Context) { + defer func () { + for pid, details := range m.detailsByPid { + if details.inst == nil { + continue + } + err := details.inst.Close(ctx) + if err != nil { + m.logger.Error(err, "failed to close instrumentation", "pid", pid) + } + // probably shouldn't remove instrumentation instance here + // as this flow is happening when Odiglet is shutting down + } + m.detailsByPid = nil + m.detailsByWorkload = nil + } () + // main event loop for handling instrumentations for { select { case <-ctx.Done(): - m.logger.Info("stopping eBPF instrumentation manager") - for pid, details := range m.detailsByPid { - if details.inst == nil { - continue - } - err := details.inst.Close(ctx) - if err != nil { - m.logger.Error(err, "failed to close instrumentation", "pid", pid) - } - // probably shouldn't remove instrumentation instance here - // as this flow is happening when Odiglet is shutting down - } - m.detailsByPid = nil - m.detailsByWorkload = nil + m.logger.Info("context canceled, stopping eBPF instrumentation manager") return - case e := <-m.procEvents: + case e, ok := <-m.procEvents: + if !ok { + m.logger.Info("process events channel closed, stopping eBPF instrumentation manager") + return + } switch e.EventType { case detector.ProcessExecEvent: m.logger.V(1).Info("detected new process", "pid", e.PID, "cmd", e.ExecDetails.CmdLine) diff --git a/k8sutils/pkg/consts/consts.go b/k8sutils/pkg/consts/consts.go index 7b17a3fcf..bf094f166 100644 --- a/k8sutils/pkg/consts/consts.go +++ b/k8sutils/pkg/consts/consts.go @@ -42,6 +42,10 @@ const ( OdigosNodeCollectorConfigMapKey = "conf" // this key is different than the cluster collector value. not sure why ) +const ( + OdigosProSecretName = "odigos-pro" +) + const ( OdigosEnvVarNamespace = "ODIGOS_WORKLOAD_NAMESPACE" OdigosEnvVarContainerName = "ODIGOS_CONTAINER_NAME" @@ -70,4 +74,7 @@ const ( WorkloadNameLabel = "odigos.io/workload-name" WorkloadNamespaceLabel = "odigos.io/workload-namespace" WorkloadKindLabel = "odigos.io/workload-kind" + + OdigosCloudApiKeySecretKey = "odigos-cloud-api-key" + OdigosOnpremTokenSecretKey = "odigos-onprem-token" ) diff --git a/k8sutils/pkg/utils/tiers.go b/k8sutils/pkg/utils/tiers.go index 1b5bc10e2..6848de9fe 100644 --- a/k8sutils/pkg/utils/tiers.go +++ b/k8sutils/pkg/utils/tiers.go @@ -4,6 +4,7 @@ import ( "context" "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -13,7 +14,6 @@ import ( const ( odigosCloudApiKeySecretKey = "odigos-cloud-api-key" odigosOnpremTokenSecretKey = "odigos-onprem-token" - odigosProSecretName = "odigos-pro" ) func GetCurrentOdigosTier(ctx context.Context, namespaces string, client *kubernetes.Clientset) (common.OdigosTier, error) { @@ -38,7 +38,7 @@ func GetCurrentOdigosTier(ctx context.Context, namespaces string, client *kubern func getCurrentOdigosProSecret(ctx context.Context, namespace string, client *kubernetes.Clientset) (*corev1.Secret, error) { - secret, err := client.CoreV1().Secrets(namespace).Get(ctx, odigosProSecretName, metav1.GetOptions{}) + secret, err := client.CoreV1().Secrets(namespace).Get(ctx, consts.OdigosProSecretName, metav1.GetOptions{}) if apierrors.IsNotFound(err) { return nil, nil } diff --git a/odiglet/odiglet.go b/odiglet/odiglet.go index d8793b0ee..40d588d78 100644 --- a/odiglet/odiglet.go +++ b/odiglet/odiglet.go @@ -84,6 +84,7 @@ func New(deviceInjectionCallbacks instrumentation.OtelSdksLsf, factories map[com // Run starts the Odiglet components and blocks until the context is cancelled, or a critical error occurs. func (o *Odiglet) Run(ctx context.Context) { + ctx, cancel := context.WithCancel(ctx) g, groupCtx := errgroup.WithContext(ctx) if err := o.criClient.Connect(ctx); err != nil { @@ -108,11 +109,16 @@ func (o *Odiglet) Run(ctx context.Context) { // Start device manager // the device manager library doesn't support passing a context, // however, internally it uses a context to cancel the device manager once SIGTERM or SIGINT is received. - g.Go(func() error { + // We run it outside of the error group to avoid blocking on Wait() in case of a fatal error. + go func() { err := runDeviceManager(o.clientset, o.deviceInjectionCallbacks) - log.Logger.V(0).Info("Device manager exited") - return err - }) + if err != nil { + log.Logger.Error(err, "Device manager exited with error") + cancel() + } else { + log.Logger.V(0).Info("Device manager exited") + } + } () g.Go(func() error { err := o.ebpfManager.Run(groupCtx) From 4969d6753664148a93d31997f180e23cfa6592d9 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Wed, 8 Jan 2025 13:41:42 -0500 Subject: [PATCH 204/259] Add Namespace Source instrumentation (#2105) This adds instrumentation+uninstrumentation for Namespace-wide resources with Source objects. A namespace Source looks like so: ```yaml apiVersion: odigos.io/v1alpha1 kind: Source metadata: name: default namespace: default labels: odigos.io/e2e: source spec: workload: name: default namespace: default kind: Namespace ``` With `name` and `namespace` matching the name of the Namespace, and `kind` set to `Namespace`. These 3 fields are required because we're have the entire [PodWorkload field marked required](https://github.com/odigos-io/odigos/blob/ccdcc4920be151c462af667d1b46e90bfce1e3c1/api/odigos/v1alpha1/source_types.go#L48-L49), so this can probably be simplified. What this does: * When calling `GetSourceListForWorkload`, check for inherited instrumentation from Namespace. * In startlangdetection, if the Source is for a Namespace, loop through all potential workloads in that Namespace (similar to how we do label based namespace instrumentation today) * Exclude any workloads that are explicitly excluded (by setting `odigos.io/excluded` on a Source for that workload -- TODO add a test for this) * In deleteinstrumentedapplication, do something similar to what we do today by looping through the workloads again, re-using most of the check functions. * TODO some of these functions still rely on the label, like `isInheritingInstrumentationFromNs` in `syncGenericWorkloadListToNs` * Add e2e for Namespace instrumentation and test steps for uninstrumentation --- api/odigos/v1alpha1/source_types.go | 72 +++- common/consts/consts.go | 1 + .../deleteinstrumentationconfig/common.go | 30 +- .../instrumentationconfig_controller.go | 4 +- .../deleteinstrumentationconfig/manager.go | 3 +- .../namespace_controller.go | 29 +- .../source_controller.go | 135 ++++++- .../instrumentationdevice/pods_webhook.go | 8 + .../namespace_controller.go | 9 +- .../startlangdetection/source_controller.go | 154 ++++++-- .../workload_controllers.go | 12 +- .../utils/predicates/instrumentation_rule.go | 1 - k8sutils/pkg/consts/consts.go | 10 +- k8sutils/pkg/workload/workloadkinds.go | 13 + tests/common/traceql_runner.sh | 31 +- tests/e2e/source/01-sources.yaml | 10 + tests/e2e/source/01-workloads.yaml | 44 +++ tests/e2e/source/02-source-ns.yaml | 12 + tests/e2e/source/02-workloads.yaml | 44 +++ tests/e2e/source/03-workloads.yaml | 44 +++ tests/e2e/source/04-workloads.yaml | 44 +++ .../source/05-assert-runtime-detected.yaml | 16 + tests/e2e/source/05-source.yaml | 12 + tests/e2e/source/05-workloads.yaml | 44 +++ tests/e2e/source/06-workloads.yaml | 44 +++ tests/e2e/source/07-workloads.yaml | 44 +++ .../source/08-assert-runtime-detected.yaml | 69 ++++ tests/e2e/source/08-source.yaml | 13 + tests/e2e/source/08-workloads-2.yaml | 44 +++ tests/e2e/source/08-workloads.yaml | 44 +++ .../source/09-assert-runtime-detected.yaml | 52 +++ tests/e2e/source/09-source.yaml | 13 + tests/e2e/source/09-workloads.yaml | 44 +++ tests/e2e/source/README.md | 56 +++ tests/e2e/source/chainsaw-test.yaml | 328 +++++++++++++++++- .../tracesql/context-propagation-2.yaml | 13 + .../source/tracesql/context-propagation.yaml | 2 +- .../source/tracesql/span-attributes-2.yaml | 18 + .../e2e/source/tracesql/span-attributes.yaml | 2 +- .../e2e/source/tracesql/wait-for-trace-2.yaml | 11 + tests/e2e/source/tracesql/wait-for-trace.yaml | 2 +- .../02-update-workload-manifests.yaml | 30 +- .../workload-lifecycle/02-wait-for-trace.yaml | 29 +- 43 files changed, 1513 insertions(+), 127 deletions(-) create mode 100644 tests/e2e/source/01-workloads.yaml create mode 100644 tests/e2e/source/02-source-ns.yaml create mode 100644 tests/e2e/source/02-workloads.yaml create mode 100644 tests/e2e/source/03-workloads.yaml create mode 100644 tests/e2e/source/04-workloads.yaml create mode 100644 tests/e2e/source/05-assert-runtime-detected.yaml create mode 100644 tests/e2e/source/05-source.yaml create mode 100644 tests/e2e/source/05-workloads.yaml create mode 100644 tests/e2e/source/06-workloads.yaml create mode 100644 tests/e2e/source/07-workloads.yaml create mode 100644 tests/e2e/source/08-assert-runtime-detected.yaml create mode 100644 tests/e2e/source/08-source.yaml create mode 100644 tests/e2e/source/08-workloads-2.yaml create mode 100644 tests/e2e/source/08-workloads.yaml create mode 100644 tests/e2e/source/09-assert-runtime-detected.yaml create mode 100644 tests/e2e/source/09-source.yaml create mode 100644 tests/e2e/source/09-workloads.yaml create mode 100644 tests/e2e/source/README.md create mode 100644 tests/e2e/source/tracesql/context-propagation-2.yaml create mode 100644 tests/e2e/source/tracesql/span-attributes-2.yaml create mode 100644 tests/e2e/source/tracesql/wait-for-trace-2.yaml diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index ae4ab1799..1d3bfe38b 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -18,15 +18,19 @@ package v1alpha1 import ( "context" + "errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" + commonconsts "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) +var ErrorTooManySources = errors.New("too many Sources found for workload") + // Source configures an application for auto-instrumentation. // +genclient // +kubebuilder:object:root=true @@ -67,18 +71,66 @@ type SourceList struct { Items []Source `json:"items"` } -// GetSourceListForWorkload returns a SourceList of all Sources that have matching -// workload name, namespace, and kind labels for an object. In theory, this should only -// ever return a list with 0 or 1 items, but due diligence should handle unexpected cases. -func GetSourceListForWorkload(ctx context.Context, kubeClient client.Client, obj client.Object) (SourceList, error) { - sourceList := SourceList{} - selector := labels.SelectorFromSet(labels.Set{ - consts.WorkloadNameLabel: obj.GetName(), +// +kubebuilder:object:generate=false + +type WorkloadSources struct { + Workload *Source + Namespace *Source +} + +// GetWorkloadSources returns a WorkloadSources listing the Workload and Namespace Source +// that currently apply to the given object. In theory, this should only ever return at most +// 1 Namespace and/or 1 Workload Source for an object. If more are found, an error is returned. +func GetWorkloadSources(ctx context.Context, kubeClient client.Client, obj client.Object) (*WorkloadSources, error) { + var err error + workloadSources := &WorkloadSources{} + + if obj.GetObjectKind().GroupVersionKind().Kind != "Namespace" { + sourceList := SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + consts.WorkloadNameLabel: obj.GetName(), + consts.WorkloadNamespaceLabel: obj.GetNamespace(), + consts.WorkloadKindLabel: obj.GetObjectKind().GroupVersionKind().Kind, + }) + err := kubeClient.List(ctx, &sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return nil, err + } + if len(sourceList.Items) > 1 { + return nil, ErrorTooManySources + } + if len(sourceList.Items) == 1 { + workloadSources.Workload = &sourceList.Items[0] + } + } + + namespaceSourceList := SourceList{} + namespaceSelector := labels.SelectorFromSet(labels.Set{ + consts.WorkloadNameLabel: obj.GetNamespace(), consts.WorkloadNamespaceLabel: obj.GetNamespace(), - consts.WorkloadKindLabel: obj.GetObjectKind().GroupVersionKind().Kind, + consts.WorkloadKindLabel: "Namespace", }) - err := kubeClient.List(ctx, &sourceList, &client.ListOptions{LabelSelector: selector}) - return sourceList, err + err = kubeClient.List(ctx, &namespaceSourceList, &client.ListOptions{LabelSelector: namespaceSelector}) + if err != nil { + return nil, err + } + if len(namespaceSourceList.Items) > 1 { + return nil, ErrorTooManySources + } + if len(namespaceSourceList.Items) == 1 { + workloadSources.Namespace = &namespaceSourceList.Items[0] + } + + return workloadSources, nil +} + +// IsWorkloadExcludedSource returns true if the Source is used to exclude a workload. +// Otherwise, it returns false. +func IsWorkloadExcludedSource(source *Source) bool { + if val, exists := source.Labels[commonconsts.OdigosWorkloadExcludedLabel]; exists && val == "true" { + return true + } + return false } func init() { diff --git a/common/consts/consts.go b/common/consts/consts.go index 540088447..81d9cf148 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -18,6 +18,7 @@ const ( OdigosNamespaceAnnotation = "odigos.io/workload-namespace" OdigosWorkloadKindAnnotation = "odigos.io/workload-kind" OdigosWorkloadNameAnnotation = "odigos.io/workload-name" + OdigosWorkloadExcludedLabel = "odigos.io/workload-excluded" OdigosReportedNameAnnotation = "odigos.io/reported-name" RolloutTriggerAnnotation = "rollout-trigger" diff --git a/instrumentor/controllers/deleteinstrumentationconfig/common.go b/instrumentor/controllers/deleteinstrumentationconfig/common.go index 5a0770df4..dd0dcedaf 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/common.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/common.go @@ -3,7 +3,6 @@ package deleteinstrumentationconfig import ( "context" - "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -22,14 +21,18 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work } if instEffectiveEnabled { - // Check if a Source object exists for this workload - sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, kubeClient, workloadObject) - if err != nil { - return err - } - if len(sourceList.Items) == 0 { - return nil - } + return nil + } + + // Check if a Source object exists for this workload + sourceList, err := odigosv1.GetWorkloadSources(ctx, kubeClient, workloadObject) + if err != nil { + return err + } + if sourceList.Workload != nil || sourceList.Namespace != nil { + // Note that if a Source is being deleted, but still has the finalizer, it will still show up in this List + // So we can't use this check to trigger un-instrumentation via Source deletion + return nil } if err := deleteWorkloadInstrumentationConfig(ctx, kubeClient, workloadObject); err != nil { @@ -46,15 +49,17 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work } func deleteWorkloadInstrumentationConfig(ctx context.Context, kubeClient client.Client, workloadObject client.Object) error { + logger := log.FromContext(ctx) ns := workloadObject.GetNamespace() name := workloadObject.GetName() kind := workload.WorkloadKindFromClientObject(workloadObject) - instrumentedApplicationName := workload.CalculateWorkloadRuntimeObjectName(name, kind) + instrumentationConfigName := workload.CalculateWorkloadRuntimeObjectName(name, kind) + logger.V(1).Info("deleting instrumentationconfig", "name", instrumentationConfigName, "kind", kind) instConfigErr := kubeClient.Delete(ctx, &odigosv1.InstrumentationConfig{ ObjectMeta: metav1.ObjectMeta{ Namespace: ns, - Name: instrumentedApplicationName, + Name: instrumentationConfigName, }, }) @@ -62,8 +67,7 @@ func deleteWorkloadInstrumentationConfig(ctx context.Context, kubeClient client. return client.IgnoreNotFound(instConfigErr) } - logger := log.FromContext(ctx) - logger.V(1).Info("instrumented application deleted", "namespace", ns, "name", name, "kind", kind) + logger.V(1).Info("instrumentationconfig deleted", "namespace", ns, "name", name, "kind", kind) return nil } diff --git a/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go index ab8ebeaf4..561644100 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go @@ -92,11 +92,11 @@ func (r *InstrumentationConfigReconciler) Reconcile(ctx context.Context, req ctr if !instEffectiveEnabled { // Check if a Source object exists for this workload - sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, r.Client, workloadObject) + sourceList, err := v1alpha1.GetWorkloadSources(ctx, r.Client, workloadObject) if err != nil { return ctrl.Result{}, err } - if len(sourceList.Items) == 0 { + if sourceList.Workload == nil && sourceList.Namespace == nil { logger.Info("Deleting instrumented application for non-enabled workload") err := r.Client.Delete(ctx, &instrumentationConfig) return ctrl.Result{}, client.IgnoreNotFound(err) diff --git a/instrumentor/controllers/deleteinstrumentationconfig/manager.go b/instrumentor/controllers/deleteinstrumentationconfig/manager.go index 851afa213..90f22c8e2 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/manager.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/manager.go @@ -2,7 +2,6 @@ package deleteinstrumentationconfig import ( odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" - k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/predicate" odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" appsv1 "k8s.io/api/apps/v1" @@ -81,7 +80,7 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. ControllerManagedBy(mgr). Named("deleteinstrumentationconfig-source"). - WithEventFilter(&k8sutils.OnlyUpdatesPredicate{}). + WithEventFilter(predicate.Or(&odigospredicate.CreationPredicate{}, &odigospredicate.OnlyUpdatesPredicate{})). For(&odigosv1.Source{}). Complete(&SourceReconciler{ Client: mgr.GetClient(), diff --git a/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go index ef8271473..32398ee60 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go @@ -20,6 +20,7 @@ import ( "context" "errors" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -137,7 +138,6 @@ func (r *NamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } func syncGenericWorkloadListToNs(ctx context.Context, c client.Client, kind workload.WorkloadKind, key client.ObjectKey) error { - // it is very important that we make the changes based on a fresh copy of the workload object // if a list operation pulled in state and is now slowly iterating over it, we might be working with stale data freshWorkloadCopy := workload.ClientObjectFromWorkloadKind(kind) @@ -151,11 +151,15 @@ func syncGenericWorkloadListToNs(ctx context.Context, c client.Client, kind work } } - if !isInheritingInstrumentationFromNs(freshWorkloadCopy) { + var err error + inheriting, err := isInheritingInstrumentationFromNs(ctx, c, freshWorkloadCopy) + if err != nil { + return err + } + if !inheriting { return nil } - var err error err = errors.Join(err, deleteWorkloadInstrumentationConfig(ctx, c, freshWorkloadCopy)) err = errors.Join(err, removeReportedNameAnnotation(ctx, c, freshWorkloadCopy)) return err @@ -165,11 +169,24 @@ func syncGenericWorkloadListToNs(ctx context.Context, c client.Client, kind work // when reconciling the namespace, the usecase is to delete instrumentation for workloads that were only // instrumented due to the label on the namespace. These are workloads with the label missing. // (they inherit the instrumentation from the namespace this way) -func isInheritingInstrumentationFromNs(obj client.Object) bool { +func isInheritingInstrumentationFromNs(ctx context.Context, c client.Client, obj client.Object) (bool, error) { + sourceList, err := v1alpha1.GetWorkloadSources(ctx, c, obj) + if err != nil { + return false, err + } + + if sourceList.Namespace != nil && sourceList.Namespace.DeletionTimestamp.IsZero() { + return true, nil + } + + if sourceList.Workload != nil && sourceList.Workload.DeletionTimestamp.IsZero() { + return false, nil + } + labels := obj.GetLabels() if labels == nil { - return true + return true, nil } _, exists := labels[consts.OdigosInstrumentationLabel] - return !exists + return !exists, nil } diff --git a/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go index 9f0d758b7..d1620ade6 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go @@ -18,11 +18,14 @@ package deleteinstrumentationconfig import ( "context" + "errors" "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/consts" + k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/utils" "github.com/odigos-io/odigos/k8sutils/pkg/workload" + appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -38,7 +41,7 @@ type SourceReconciler struct { func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) - logger.Info("Reconciling Deleted Source object", "name", req.Name, "namespace", req.Namespace) + logger.Info("Reconciling Source object", "name", req.Name, "namespace", req.Namespace) source := &v1alpha1.Source{} err := r.Get(ctx, req.NamespacedName, source) @@ -46,26 +49,130 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, client.IgnoreNotFound(err) } - if !source.DeletionTimestamp.IsZero() { - logger.Info("Reconciling workload for deleted Source object", "name", req.Name, "namespace", req.Namespace) - obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) - err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) - if err != nil { - // TODO: Deleted objects should be filtered in the event filter - return ctrl.Result{}, err - } + // If this is a regular Source that's being deleted, or a workload Exclusion Source + // that's being created, try to uninstrument relevant workloads. + if source.DeletionTimestamp.IsZero() == v1alpha1.IsWorkloadExcludedSource(source) { + logger.Info("Reconciling workload for Source object", "name", req.Name, "namespace", req.Namespace) - if controllerutil.ContainsFinalizer(source, consts.InstrumentedApplicationFinalizer) { - controllerutil.RemoveFinalizer(source, consts.InstrumentedApplicationFinalizer) + if result, err := r.setSourceLabelsIfNecessary(ctx, source); err != nil { + return result, err + } + if v1alpha1.IsWorkloadExcludedSource(source) && !controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { + controllerutil.AddFinalizer(source, consts.StartLangDetectionFinalizer) if err := r.Update(ctx, source); err != nil { + return k8sutils.K8SUpdateErrorHandler(err) + } + } + + if source.Spec.Workload.Kind == "Namespace" { + logger.V(2).Info("Uninstrumenting workloads for Namespace Source", "name", req.Name, "namespace", req.Namespace) + + for _, kind := range []workload.WorkloadKind{ + workload.WorkloadKindDaemonSet, + workload.WorkloadKindDeployment, + workload.WorkloadKindStatefulSet, + } { + result, err := r.listAndSyncWorkloadList(ctx, req, kind) + if err != nil { + return result, err + } + } + } else { + // This is a Source for a specific workload, not an entire namespace + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // TODO: Deleted objects should be filtered in the event filter return ctrl.Result{}, err } + + sourceList, err := v1alpha1.GetWorkloadSources(ctx, r.Client, obj) + if err != nil { + return ctrl.Result{}, err + } + if sourceList.Namespace == nil || + (sourceList.Namespace != nil && !sourceList.Namespace.DeletionTimestamp.IsZero()) || + (sourceList.Workload != nil && sourceList.Workload.DeletionTimestamp.IsZero() && v1alpha1.IsWorkloadExcludedSource(source)) { + // if this workload doesn't have a live Namespace instrumentation, or it has a live exclusion source, uninstrument it + err = errors.Join(err, deleteWorkloadInstrumentationConfig(ctx, r.Client, obj)) + err = errors.Join(err, removeReportedNameAnnotation(ctx, r.Client, obj)) + if err != nil { + return ctrl.Result{}, err + } + } } - err = reconcileWorkloadObject(ctx, r.Client, obj) - if err != nil { - return ctrl.Result{}, err + if !v1alpha1.IsWorkloadExcludedSource(source) && controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { + controllerutil.RemoveFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) + if err := r.Update(ctx, source); err != nil { + return k8sutils.K8SUpdateErrorHandler(err) + } } } return ctrl.Result{}, nil } + +// TODO: Move to mutating webhook +func (r *SourceReconciler) setSourceLabelsIfNecessary(ctx context.Context, source *v1alpha1.Source) (ctrl.Result, error) { + if source.Labels == nil { + source.Labels = make(map[string]string) + } + + if source.Labels[consts.WorkloadNameLabel] != source.Spec.Workload.Name || + source.Labels[consts.WorkloadNamespaceLabel] != source.Spec.Workload.Namespace || + source.Labels[consts.WorkloadKindLabel] != string(source.Spec.Workload.Kind) { + + source.Labels[consts.WorkloadNameLabel] = source.Spec.Workload.Name + source.Labels[consts.WorkloadNamespaceLabel] = source.Spec.Workload.Namespace + source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) + + if err := r.Update(ctx, source); err != nil { + return k8sutils.K8SUpdateErrorHandler(err) + } + } + return ctrl.Result{}, nil +} + +func (r *SourceReconciler) listAndSyncWorkloadList(ctx context.Context, + req ctrl.Request, + kind workload.WorkloadKind) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.V(2).Info("Uninstrumenting workloads for Namespace Source", "name", req.Name, "namespace", req.Namespace, "kind", kind) + + workloads := workload.ClientListObjectFromWorkloadKind(kind) + err := r.Client.List(ctx, workloads, client.InNamespace(req.Name)) + if err != nil { + return ctrl.Result{}, err + } + + switch obj := workloads.(type) { + case *appsv1.DeploymentList: + for _, dep := range obj.Items { + err = r.syncWorkloadList(ctx, kind, client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}) + if err != nil { + return ctrl.Result{}, err + } + } + case *appsv1.DaemonSetList: + for _, dep := range obj.Items { + err = r.syncWorkloadList(ctx, kind, client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}) + if err != nil { + return ctrl.Result{}, err + } + } + case *appsv1.StatefulSetList: + for _, dep := range obj.Items { + err = r.syncWorkloadList(ctx, kind, client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}) + if err != nil { + return ctrl.Result{}, err + } + } + } + return ctrl.Result{}, err +} + +func (r *SourceReconciler) syncWorkloadList(ctx context.Context, + kind workload.WorkloadKind, + key client.ObjectKey) error { + return syncGenericWorkloadListToNs(ctx, r.Client, kind, key) +} diff --git a/instrumentor/controllers/instrumentationdevice/pods_webhook.go b/instrumentor/controllers/instrumentationdevice/pods_webhook.go index 83e9ad042..8759dbe08 100644 --- a/instrumentor/controllers/instrumentationdevice/pods_webhook.go +++ b/instrumentor/controllers/instrumentationdevice/pods_webhook.go @@ -14,6 +14,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" @@ -62,6 +63,13 @@ func (p *PodsWebhook) getServiceNameForEnv(ctx context.Context, pod *corev1.Pod) return nil, nil } + req, err := admission.RequestFromContext(ctx) + if err != nil { + logger.Error(err, "failed to get admission request from context") + return nil, nil + } + podWorkload.Namespace = req.Namespace + workloadObj, err := workload.GetWorkloadObject(ctx, client.ObjectKey{Namespace: podWorkload.Namespace, Name: podWorkload.Name}, podWorkload.Kind, p.Client) if err != nil { logger.Error(err, "failed to get workload object from cache. cannot check for workload annotation. using workload name as OTEL_SERVICE_NAME") diff --git a/instrumentor/controllers/startlangdetection/namespace_controller.go b/instrumentor/controllers/startlangdetection/namespace_controller.go index 8e36d49b9..6f3e63b45 100644 --- a/instrumentor/controllers/startlangdetection/namespace_controller.go +++ b/instrumentor/controllers/startlangdetection/namespace_controller.go @@ -3,6 +3,7 @@ package startlangdetection import ( "context" + "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" @@ -29,7 +30,13 @@ func (n *NamespacesReconciler) Reconcile(ctx context.Context, request ctrl.Reque } if !k8sutils.IsObjectLabeledForInstrumentation(&ns) { - return ctrl.Result{}, nil + sourceList, err := v1alpha1.GetWorkloadSources(ctx, n.Client, &ns) + if err != nil { + return ctrl.Result{}, err + } + if sourceList.Namespace == nil { + return ctrl.Result{}, nil + } } logger.V(0).Info("Namespace labeled for instrumentation, recalculating runtime details of relevant workloads") diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 46e837d7d..07a03e979 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -3,6 +3,7 @@ package startlangdetection import ( "context" + v1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -30,56 +31,141 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, client.IgnoreNotFound(err) } - obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) - err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) - if err != nil { - // TODO: Deleted objects should be filtered in the event filter - return ctrl.Result{}, err - } - instConfigName := workload.CalculateWorkloadRuntimeObjectName(source.Spec.Workload.Name, source.Spec.Workload.Kind) + // If this is a regular Source that is being created, or an Exclusion Source that is being deleted, + // Attempt to reconcile the workloads for instrumentation. + if source.DeletionTimestamp.IsZero() != v1alpha1.IsWorkloadExcludedSource(source) { + if result, err := r.setSourceLabelsIfNecessary(ctx, source); err != nil { + return result, err + } + if !v1alpha1.IsWorkloadExcludedSource(source) && !controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { + controllerutil.AddFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) + if err := r.Update(ctx, source); err != nil { + return k8sutils.K8SUpdateErrorHandler(err) + } + } - if source.DeletionTimestamp.IsZero() { - if !controllerutil.ContainsFinalizer(source, consts.SourceFinalizer) { - controllerutil.AddFinalizer(source, consts.SourceFinalizer) - // Removed by deleteinstrumentationconfig controller - controllerutil.AddFinalizer(source, consts.InstrumentedApplicationFinalizer) + if source.Spec.Workload.Kind == "Namespace" { + // pre-process existing Sources for specific workloads so we don't have to make a bunch of API calls + // This is used to check if a workload already has an explicit Source, so we don't overwrite its InstrumentationConfig + sourceList := v1alpha1.SourceList{} + err := r.Client.List(ctx, &sourceList, client.InNamespace(source.Spec.Workload.Name)) + if err != nil { + return ctrl.Result{}, err + } + namespaceKindSources := make(map[workload.WorkloadKind]map[string]struct{}) + for _, source := range sourceList.Items { + if _, exists := namespaceKindSources[source.Spec.Workload.Kind]; !exists { + namespaceKindSources[source.Spec.Workload.Kind] = make(map[string]struct{}) + } + // ex: map["Deployment"]["my-app"] = ... + namespaceKindSources[source.Spec.Workload.Kind][source.Spec.Workload.Name] = struct{}{} + } - if source.Labels == nil { - source.Labels = make(map[string]string) + for _, kind := range []workload.WorkloadKind{ + workload.WorkloadKindDaemonSet, + workload.WorkloadKindDeployment, + workload.WorkloadKindStatefulSet, + } { + result, err := r.listAndReconcileWorkloadList(ctx, source, kind, namespaceKindSources) + if err != nil { + return result, err + } + } + } else { + _, err = reconcileWorkload(ctx, + r.Client, + source.Spec.Workload.Kind, + ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: source.Spec.Workload.Namespace, + Name: source.Spec.Workload.Name, + }, + }, + r.Scheme) + if err != nil { + return ctrl.Result{}, err } - source.Labels[consts.WorkloadNameLabel] = source.Spec.Workload.Name - source.Labels[consts.WorkloadNamespaceLabel] = source.Spec.Workload.Namespace - source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) + } + if v1alpha1.IsWorkloadExcludedSource(source) && controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { + controllerutil.RemoveFinalizer(source, consts.StartLangDetectionFinalizer) if err := r.Update(ctx, source); err != nil { return k8sutils.K8SUpdateErrorHandler(err) } + } + } + + return ctrl.Result{}, err +} + +// TODO: Move to mutating webhook +func (r *SourceReconciler) setSourceLabelsIfNecessary(ctx context.Context, source *v1alpha1.Source) (ctrl.Result, error) { + if source.Labels == nil { + source.Labels = make(map[string]string) + } + + if source.Labels[consts.WorkloadNameLabel] != source.Spec.Workload.Name || + source.Labels[consts.WorkloadNamespaceLabel] != source.Spec.Workload.Namespace || + source.Labels[consts.WorkloadKindLabel] != string(source.Spec.Workload.Kind) { + + source.Labels[consts.WorkloadNameLabel] = source.Spec.Workload.Name + source.Labels[consts.WorkloadNamespaceLabel] = source.Spec.Workload.Namespace + source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) - err = requestOdigletsToCalculateRuntimeDetails(ctx, r.Client, instConfigName, req.Namespace, obj, r.Scheme) - return ctrl.Result{}, err + if err := r.Update(ctx, source); err != nil { + return k8sutils.K8SUpdateErrorHandler(err) } - } else { - // Source is being deleted - if controllerutil.ContainsFinalizer(source, consts.SourceFinalizer) { - // Remove the finalizer first, because if the InstrumentationConfig is not found we - // will deadlock on the finalizer never getting removed. - // On the other hand, this could end up deleting a Source with an orphaned InstrumentationConfig. - controllerutil.RemoveFinalizer(source, consts.SourceFinalizer) - if err := r.Update(ctx, source); err != nil { + } + return ctrl.Result{}, nil +} + +func (r *SourceReconciler) listAndReconcileWorkloadList(ctx context.Context, + source *v1alpha1.Source, + kind workload.WorkloadKind, + namespaceKindSources map[workload.WorkloadKind]map[string]struct{}) (ctrl.Result, error) { + + deps := workload.ClientListObjectFromWorkloadKind(kind) + err := r.Client.List(ctx, deps, client.InNamespace(source.Spec.Workload.Name)) + if client.IgnoreNotFound(err) != nil { + return ctrl.Result{}, err + } + + switch obj := deps.(type) { + case *v1.DeploymentList: + for _, dep := range obj.Items { + err = r.reconcileWorkloadList(ctx, ctrl.Request{NamespacedName: client.ObjectKey{Name: dep.Name, Namespace: dep.Namespace}}, kind, namespaceKindSources) + if err != nil { return ctrl.Result{}, err } - - instConfig := &v1alpha1.InstrumentationConfig{} - err = r.Client.Get(ctx, types.NamespacedName{Name: instConfigName, Namespace: req.Namespace}, instConfig) + } + case *v1.DaemonSetList: + for _, dep := range obj.Items { + err = r.reconcileWorkloadList(ctx, ctrl.Request{NamespacedName: client.ObjectKey{Name: dep.Name, Namespace: dep.Namespace}}, kind, namespaceKindSources) if err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) + return ctrl.Result{}, err } - err = r.Client.Delete(ctx, instConfig) + } + case *v1.StatefulSetList: + for _, dep := range obj.Items { + err = r.reconcileWorkloadList(ctx, ctrl.Request{NamespacedName: client.ObjectKey{Name: dep.Name, Namespace: dep.Namespace}}, kind, namespaceKindSources) if err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) + return ctrl.Result{}, err } } } + return ctrl.Result{}, nil +} - return ctrl.Result{}, err +func (r *SourceReconciler) reconcileWorkloadList(ctx context.Context, + req ctrl.Request, + kind workload.WorkloadKind, + namespaceKindSources map[workload.WorkloadKind]map[string]struct{}) error { + logger := log.FromContext(ctx) + if _, exists := namespaceKindSources[kind][req.Name]; !exists { + _, err := reconcileWorkload(ctx, r.Client, kind, req, r.Scheme) + if err != nil { + logger.Error(err, "error requesting runtime details from odiglets", "name", req.Name, "namespace", req.Namespace, "kind", kind) + } + } + return nil } diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index ab186bb15..181ccf3ae 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -9,7 +9,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" "k8s.io/apimachinery/pkg/types" @@ -60,15 +59,20 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor if !instrumented { // Check if a Source object exists for this workload - sourceList, err := v1alpha1.GetSourceListForWorkload(ctx, k8sClient, obj) + sourceList, err := odigosv1.GetWorkloadSources(ctx, k8sClient, obj) if err != nil { return ctrl.Result{}, err } - if len(sourceList.Items) == 0 { + if sourceList.Workload == nil && sourceList.Namespace == nil { return ctrl.Result{}, nil } + // if this is explicitly excluded (and the excluded Source isn't being deleted), skip + if sourceList.Workload != nil { + if odigosv1.IsWorkloadExcludedSource(sourceList.Workload) && sourceList.Workload.DeletionTimestamp.IsZero() { + return ctrl.Result{}, nil + } + } } - err = requestOdigletsToCalculateRuntimeDetails(ctx, k8sClient, instConfigName, req.Namespace, obj, scheme) return ctrl.Result{}, err } diff --git a/instrumentor/controllers/utils/predicates/instrumentation_rule.go b/instrumentor/controllers/utils/predicates/instrumentation_rule.go index b1da0900f..ceec0c0a7 100644 --- a/instrumentor/controllers/utils/predicates/instrumentation_rule.go +++ b/instrumentor/controllers/utils/predicates/instrumentation_rule.go @@ -7,7 +7,6 @@ import ( type OtelSdkInstrumentationRulePredicate struct{} - func (o OtelSdkInstrumentationRulePredicate) Create(e event.CreateEvent) bool { // check if delete rule is for otel sdk instrumentationRule, ok := e.Object.(*odigosv1alpha1.InstrumentationRule) diff --git a/k8sutils/pkg/consts/consts.go b/k8sutils/pkg/consts/consts.go index bf094f166..defcce72f 100644 --- a/k8sutils/pkg/consts/consts.go +++ b/k8sutils/pkg/consts/consts.go @@ -67,9 +67,13 @@ var ( ) const ( - SourceFinalizer = "odigos.io/source-finalizer" - // TODO: Needed until InstrumentedApplication is removed - InstrumentedApplicationFinalizer = "odigos.io/source-instrumentedapplication-finalizer" + // StartLangDetectionFinalizer is used for Workload exclusion Sources. When a Workload exclusion Source + // is deleted, we want to go to the startlangdetection controller. There, we will check if the Workload should + // start inheriting Namespace instrumentation. + StartLangDetectionFinalizer = "odigos.io/source-startlangdetection-finalizer" + // DeleteInstrumentationConfigFinalizer is used for all non-exclusion (normal) Sources. When a normal Source + // is deleted, we want to go to the deleteinstrumentationconfig controller to un-instrument the workload/namespace. + DeleteInstrumentationConfigFinalizer = "odigos.io/source-deleteinstrumentationconfig-finalizer" WorkloadNameLabel = "odigos.io/workload-name" WorkloadNamespaceLabel = "odigos.io/workload-namespace" diff --git a/k8sutils/pkg/workload/workloadkinds.go b/k8sutils/pkg/workload/workloadkinds.go index 199b729b9..c03d9dbf2 100644 --- a/k8sutils/pkg/workload/workloadkinds.go +++ b/k8sutils/pkg/workload/workloadkinds.go @@ -108,3 +108,16 @@ func ClientObjectFromWorkloadKind(kind WorkloadKind) client.Object { return nil } } + +func ClientListObjectFromWorkloadKind(kind WorkloadKind) client.ObjectList { + switch kind { + case WorkloadKindDeployment: + return &v1.DeploymentList{} + case WorkloadKindDaemonSet: + return &v1.DaemonSetList{} + case WorkloadKindStatefulSet: + return &v1.StatefulSetList{} + default: + return nil + } +} diff --git a/tests/common/traceql_runner.sh b/tests/common/traceql_runner.sh index 052bee1a8..af7fb1d2c 100755 --- a/tests/common/traceql_runner.sh +++ b/tests/common/traceql_runner.sh @@ -8,8 +8,9 @@ function verify_yaml_schema() { local file=$1 local query=$(yq e '.query' "$file") local expected_count=$(yq e '.expected.count' "$file") + local minimum_count=$(yq e '.expected.minimum' "$file") - if [ -z "$query" ] || [ "$expected_count" == "null" ] || [ -z "$expected_count" ]; then + if [[ -z "$query" || ( "$expected_count" == "null" && "$minimum_count" == "null" ) || ( -z "$minimum_count" && -z "$expected_count" ) ]]; then echo "Invalid YAML schema in file: $file" exit 1 fi @@ -38,19 +39,33 @@ function process_yaml_file() { query=$(yq '.query' "$file") encoded_query=$(urlencode "$query") expected_count=$(yq e '.expected.count' "$file") + minimum_count=$(yq e '.expected.minimum' "$file") current_epoch=$(date +%s) one_hour=3600 start_epoch=$(($current_epoch - one_hour)) end_epoch=$(($current_epoch + one_hour)) response=$(kubectl get --raw /api/v1/namespaces/$dest_namespace/services/$dest_service:$dest_port/proxy/api/search\?end=$end_epoch\&start=$start_epoch\&q=$encoded_query\&limit=50) num_of_traces=$(echo $response | jq '.traces | length') - # if num_of_traces not equal to expected_count - if [ "$num_of_traces" -ne "$expected_count" ]; then - echo "Test FAILED: expected $expected_count got $num_of_traces" - exit 1 - else - echo "Test PASSED: expected $expected_count got $num_of_traces" - exit 0 + + if [ "$expected_count" != "null" ]; then + # if num_of_traces not equal to expected_count + if [ "$num_of_traces" -ne "$expected_count" ]; then + echo "Test FAILED: expected $expected_count got $num_of_traces" + exit 1 + else + echo "Test PASSED: expected $expected_count got $num_of_traces" + exit 0 + fi + fi + + if [ "$minimum_count" != "null" ]; then + if [ "$num_of_traces" -lt "$minimum_count" ]; then + echo "Test FAILED: expected at least $minimum_count got $num_of_traces" + exit 1 + else + echo "Test PASSED: expected at least $minimum_count got $num_of_traces" + exit 0 + fi fi } diff --git a/tests/e2e/source/01-sources.yaml b/tests/e2e/source/01-sources.yaml index 3b82128ba..657676383 100644 --- a/tests/e2e/source/01-sources.yaml +++ b/tests/e2e/source/01-sources.yaml @@ -3,6 +3,8 @@ kind: Source metadata: name: frontend namespace: default + labels: + odigos.io/e2e: source spec: workload: name: frontend @@ -14,6 +16,8 @@ kind: Source metadata: name: coupon namespace: default + labels: + odigos.io/e2e: source spec: workload: name: coupon @@ -25,6 +29,8 @@ kind: Source metadata: name: inventory namespace: default + labels: + odigos.io/e2e: source spec: workload: name: inventory @@ -36,6 +42,8 @@ kind: Source metadata: name: pricing namespace: default + labels: + odigos.io/e2e: source spec: workload: name: pricing @@ -47,6 +55,8 @@ kind: Source metadata: name: membership namespace: default + labels: + odigos.io/e2e: source spec: workload: name: membership diff --git a/tests/e2e/source/01-workloads.yaml b/tests/e2e/source/01-workloads.yaml new file mode 100644 index 000000000..32aac4a07 --- /dev/null +++ b/tests/e2e/source/01-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "2" + generation: 2 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "2" + generation: 2 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "2" + generation: 2 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "2" + generation: 2 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "2" + generation: 2 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/02-source-ns.yaml b/tests/e2e/source/02-source-ns.yaml new file mode 100644 index 000000000..deb8153f3 --- /dev/null +++ b/tests/e2e/source/02-source-ns.yaml @@ -0,0 +1,12 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: default + namespace: default + labels: + odigos.io/e2e: source +spec: + workload: + name: default + namespace: default + kind: Namespace \ No newline at end of file diff --git a/tests/e2e/source/02-workloads.yaml b/tests/e2e/source/02-workloads.yaml new file mode 100644 index 000000000..7f6d7828b --- /dev/null +++ b/tests/e2e/source/02-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "3" + generation: 3 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "3" + generation: 3 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "3" + generation: 3 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "3" + generation: 3 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "3" + generation: 3 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/03-workloads.yaml b/tests/e2e/source/03-workloads.yaml new file mode 100644 index 000000000..0a63cc552 --- /dev/null +++ b/tests/e2e/source/03-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "4" + generation: 5 # Note new generation after annotating service.name, but not new revision + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "4" + generation: 5 # Note new generation after annotating service.name, but not new revision + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "4" + generation: 5 # Note new generation after annotating service.name, but not new revision + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "4" + generation: 5 # Note new generation after annotating service.name, but not new revision + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "4" + generation: 5 # Note new generation after annotating service.name, but not new revision + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/04-workloads.yaml b/tests/e2e/source/04-workloads.yaml new file mode 100644 index 000000000..1cc4d4598 --- /dev/null +++ b/tests/e2e/source/04-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 # Generation jumps 2 because Odigos removes the reported-name annotation + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/05-assert-runtime-detected.yaml b/tests/e2e/source/05-assert-runtime-detected.yaml new file mode 100644 index 000000000..932715aec --- /dev/null +++ b/tests/e2e/source/05-assert-runtime-detected.yaml @@ -0,0 +1,16 @@ +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationConfig +metadata: + name: deployment-frontend + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: frontend +status: + runtimeDetailsByContainer: + - containerName: frontend + language: java + runtimeVersion: 17.0.11+9 diff --git a/tests/e2e/source/05-source.yaml b/tests/e2e/source/05-source.yaml new file mode 100644 index 000000000..d3382faae --- /dev/null +++ b/tests/e2e/source/05-source.yaml @@ -0,0 +1,12 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: frontend + namespace: default + labels: + odigos.io/e2e: source-single +spec: + workload: + name: frontend + namespace: default + kind: Deployment diff --git a/tests/e2e/source/05-workloads.yaml b/tests/e2e/source/05-workloads.yaml new file mode 100644 index 000000000..18770b336 --- /dev/null +++ b/tests/e2e/source/05-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" # should be only one updated right now + generation: 8 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "5" + generation: 7 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/06-workloads.yaml b/tests/e2e/source/06-workloads.yaml new file mode 100644 index 000000000..594f8e211 --- /dev/null +++ b/tests/e2e/source/06-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/07-workloads.yaml b/tests/e2e/source/07-workloads.yaml new file mode 100644 index 000000000..007b2e53b --- /dev/null +++ b/tests/e2e/source/07-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "7" + generation: 9 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "7" + generation: 9 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "7" + generation: 9 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "7" + generation: 9 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/08-assert-runtime-detected.yaml b/tests/e2e/source/08-assert-runtime-detected.yaml new file mode 100644 index 000000000..47f1ccd72 --- /dev/null +++ b/tests/e2e/source/08-assert-runtime-detected.yaml @@ -0,0 +1,69 @@ +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationConfig +metadata: + name: deployment-frontend + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: frontend +status: + runtimeDetailsByContainer: + - containerName: frontend + language: java + runtimeVersion: 17.0.11+9 +--- +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationConfig +metadata: + name: deployment-inventory + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: inventory +status: + runtimeDetailsByContainer: + - containerName: inventory + envVars: + - name: PYTHONPATH + value: /bar # this env exists in the test image + language: python + runtimeVersion: 3.11.9 +--- +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationConfig +metadata: + name: deployment-membership + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: membership +status: + runtimeDetailsByContainer: + - containerName: membership + language: go + runtimeVersion: 1.21.4 +--- +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationConfig +metadata: + name: deployment-pricing + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: pricing +status: + runtimeDetailsByContainer: + - containerName: pricing + language: dotnet diff --git a/tests/e2e/source/08-source.yaml b/tests/e2e/source/08-source.yaml new file mode 100644 index 000000000..8aa6d43b5 --- /dev/null +++ b/tests/e2e/source/08-source.yaml @@ -0,0 +1,13 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: coupon-excluded + namespace: default + labels: + odigos.io/e2e: source-excluded + odigos.io/workload-excluded: "true" +spec: + workload: + name: coupon + namespace: default + kind: Deployment diff --git a/tests/e2e/source/08-workloads-2.yaml b/tests/e2e/source/08-workloads-2.yaml new file mode 100644 index 000000000..e6548dfc6 --- /dev/null +++ b/tests/e2e/source/08-workloads-2.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/08-workloads.yaml b/tests/e2e/source/08-workloads.yaml new file mode 100644 index 000000000..c66e30df9 --- /dev/null +++ b/tests/e2e/source/08-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "7" + generation: 9 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/09-assert-runtime-detected.yaml b/tests/e2e/source/09-assert-runtime-detected.yaml new file mode 100644 index 000000000..e49061385 --- /dev/null +++ b/tests/e2e/source/09-assert-runtime-detected.yaml @@ -0,0 +1,52 @@ +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationConfig +metadata: + name: deployment-frontend + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: frontend +status: + runtimeDetailsByContainer: + - containerName: frontend + language: java + runtimeVersion: 17.0.11+9 +--- +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationConfig +metadata: + name: deployment-inventory + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: inventory +status: + runtimeDetailsByContainer: + - containerName: inventory + envVars: + - name: PYTHONPATH + value: /bar # this env exists in the test image + language: python + runtimeVersion: 3.11.9 +--- +apiVersion: odigos.io/v1alpha1 +kind: InstrumentationConfig +metadata: + name: deployment-pricing + namespace: default + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: pricing +status: + runtimeDetailsByContainer: + - containerName: pricing + language: dotnet diff --git a/tests/e2e/source/09-source.yaml b/tests/e2e/source/09-source.yaml new file mode 100644 index 000000000..19845f01b --- /dev/null +++ b/tests/e2e/source/09-source.yaml @@ -0,0 +1,13 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: membership-excluded + namespace: default + labels: + odigos.io/e2e: source-excluded + odigos.io/workload-excluded: "true" +spec: + workload: + name: membership + namespace: default + kind: Deployment diff --git a/tests/e2e/source/09-workloads.yaml b/tests/e2e/source/09-workloads.yaml new file mode 100644 index 000000000..60dac05b9 --- /dev/null +++ b/tests/e2e/source/09-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "9" + generation: 11 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/README.md b/tests/e2e/source/README.md new file mode 100644 index 000000000..cbc8e2b14 --- /dev/null +++ b/tests/e2e/source/README.md @@ -0,0 +1,56 @@ +# Source object e2e + +This e2e extensively tests the use of Source objects for instrumentation, uninstrumentation, and exclusion. + +It has the following phases: + +1. **Setup** - Install Odigos, Tempo, and the Demo app. +2. **Workload instrumentation** - Create a Source for each individual workload. Add Tempo as a destination. Verify: + 2.1 InstrumentationConfigs are created for each deployment + 2.2 Tempo is ready to receive traces + 2.3 The Odigos pipeline is ready + 2.4 Each deployment rolls out a new (instrumented) revision + 2.5 Generated traffic results in expected spans + 2.6 Context propagation works across deployments + 2.7 Resource attributes are present + 2.8 Span attributes are present +3. **Workload uninstrumentation** - Delete all Source objects for deployments. Verify: + 3.1 Workloads roll out a new (uninstrumented) revision +4. **Namespace instrumentation** - Update the service name for each deployment. Instrument Namespace. Verify: + 4.1 InstrumentationConfigs are present for each workload in the Namespace. + 4.2 Workloads roll out a new (instrumented) revision. + 4.3 Generated traffic results in expected spans (with new service name) + 4.4 Context propagation works + 4.5 Resource attributes are present + 4.6 Span attributes are present +5. **Namespace uninstrumentation** - Delete Namespace Source object. Verify: + 5.1 Workloads roll out a new (uninstrumented) revision +6. **Namespace+Workload instrumentation** - Instrument a single workload, then instrument the rest of the namespace. Verify: + 6.1 InstrumentationConfig is created for the single workload. + 6.2 Single workload rolls out a new revision. + 6.3 InstrumentationConfigs are then detected for all workloads. + 6.4 Remaining workloads roll out a new revision. + 6.5 Deleting Namespace Source does not delete individual Workload source. +7. **Workload exclusion** - Create an Excluded Source for another workload. Instrument the namespace. Verify: + 7.1 InstrumentationConfigs are created for all workloads except excluded workload + 7.2 All workloads except excluded workload roll out a new revision +8. **Workload inclusion** - Delete an Excluded source in an already-instrumented namespace. Verify: + 8.1 InstrumentationConfigs exist for all workloads in the namespace. + 8.2 Only the previously-excluded workload rolls out a new revision. + 8.3 Previously-excluded workload now has runtime detected +9. **Workload exclusion (2)** - Create an Excluded Source in an already-instrumented namespace. Verify: + 9.1 Only the newly excluded workload rolls out a new revision. + 9.2 InstrumentationConfigs exist for all workloads except newly excluded workload. + +## Workload generations and revisions + +The various `*-workloads.yaml` files for each phase of the test look at 2 important values: + +* The `deployment.kubernetes.io/revision` annotation +* The `metadata.generation` value + +Changes to the workload manifest that don't result in a new rollout increase the `generation`, but not the `revision`. + +In this case, the numbers become skewed when we annotate the deployments in step 4 (which does not trigger a rollout). + +These numbers are used to verify that the Odigos controllers have triggered an instrumentation rollout. diff --git a/tests/e2e/source/chainsaw-test.yaml b/tests/e2e/source/chainsaw-test.yaml index 7e3e4ff30..81f108514 100644 --- a/tests/e2e/source/chainsaw-test.yaml +++ b/tests/e2e/source/chainsaw-test.yaml @@ -89,25 +89,36 @@ spec: try: - assert: file: ../../common/assert/simple-demo-instrumented.yaml + - assert: + timeout: 2m + file: 01-workloads.yaml + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership - name: Generate Traffic try: - script: - timeout: 300s + timeout: 500s content: | - # Apply the job - kubectl apply -f ../../common/apply/generate-traffic-job.yaml - - # Wait for the job to complete - job_name=$(kubectl get -f ../../common/apply/generate-traffic-job.yaml -o=jsonpath='{.metadata.name}') - kubectl wait --for=condition=complete job/$job_name - - # Delete the job - kubectl delete -f ../../common/apply/generate-traffic-job.yaml - while true; do # wait for traces to be available sleep 8 + # Apply the job + kubectl apply -f ../../common/apply/generate-traffic-job.yaml + + # Wait for the job to complete + job_name=$(kubectl get -f ../../common/apply/generate-traffic-job.yaml -o=jsonpath='{.metadata.name}') + kubectl wait --for=condition=complete job/$job_name + + # Delete the job + kubectl delete -f ../../common/apply/generate-traffic-job.yaml + # Run the wait-for-trace script echo "Running TraceQL test at $(date)" ../../common/traceql_runner.sh tracesql/wait-for-trace.yaml @@ -148,3 +159,298 @@ spec: - podLogs: name: odiglet namespace: odigos-system + - name: Uninstrument individual deployments + try: + - script: + timeout: 60s + content: | + kubectl delete sources --all + while true; do + ic_count=$(kubectl get instrumentationconfigs --output name | wc -l) + if [ $ic_count -eq "0" ]; then + break + fi + sleep 5 + done + - name: Assert workloads updated after uninstrumentation + try: + - assert: + timeout: 2m + file: 02-workloads.yaml + - name: Update reported service name for second phase of test + try: + - script: + timeout: 70s + content: | + kubectl annotate deployment coupon odigos.io/reported-name=coupon-2 + kubectl annotate deployment frontend odigos.io/reported-name=frontend-2 + kubectl annotate deployment inventory odigos.io/reported-name=inventory-2 + kubectl annotate deployment pricing odigos.io/reported-name=pricing-2 + kubectl annotate deployment membership odigos.io/reported-name=membership-2 + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Instrument Namespace + try: + - apply: + file: 02-source-ns.yaml + - name: Assert Runtime Detected + try: + - assert: + timeout: 2m + file: ../../common/assert/simple-demo-runtime-detected.yaml + - assert: + timeout: 2m + file: 03-workloads.yaml + - name: Simple demo instrumented after runtime detection + try: + - assert: + file: ../../common/assert/simple-demo-instrumented.yaml + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Generate Traffic + try: + - script: + timeout: 500s + content: | + while true; do + # wait for traces to be available + sleep 8 + + # Apply the job + kubectl apply -f ../../common/apply/generate-traffic-job.yaml + + # Wait for the job to complete + job_name=$(kubectl get -f ../../common/apply/generate-traffic-job.yaml -o=jsonpath='{.metadata.name}') + kubectl wait --for=condition=complete job/$job_name + + # Delete the job + kubectl delete -f ../../common/apply/generate-traffic-job.yaml + + # Run the wait-for-trace script + echo "Running TraceQL test at $(date)" + ../../common/traceql_runner.sh tracesql/wait-for-trace-2.yaml + + if [ $? -eq 0 ]; then + break + else + ../../common/flush_traces.sh + sleep 5 + fi + done + - name: Verify Trace - Context Propagation + try: + - script: + timeout: 30s + content: | + ../../common/traceql_runner.sh tracesql/context-propagation-2.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Verify Trace - Resource Attributes + try: + - script: + content: | + ../../common/traceql_runner.sh tracesql/resource-attributes.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Verify Trace - Span Attributes + try: + - script: + timeout: 60s + content: | + ../../common/traceql_runner.sh tracesql/span-attributes-2.yaml + catch: + - podLogs: + name: odiglet + namespace: odigos-system + - name: Uninstrument namespace + try: + - script: + timeout: 60s + content: | + kubectl delete sources --all + while true; do + ic_count=$(kubectl get instrumentationconfigs --output name | wc -l) + if [ $ic_count -eq "0" ]; then + break + fi + sleep 5 + done + - name: Assert workloads updated after uninstrumentation + try: + - assert: + timeout: 2m + file: 04-workloads.yaml + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Instrument frontend workload specifically + try: + - apply: + file: 05-source.yaml + - name: Assert Runtime Detected for single workload + try: + - assert: + timeout: 2m + file: 05-assert-runtime-detected.yaml + - assert: + timeout: 2m + file: 05-workloads.yaml + - name: Single workload instrumented after runtime detection + try: + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Instrument rest of Namespace + try: + - apply: + file: 02-source-ns.yaml + - name: Assert Runtime Detected for all workloads + try: + - assert: + timeout: 2m + file: ../../common/assert/simple-demo-runtime-detected.yaml + - assert: + timeout: 2m + file: 06-workloads.yaml + - name: Uninstrument namespace + try: + - script: + timeout: 60s + content: | + kubectl delete sources/default + while true; do + ic_count=$(kubectl get instrumentationconfigs --output name | wc -l) + if [ $ic_count -eq "1" ]; then + break + fi + sleep 5 + done + - name: Wait for deleted sources to roll out new revisions + try: + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Assert Runtime still Detected for single workload + try: + - assert: + timeout: 2m + file: 05-assert-runtime-detected.yaml + - assert: + timeout: 2m + file: 07-workloads.yaml + - name: Create Workload exclusion Source for single workload + try: + - apply: + file: 08-source.yaml + - name: Instrument rest of Namespace + try: + - apply: + file: 02-source-ns.yaml + - name: Wait for created source to roll out new revisions + try: + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Assert runtime detected for all workloads except excluded (coupon) + try: + - assert: + timeout: 2m + file: 08-assert-runtime-detected.yaml + - assert: + timeout: 2m + file: 08-workloads.yaml + - name: Assert runtime not detected for excluded (coupon) + try: + - script: + content: kubectl get instrumentationconfigs/deployment-coupon + check: + ($error != null): true + - name: Delete excluded workload Source + try: + - script: + content: kubectl delete sources -l odigos.io/e2e=source-excluded + check: + ($error == null): true + - name: Wait for deleted exclusion source to roll out new revisions + try: + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Assert runtime detected for no-longer-excluded workload + try: + - assert: + timeout: 2m + file: ../../common/assert/simple-demo-runtime-detected.yaml + - assert: + timeout: 2m + file: 08-workloads-2.yaml + - name: Create excluded workload Source while namespace is instrumented + try: + - apply: + file: 09-source.yaml + - name: Wait for created source to roll out new revisions + try: + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Assert runtime detected for all workloads except newly excluded (membership) + try: + - assert: + timeout: 2m + file: 09-assert-runtime-detected.yaml + - assert: + timeout: 2m + file: 09-workloads.yaml + - name: Assert runtime not detected for newly excluded (membership) + try: + - script: + content: kubectl get instrumentationconfigs/deployment-membership + check: + ($error != null): true + diff --git a/tests/e2e/source/tracesql/context-propagation-2.yaml b/tests/e2e/source/tracesql/context-propagation-2.yaml new file mode 100644 index 000000000..fb3a1ca4d --- /dev/null +++ b/tests/e2e/source/tracesql/context-propagation-2.yaml @@ -0,0 +1,13 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: This test checks if the context propagation is working correctly between different languages +query: | + { resource.service.name = "frontend-2" && resource.telemetry.sdk.language = "java" && + span.http.request.method = "POST" && span.http.route = "/buy" && span:kind = server } + >> ( + { resource.service.name = "pricing-2" && resource.telemetry.sdk.language = "dotnet" } && + { resource.service.name = "inventory-2" && resource.telemetry.sdk.language = "python" } && + ({ resource.service.name = "coupon-2" && resource.telemetry.sdk.language = "nodejs" } + >> { resource.service.name = "membership-2" && resource.telemetry.sdk.language = "go" })) +expected: + minimum: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/context-propagation.yaml b/tests/e2e/source/tracesql/context-propagation.yaml index 9c463f9b3..8ebb6d301 100644 --- a/tests/e2e/source/tracesql/context-propagation.yaml +++ b/tests/e2e/source/tracesql/context-propagation.yaml @@ -10,4 +10,4 @@ query: | ({ resource.service.name = "coupon" && resource.telemetry.sdk.language = "nodejs" } >> { resource.service.name = "membership" && resource.telemetry.sdk.language = "go" })) expected: - count: 1 \ No newline at end of file + minimum: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/span-attributes-2.yaml b/tests/e2e/source/tracesql/span-attributes-2.yaml new file mode 100644 index 000000000..b997d72b4 --- /dev/null +++ b/tests/e2e/source/tracesql/span-attributes-2.yaml @@ -0,0 +1,18 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: | + This test checks the span attributes for a specific trace. + TODO - JS, Python and DotNet SDK are not generating data in latest semconv. add additional checks when they are updated. +query: | + { resource.service.name = "frontend-2" && resource.telemetry.sdk.language = "java" && + span.http.request.method = "POST" && span.http.route = "/buy" && span:kind = server && + span.http.response.status_code = 200 && span.url.query = "id=123" } + >> ( + { resource.service.name = "pricing-2" && resource.telemetry.sdk.language = "dotnet" && span:kind = server } && + { resource.service.name = "inventory-2" && resource.telemetry.sdk.language = "python" && span:kind = server } && + ({ resource.service.name = "coupon-2" && resource.telemetry.sdk.language = "nodejs" && span:kind = server } + >> { resource.service.name = "membership-2" && resource.telemetry.sdk.language = "go" && + span.http.request.method = "GET" && span:kind = server && + span.http.response.status_code = 200 && span.url.path = "/isMember" })) +expected: + minimum: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/span-attributes.yaml b/tests/e2e/source/tracesql/span-attributes.yaml index d508d4a39..6dd10cd28 100644 --- a/tests/e2e/source/tracesql/span-attributes.yaml +++ b/tests/e2e/source/tracesql/span-attributes.yaml @@ -15,4 +15,4 @@ query: | span.http.request.method = "GET" && span:kind = server && span.http.response.status_code = 200 && span.url.path = "/isMember" })) expected: - count: 1 \ No newline at end of file + minimum: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/wait-for-trace-2.yaml b/tests/e2e/source/tracesql/wait-for-trace-2.yaml new file mode 100644 index 000000000..a51ab240b --- /dev/null +++ b/tests/e2e/source/tracesql/wait-for-trace-2.yaml @@ -0,0 +1,11 @@ +apiVersion: e2e.tests.odigos.io/v1 +kind: TraceTest +description: This test waits for a trace that goes from frontend to pricing, inventory, coupon, and membership services +query: | + { resource.service.name = "frontend-2" } && + { resource.service.name = "pricing-2" } && + { resource.service.name = "inventory-2" } && + { resource.service.name = "coupon-2" } && + { resource.service.name = "membership-2" } +expected: + minimum: 1 \ No newline at end of file diff --git a/tests/e2e/source/tracesql/wait-for-trace.yaml b/tests/e2e/source/tracesql/wait-for-trace.yaml index a88f58987..62674d4ee 100644 --- a/tests/e2e/source/tracesql/wait-for-trace.yaml +++ b/tests/e2e/source/tracesql/wait-for-trace.yaml @@ -8,4 +8,4 @@ query: | { resource.service.name = "coupon" } && { resource.service.name = "membership" } expected: - count: 1 \ No newline at end of file + minimum: 1 \ No newline at end of file diff --git a/tests/e2e/workload-lifecycle/02-update-workload-manifests.yaml b/tests/e2e/workload-lifecycle/02-update-workload-manifests.yaml index c9b5432c9..eede39830 100644 --- a/tests/e2e/workload-lifecycle/02-update-workload-manifests.yaml +++ b/tests/e2e/workload-lifecycle/02-update-workload-manifests.yaml @@ -55,6 +55,8 @@ metadata: namespace: default labels: app: nodejs-minimum-version + annotations: + odigos.io/reported-name: nodejs-minimum-version-reported spec: selector: matchLabels: @@ -80,6 +82,8 @@ metadata: namespace: default labels: app: nodejs-latest-version + annotations: + odigos.io/reported-name: nodejs-latest-version-reported spec: selector: matchLabels: @@ -105,6 +109,8 @@ metadata: namespace: default labels: app: nodejs-dockerfile-env + annotations: + odigos.io/reported-name: nodejs-dockerfile-env-reported spec: selector: matchLabels: @@ -130,6 +136,8 @@ metadata: namespace: default labels: app: nodejs-manifest-env + annotations: + odigos.io/reported-name: nodejs-manifest-env-reported spec: selector: matchLabels: @@ -183,6 +191,8 @@ metadata: namespace: default labels: app: java-supported-version + annotations: + odigos.io/reported-name: java-supported-version-reported spec: selector: matchLabels: @@ -212,6 +222,8 @@ metadata: namespace: default labels: app: java-azul + annotations: + odigos.io/reported-name: java-azul-reported spec: selector: matchLabels: @@ -241,6 +253,8 @@ metadata: namespace: default labels: app: java-supported-docker-env + annotations: + odigos.io/reported-name: java-supported-docker-env-reported spec: selector: matchLabels: @@ -271,6 +285,8 @@ metadata: namespace: default labels: app: java-supported-manifest-env + annotations: + odigos.io/reported-name: java-supported-manifest-env-reported spec: selector: matchLabels: @@ -301,6 +317,8 @@ metadata: namespace: default labels: app: java-latest-version + annotations: + odigos.io/reported-name: java-latest-version-reported spec: selector: matchLabels: @@ -331,6 +349,8 @@ metadata: namespace: default labels: app: java-old-version + annotations: + odigos.io/reported-name: java-old-version-reported spec: selector: matchLabels: @@ -360,6 +380,8 @@ metadata: namespace: default labels: app: python-latest-version + annotations: + odigos.io/reported-name: python-latest-version-reported spec: selector: matchLabels: @@ -397,6 +419,8 @@ metadata: namespace: default labels: app: python-alpine + annotations: + odigos.io/reported-name: python-alpine-reported spec: selector: matchLabels: @@ -434,6 +458,8 @@ metadata: namespace: default labels: app: python-not-supported + annotations: + odigos.io/reported-name: python-not-supported-reported spec: selector: matchLabels: @@ -471,6 +497,8 @@ metadata: namespace: default labels: app: python-min-version + annotations: + odigos.io/reported-name: python-min-version-reported spec: selector: matchLabels: @@ -611,4 +639,4 @@ spec: - containerPort: 8080 readinessProbe: tcpSocket: - port: 8080 \ No newline at end of file + port: 8080 diff --git a/tests/e2e/workload-lifecycle/02-wait-for-trace.yaml b/tests/e2e/workload-lifecycle/02-wait-for-trace.yaml index 41289ee4b..f0d00d800 100644 --- a/tests/e2e/workload-lifecycle/02-wait-for-trace.yaml +++ b/tests/e2e/workload-lifecycle/02-wait-for-trace.yaml @@ -2,18 +2,19 @@ apiVersion: e2e.tests.odigos.io/v1 kind: TraceTest description: This test waits for a trace that is generated from the successful instrumented services. query: | - { resource.service.name = "nodejs-minimum-version" } || - { resource.service.name = "nodejs-latest-version" } || - { resource.service.name = "nodejs-dockerfile-env" } || - { resource.service.name = "nodejs-manifest-env" } || - { resource.service.name = "java-supported-version" } || - { resource.service.name = "java-latest-version" } || - { resource.service.name = "java-old-version" } || - { resource.service.name = "java-supported-docker-env" } || - { resource.service.name = "java-supported-manifest-env" } || - { resource.service.name = "java-azul" } || - { resource.service.name = "python-latest-version" && span.http.route = "insert-random/" } || - { resource.service.name = "python-alpine" && span.http.route = "insert-random/" } || - { resource.service.name = "python-min-version" && span.http.route = "insert-random/" } + { resource.service.name = "nodejs-minimum-version-reported" } || + { resource.service.name = "nodejs-latest-version-reported" } || + { resource.service.name = "nodejs-dockerfile-env-reported" } || + { resource.service.name = "nodejs-manifest-env-reported" } || + { resource.service.name = "java-supported-version-reported" } || + { resource.service.name = "java-latest-version-reported" } || + { resource.service.name = "java-old-version-reported" } || + { resource.service.name = "java-supported-docker-env-reported" } || + { resource.service.name = "java-supported-manifest-env-reported" } || + { resource.service.name = "java-azul-reported" } || + { resource.service.name = "python-latest-version-reported" && span.http.route = "insert-random/" } || + { resource.service.name = "python-alpine-reported" && span.http.route = "insert-random/" } || + { resource.service.name = "python-not-supported-reported" && span.http.route = "insert-random/" } || + { resource.service.name = "python-min-version-reported" && span.http.route = "insert-random/" } expected: - count: 26 # 13 before +13 new ones + count: 13 From fc9f1c845e6a1c883f98184fc8779b3b7a869310 Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Wed, 8 Jan 2025 21:44:19 +0200 Subject: [PATCH 205/259] [GEN-2149]: handle Source CRD "kind=Namespace" in UI (#2116) This pull request includes multiple changes to the `frontend` package, focusing on improving the handling of Kubernetes namespaces, updating the GraphQL schema, and refining the source CRD management. The most important changes include modifying how `selected` is handled for namespaces, updating the GraphQL schema to reflect these changes, and improving error handling and code readability. ### Namespace Handling Improvements: * Changed `Selected` from a pointer to a boolean in `K8sActualNamespace` struct and updated related methods to reflect this change. [[1]](diffhunk://#diff-642ccd7ed71fdfa394bd7f7fd99c9c33e20ff18c876a91cb989d379a44390469L300-R300) [[2]](diffhunk://#diff-bc07b91dedd1782d9ddbbb6374ad97c7604f9a267de5174645723d863c732f80L64-R64) * Updated `GetK8SNamespaces` function to return `model.K8sActualNamespace` objects directly and improved error handling. [[1]](diffhunk://#diff-f9a4ef75e4edee8d545dc9ff344d09497a9f43e15fda565ab7b732183f1ea559L6-R50) [[2]](diffhunk://#diff-f9a4ef75e4edee8d545dc9ff344d09497a9f43e15fda565ab7b732183f1ea559L121-L138) ### GraphQL Schema Updates: * Updated GraphQL schema to make `selected` a non-nullable boolean in `K8sActualNamespace`. * Modified `schema.resolvers.go` to handle the new structure of `K8sActualNamespace` and improved error handling in the `PersistK8sNamespace` resolver. [[1]](diffhunk://#diff-8e6e95029056db2c0301fc338e0ca5a04356ce5d45ee9514bbd167f2d85bae70L23-R37) [[2]](diffhunk://#diff-8e6e95029056db2c0301fc338e0ca5a04356ce5d45ee9514bbd167f2d85bae70L360-R369) ### Source CRD Management: * Added `WorkloadKindNamespace` to the `WorkloadKind` type and updated related functions to handle namespace selection. [[1]](diffhunk://#diff-d8f62b6675961fc4e307e4fd622a59132ddc730b6e701ae1bc43dd1695f969a7R27) [[2]](diffhunk://#diff-d8f62b6675961fc4e307e4fd622a59132ddc730b6e701ae1bc43dd1695f969a7L268-R306) * Refactored `ToggleSourceCRD` to accept a boolean instead of a pointer and added a new function `CheckWorkloadKindForSourceCRD`. [[1]](diffhunk://#diff-d8f62b6675961fc4e307e4fd622a59132ddc730b6e701ae1bc43dd1695f969a7L333-R343) [[2]](diffhunk://#diff-0bc55cad555c9f9e19da7b9d8cee8cf9a01a961e1d4feb3293a54f449b7f1064R63-R72) ### Error Handling and Code Readability: * Improved error handling in `_K8sActualNamespace_selected` and `_K8sActualNamespace` methods in `generated.go` to ensure errors are correctly reported. [[1]](diffhunk://#diff-4bacf1f13939a5c243f3f83d21f4560b331d13667d81ea5945ed1f57ddb205f2R8587-R8594) [[2]](diffhunk://#diff-4bacf1f13939a5c243f3f83d21f4560b331d13667d81ea5945ed1f57ddb205f2R19486-R19488) * Removed unused imports and refactored code for better readability in `schema.resolvers.go`. ### Web Application Hooks: * Simplified `useSourceCRUD` hook by removing the unused `removeSource` function. --------- Co-authored-by: Mike Dame Co-authored-by: Ron Federman --- frontend/graph/generated.go | 10 +- frontend/graph/model/models_gen.go | 2 +- frontend/graph/schema.graphqls | 2 +- frontend/graph/schema.resolvers.go | 28 +++-- frontend/services/namespaces.go | 100 ++++-------------- frontend/services/sources.go | 41 +++---- frontend/services/utils.go | 10 ++ .../webapp/hooks/sources/useSourceCRUD.ts | 2 +- .../e2e/source/tracesql/wait-for-trace-2.yaml | 2 +- 9 files changed, 85 insertions(+), 112 deletions(-) diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index 06e2a490f..7e54f9e6e 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -8584,11 +8584,14 @@ func (ec *executionContext) _K8sActualNamespace_selected(ctx context.Context, fi return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*bool) + res := resTmp.(bool) fc.Result = res - return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) + return ec.marshalNBoolean2bool(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_K8sActualNamespace_selected(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -19480,6 +19483,9 @@ func (ec *executionContext) _K8sActualNamespace(ctx context.Context, sel ast.Sel } case "selected": out.Values[i] = ec._K8sActualNamespace_selected(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } case "k8sActualSources": field := field diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index fd895d00b..191de4a84 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -297,7 +297,7 @@ type InstrumentationRuleInput struct { type K8sActualNamespace struct { Name string `json:"name"` - Selected *bool `json:"selected,omitempty"` + Selected bool `json:"selected"` K8sActualSources []*K8sActualSource `json:"k8sActualSources"` } diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index 268511f3f..2685b97e1 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -61,7 +61,7 @@ type Condition { type K8sActualNamespace { name: String! - selected: Boolean + selected: Boolean! k8sActualSources: [K8sActualSource]! } diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index 9dae1d295..33ca6a1a5 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -20,21 +20,21 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" ) // K8sActualNamespaces is the resolver for the k8sActualNamespaces field. func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) { - namespacesResponse := services.GetK8SNamespaces(ctx) + k8sNamespaces, err := services.GetK8SNamespaces(ctx) + if err != nil { + return nil, err + } - K8sActualNamespaces := make([]*model.K8sActualNamespace, len(namespacesResponse.Namespaces)) - for i, namespace := range namespacesResponse.Namespaces { - K8sActualNamespaces[i] = &model.K8sActualNamespace{ - Name: namespace.Name, - } + k8sActualNamespaces := make([]*model.K8sActualNamespace, len(k8sNamespaces.Namespaces)) + for i, namespace := range k8sNamespaces.Namespaces { + k8sActualNamespaces[i] = &namespace } - return K8sActualNamespaces, nil + return k8sActualNamespaces, nil } // K8sActualNamespace is the resolver for the k8sActualNamespace field. @@ -357,10 +357,16 @@ func (r *mutationResolver) CreateNewDestination(ctx context.Context, destination // PersistK8sNamespace is the resolver for the persistK8sNamespace field. func (r *mutationResolver) PersistK8sNamespace(ctx context.Context, namespace model.PersistNamespaceItemInput) (bool, error) { - jsonMergePayload := services.GetJsonMergePatchForInstrumentationLabel(&namespace.FutureSelected) - _, err := kube.DefaultClient.CoreV1().Namespaces().Patch(ctx, namespace.Name, types.MergePatchType, jsonMergePayload, metav1.PatchOptions{}) + persistObjects := []model.PersistNamespaceSourceInput{} + persistObjects = append(persistObjects, model.PersistNamespaceSourceInput{ + Name: namespace.Name, + Kind: model.K8sResourceKind(services.WorkloadKindNamespace), + Selected: namespace.FutureSelected, + }) + + err := services.SyncWorkloadsInNamespace(ctx, namespace.Name, persistObjects) if err != nil { - return false, fmt.Errorf("failed to patch namespace: %v", err) + return false, fmt.Errorf("failed to sync workloads: %v", err) } return true, nil diff --git a/frontend/services/namespaces.go b/frontend/services/namespaces.go index 7c8c22ca2..69787f104 100644 --- a/frontend/services/namespaces.go +++ b/frontend/services/namespaces.go @@ -3,76 +3,51 @@ package services import ( "context" "fmt" - - "github.com/odigos-io/odigos/common" - "github.com/odigos-io/odigos/k8sutils/pkg/client" - "sigs.k8s.io/yaml" - - "k8s.io/apimachinery/pkg/runtime/schema" + "strings" "golang.org/x/sync/errgroup" + "github.com/odigos-io/odigos/common" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/common/utils" - "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" + "github.com/odigos-io/odigos/k8sutils/pkg/client" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/yaml" ) type GetNamespacesResponse struct { - Namespaces []GetNamespaceItem `json:"namespaces"` + Namespaces []model.K8sActualNamespace `json:"namespaces"` } -type GetNamespaceItem struct { - Name string `json:"name"` - Selected bool `json:"selected"` - TotalApps int `json:"totalApps"` -} - -const ( - OdigosSystemNamespace = "odigos-system" -) - -func GetK8SNamespaces(ctx context.Context) GetNamespacesResponse { - - var ( - relevantNameSpaces []v1.Namespace - appsPerNamespace map[string]int - ) - - g, ctx := errgroup.WithContext(ctx) - g.Go(func() error { - var err error - relevantNameSpaces, err = getRelevantNameSpaces(ctx, OdigosSystemNamespace) - return err - }) - - g.Go(func() error { - var err error - appsPerNamespace, err = CountAppsPerNamespace(ctx) - return err - }) - - if err := g.Wait(); err != nil { - - return GetNamespacesResponse{} +func GetK8SNamespaces(ctx context.Context) (GetNamespacesResponse, error) { + relevantNameSpaces, err := getRelevantNameSpaces(ctx, consts.DefaultOdigosNamespace) + if err != nil { + return GetNamespacesResponse{}, err } var response GetNamespacesResponse for _, namespace := range relevantNameSpaces { + nsName := namespace.Name + // check if entire namespace is instrumented - selected := namespace.Labels[consts.OdigosInstrumentationLabel] == consts.InstrumentationEnabled + source, err := GetSourceCRD(ctx, nsName, nsName, WorkloadKindNamespace) + if err != nil && !strings.Contains(err.Error(), "not found") { + return GetNamespacesResponse{}, err + } - response.Namespaces = append(response.Namespaces, GetNamespaceItem{ - Name: namespace.Name, - Selected: selected, - TotalApps: appsPerNamespace[namespace.Name], + selected := source != nil + response.Namespaces = append(response.Namespaces, model.K8sActualNamespace{ + Name: nsName, + Selected: selected, }) } - return response + return response, nil } // getRelevantNameSpaces returns a list of namespaces that are relevant for instrumentation. @@ -118,24 +93,10 @@ func getRelevantNameSpaces(ctx context.Context, odigosns string) ([]v1.Namespace return result, nil } -type PersistNamespaceItem struct { - Name string `json:"name"` - SelectedAll bool `json:"selected_all"` - FutureSelected *bool `json:"future_selected,omitempty"` - Objects []PersistNamespaceObject `json:"objects"` -} - -type PersistNamespaceObject struct { - Name string `json:"name"` - Kind WorkloadKind `json:"kind"` - Selected *bool `json:"selected,omitempty"` -} - // returns a map, where the key is a namespace name and the value is the // number of apps in this namespace (not necessarily instrumented) func CountAppsPerNamespace(ctx context.Context) (map[string]int, error) { namespaceToAppsCount := make(map[string]int) - resourceTypes := []string{"deployments", "statefulsets", "daemonsets"} for _, resourceType := range resourceTypes { @@ -158,28 +119,13 @@ func CountAppsPerNamespace(ctx context.Context) (map[string]int, error) { return namespaceToAppsCount, nil } -func GetJsonMergePatchForInstrumentationLabel(enabled *bool) []byte { - labelJsonMergePatchValue := "null" - if enabled != nil { - if *enabled { - labelJsonMergePatchValue = fmt.Sprintf("\"%s\"", consts.InstrumentationEnabled) - } else { - labelJsonMergePatchValue = fmt.Sprintf("\"%s\"", consts.InstrumentationDisabled) - } - } - - jsonMergePatchContent := fmt.Sprintf(`{"metadata":{"labels":{"%s":%s}}}`, consts.OdigosInstrumentationLabel, labelJsonMergePatchValue) - return []byte(jsonMergePatchContent) -} - func SyncWorkloadsInNamespace(ctx context.Context, nsName string, workloads []model.PersistNamespaceSourceInput) error { g, ctx := errgroup.WithContext(ctx) g.SetLimit(kube.K8sClientDefaultBurst) for _, workload := range workloads { - currWorkload := workload g.Go(func() error { - return ToggleSourceCRD(ctx, nsName, currWorkload.Name, WorkloadKind(currWorkload.Kind.String()), &currWorkload.Selected) + return ToggleSourceCRD(ctx, nsName, workload.Name, WorkloadKind(workload.Kind.String()), workload.Selected) }) } diff --git a/frontend/services/sources.go b/frontend/services/sources.go index ffe967d1f..9dda39229 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -3,6 +3,7 @@ package services import ( "context" "fmt" + "strings" "time" "github.com/odigos-io/odigos/api/odigos/v1alpha1" @@ -23,6 +24,7 @@ import ( type WorkloadKind string const ( + WorkloadKindNamespace WorkloadKind = "Namespace" WorkloadKindDeployment WorkloadKind = "Deployment" WorkloadKindStatefulSet WorkloadKind = "StatefulSet" WorkloadKindDaemonSet WorkloadKind = "DaemonSet" @@ -265,36 +267,43 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s } func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { - source, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{ - consts.OdigosNamespaceAnnotation: nsName, - consts.OdigosWorkloadNameAnnotation: workloadName, - consts.OdigosWorkloadKindAnnotation: string(workloadKind), - }).String()}) + list, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{ + LabelSelector: labels.SelectorFromSet(labels.Set{ + consts.OdigosNamespaceAnnotation: nsName, + consts.OdigosWorkloadNameAnnotation: workloadName, + consts.OdigosWorkloadKindAnnotation: string(workloadKind), + }).String(), + }) if err != nil { return nil, err } - if len(source.Items) == 0 { + if len(list.Items) == 0 { return nil, fmt.Errorf(`source "%s" not found`, workloadName) } - if len(source.Items) > 1 { - return nil, fmt.Errorf(`expected to get 1 source "%s", got %d`, workloadName, len(source.Items)) + if len(list.Items) > 1 { + return nil, fmt.Errorf(`expected to get 1 source "%s", got %d`, workloadName, len(list.Items)) } - crdName := source.Items[0].Name + crdName := list.Items[0].Name crd, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) return crd, err } func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - err := CheckWorkloadKind(workloadKind) + err := CheckWorkloadKindForSourceCRD(workloadKind) if err != nil { return err } source, err := GetSourceCRD(ctx, nsName, workloadName, workloadKind) - if source != nil && err == nil { + if source != nil { + // source already exists, do not create a new one + return nil + } + if err != nil && !strings.Contains(err.Error(), "not found") { + // error occurred while trying to get the source return err } @@ -316,7 +325,7 @@ func createSourceCRD(ctx context.Context, nsName string, workloadName string, wo } func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { - err := CheckWorkloadKind(workloadKind) + err := CheckWorkloadKindForSourceCRD(workloadKind) if err != nil { return err } @@ -330,12 +339,8 @@ func deleteSourceCRD(ctx context.Context, nsName string, workloadName string, wo return err } -func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled *bool) error { - if enabled == nil { - return fmt.Errorf("enabled must be provided") - } - - if *enabled { +func ToggleSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind, enabled bool) error { + if enabled { return createSourceCRD(ctx, nsName, workloadName, workloadKind) } else { return deleteSourceCRD(ctx, nsName, workloadName, workloadKind) diff --git a/frontend/services/utils.go b/frontend/services/utils.go index c092e532e..0b056d149 100644 --- a/frontend/services/utils.go +++ b/frontend/services/utils.go @@ -60,3 +60,13 @@ func CheckWorkloadKind(kind WorkloadKind) error { return errors.New("unsupported workload kind: " + string(kind)) } } + +func CheckWorkloadKindForSourceCRD(kind WorkloadKind) error { + switch kind { + // Namespace is not a workload, but we need it to "select future apps" by creating a Source CRD for it + case WorkloadKindNamespace, WorkloadKindDeployment, WorkloadKindStatefulSet, WorkloadKindDaemonSet: + return nil + default: + return errors.New("unsupported workload kind: " + string(kind)) + } +} diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index bbbde5378..3e7e7ecf9 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -15,7 +15,7 @@ export const useSourceCRUD = (params?: Params) => { const { persistNamespace } = useNamespace(); const filters = useFilterStore(); - const { sources, updateSource, removeSource } = usePaginatedStore(); + const { sources, updateSource } = usePaginatedStore(); const { setConfiguredSources } = useAppStore(); const { addPendingItems, removePendingItems } = usePendingStore(); const { addNotification, removeNotifications } = useNotificationStore(); diff --git a/tests/e2e/source/tracesql/wait-for-trace-2.yaml b/tests/e2e/source/tracesql/wait-for-trace-2.yaml index a51ab240b..2c63c4db7 100644 --- a/tests/e2e/source/tracesql/wait-for-trace-2.yaml +++ b/tests/e2e/source/tracesql/wait-for-trace-2.yaml @@ -8,4 +8,4 @@ query: | { resource.service.name = "coupon-2" } && { resource.service.name = "membership-2" } expected: - minimum: 1 \ No newline at end of file + minimum: 1 From 68ffdcb90a566cd229dbe6c870b2279d6b4b117e Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Wed, 8 Jan 2025 14:59:31 -0500 Subject: [PATCH 206/259] Add Source defaulting and validating webhooks (#2167) Adds webhooks for Source objects: * Defaulting: set labels on Source to identify workload and finalizers for deletion * Validating: ensure Sources are unique and workload identifying fields are immutable (labels and spec.workload) --- .github/workflows/e2e.yaml | 1 + cli/cmd/resources/instrumentor.go | 161 +++++++++-- .../{webhook.yaml => webhook-pod.yaml} | 0 .../instrumentor/webhook-source.yaml | 67 +++++ .../source_controller.go | 92 +++--- .../startlangdetection/source_controller.go | 61 +--- instrumentor/main.go | 25 ++ instrumentor/sources_webhooks.go | 263 ++++++++++++++++++ .../01-assert-source-without-labels.yaml | 14 + .../01-source-without-labels.yaml | 10 + .../02-assert-source-with-labels.yaml | 14 + .../02-source-with-labels.yaml | 14 + ...3-assert-source-with-incorrect-labels.yaml | 14 + .../03-source-with-incorrect-labels.yaml | 14 + ...deleteinstrumentationconfig-finalizer.yaml | 12 + ...deleteinstrumentationconfig-finalizer.yaml | 10 + ...5-assert-startlangdetection-finalizer.yaml | 14 + .../05-startlangdetection-finalizer.yaml | 12 + .../e2e/source-webhooks/06-normal-source.yaml | 10 + .../source-webhooks/07-excluded-source.yaml | 12 + .../source-webhooks/08-namespace-source.yaml | 10 + .../09-duplicate-normal-source.yaml | 10 + .../10-duplicate-excluded-source.yaml | 12 + .../11-duplicate-namespace-source.yaml | 10 + .../12-valid-workload-existing-ns-source.yaml | 10 + .../13-invalid-namespace-name-source.yaml | 10 + .../14-invalid-workload-kind-source.yaml | 10 + tests/e2e/source-webhooks/chainsaw-test.yaml | 168 +++++++++++ 28 files changed, 938 insertions(+), 122 deletions(-) rename helm/odigos/templates/instrumentor/{webhook.yaml => webhook-pod.yaml} (100%) create mode 100644 helm/odigos/templates/instrumentor/webhook-source.yaml create mode 100644 instrumentor/sources_webhooks.go create mode 100644 tests/e2e/source-webhooks/01-assert-source-without-labels.yaml create mode 100644 tests/e2e/source-webhooks/01-source-without-labels.yaml create mode 100644 tests/e2e/source-webhooks/02-assert-source-with-labels.yaml create mode 100644 tests/e2e/source-webhooks/02-source-with-labels.yaml create mode 100644 tests/e2e/source-webhooks/03-assert-source-with-incorrect-labels.yaml create mode 100644 tests/e2e/source-webhooks/03-source-with-incorrect-labels.yaml create mode 100644 tests/e2e/source-webhooks/04-assert-deleteinstrumentationconfig-finalizer.yaml create mode 100644 tests/e2e/source-webhooks/04-deleteinstrumentationconfig-finalizer.yaml create mode 100644 tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml create mode 100644 tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml create mode 100644 tests/e2e/source-webhooks/06-normal-source.yaml create mode 100644 tests/e2e/source-webhooks/07-excluded-source.yaml create mode 100644 tests/e2e/source-webhooks/08-namespace-source.yaml create mode 100644 tests/e2e/source-webhooks/09-duplicate-normal-source.yaml create mode 100644 tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml create mode 100644 tests/e2e/source-webhooks/11-duplicate-namespace-source.yaml create mode 100644 tests/e2e/source-webhooks/12-valid-workload-existing-ns-source.yaml create mode 100644 tests/e2e/source-webhooks/13-invalid-namespace-name-source.yaml create mode 100644 tests/e2e/source-webhooks/14-invalid-workload-kind-source.yaml create mode 100644 tests/e2e/source-webhooks/chainsaw-test.yaml diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 5aa1353d4..e5136fa76 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -76,6 +76,7 @@ jobs: - 'cli-upgrade' - 'workload-lifecycle' - 'source' + - 'source-webhooks' include: - kube-version: '1.20.15' kind-image: 'kindest/node:v1.20.15@sha256:a32bf55309294120616886b5338f95dd98a2f7231519c7dedcec32ba29699394' diff --git a/cli/cmd/resources/instrumentor.go b/cli/cmd/resources/instrumentor.go index 69c90babd..ba725f87e 100644 --- a/cli/cmd/resources/instrumentor.go +++ b/cli/cmd/resources/instrumentor.go @@ -21,20 +21,22 @@ import ( ) const ( - InstrumentorOtelServiceName = "instrumentor" - InstrumentorDeploymentName = "odigos-instrumentor" - InstrumentorAppLabelValue = InstrumentorDeploymentName - InstrumentorServiceName = InstrumentorDeploymentName - InstrumentorServiceAccountName = InstrumentorDeploymentName - InstrumentorRoleName = InstrumentorDeploymentName - InstrumentorRoleBindingName = InstrumentorDeploymentName - InstrumentorClusterRoleName = InstrumentorDeploymentName - InstrumentorClusterRoleBindingName = InstrumentorDeploymentName - InstrumentorCertificateName = InstrumentorDeploymentName - InstrumentorMutatingWebhookName = "mutating-webhook-configuration" - InstrumentorContainerName = "manager" - InstrumentorWebhookSecretName = "webhook-cert" - InstrumentorWebhookVolumeName = "webhook-cert" + InstrumentorOtelServiceName = "instrumentor" + InstrumentorDeploymentName = "odigos-instrumentor" + InstrumentorAppLabelValue = InstrumentorDeploymentName + InstrumentorServiceName = InstrumentorDeploymentName + InstrumentorServiceAccountName = InstrumentorDeploymentName + InstrumentorRoleName = InstrumentorDeploymentName + InstrumentorRoleBindingName = InstrumentorDeploymentName + InstrumentorClusterRoleName = InstrumentorDeploymentName + InstrumentorClusterRoleBindingName = InstrumentorDeploymentName + InstrumentorCertificateName = InstrumentorDeploymentName + InstrumentorMutatingWebhookName = "mutating-webhook-configuration" + InstrumentorSourceMutatingWebhookName = "source-mutating-webhook-configuration" + InstrumentorSourceValidatingWebhookName = "source-validating-webhook-configuration" + InstrumentorContainerName = "manager" + InstrumentorWebhookSecretName = "webhook-cert" + InstrumentorWebhookVolumeName = "webhook-cert" ) func NewInstrumentorServiceAccount(ns string) *corev1.ServiceAccount { @@ -252,7 +254,132 @@ func NewInstrumentorService(ns string) *corev1.Service { } } -func NewMutatingWebhookConfiguration(ns string, caBundle []byte) *admissionregistrationv1.MutatingWebhookConfiguration { +func NewSourceValidatingWebhookConfiguration(ns string, caBundle []byte) *admissionregistrationv1.ValidatingWebhookConfiguration { + webhook := &admissionregistrationv1.ValidatingWebhookConfiguration{ + TypeMeta: metav1.TypeMeta{ + Kind: "ValidatingWebhookConfiguration", + APIVersion: "admissionregistration.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: InstrumentorSourceValidatingWebhookName, + Labels: map[string]string{ + "app.kubernetes.io/name": "source-validating-webhook", + "app.kubernetes.io/instance": InstrumentorSourceValidatingWebhookName, + "app.kubernetes.io/component": "webhook", + "app.kubernetes.io/created-by": "instrumentor", + "app.kubernetes.io/part-of": "odigos", + }, + }, + Webhooks: []admissionregistrationv1.ValidatingWebhook{ + { + Name: "source-validating-webhook.odigos.io", + ClientConfig: admissionregistrationv1.WebhookClientConfig{ + Service: &admissionregistrationv1.ServiceReference{ + Name: InstrumentorServiceName, + Namespace: ns, + Path: ptrString("/validate-odigos-io-v1alpha1-source"), + Port: intPtr(9443), + }, + }, + Rules: []admissionregistrationv1.RuleWithOperations{ + { + Operations: []admissionregistrationv1.OperationType{ + admissionregistrationv1.Create, + admissionregistrationv1.Update, + }, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"odigos.io"}, + APIVersions: []string{"v1alpha1"}, + Resources: []string{"sources"}, + Scope: ptrGeneric(admissionregistrationv1.NamespacedScope), + }, + }, + }, + FailurePolicy: ptrGeneric(admissionregistrationv1.Ignore), + SideEffects: ptrGeneric(admissionregistrationv1.SideEffectClassNone), + TimeoutSeconds: intPtr(10), + AdmissionReviewVersions: []string{ + "v1", + }, + }, + }, + } + + if caBundle == nil { + webhook.Annotations = map[string]string{ + "cert-manager.io/inject-ca-from": fmt.Sprintf("%s/serving-cert", ns), + } + } else { + webhook.Webhooks[0].ClientConfig.CABundle = caBundle + } + + return webhook +} + +func NewSourceMutatingWebhookConfiguration(ns string, caBundle []byte) *admissionregistrationv1.MutatingWebhookConfiguration { + webhook := &admissionregistrationv1.MutatingWebhookConfiguration{ + TypeMeta: metav1.TypeMeta{ + Kind: "MutatingWebhookConfiguration", + APIVersion: "admissionregistration.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: InstrumentorSourceMutatingWebhookName, + Labels: map[string]string{ + "app.kubernetes.io/name": "source-mutating-webhook", + "app.kubernetes.io/instance": InstrumentorSourceMutatingWebhookName, + "app.kubernetes.io/component": "webhook", + "app.kubernetes.io/created-by": "instrumentor", + "app.kubernetes.io/part-of": "odigos", + }, + }, + Webhooks: []admissionregistrationv1.MutatingWebhook{ + { + Name: "source-mutating-webhook.odigos.io", + ClientConfig: admissionregistrationv1.WebhookClientConfig{ + Service: &admissionregistrationv1.ServiceReference{ + Name: InstrumentorServiceName, + Namespace: ns, + Path: ptrString("/mutate-odigos-io-v1alpha1-source"), + Port: intPtr(9443), + }, + }, + Rules: []admissionregistrationv1.RuleWithOperations{ + { + Operations: []admissionregistrationv1.OperationType{ + admissionregistrationv1.Create, + admissionregistrationv1.Update, + }, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"odigos.io"}, + APIVersions: []string{"v1alpha1"}, + Resources: []string{"sources"}, + Scope: ptrGeneric(admissionregistrationv1.NamespacedScope), + }, + }, + }, + FailurePolicy: ptrGeneric(admissionregistrationv1.Ignore), + ReinvocationPolicy: ptrGeneric(admissionregistrationv1.IfNeededReinvocationPolicy), + SideEffects: ptrGeneric(admissionregistrationv1.SideEffectClassNone), + TimeoutSeconds: intPtr(10), + AdmissionReviewVersions: []string{ + "v1", + }, + }, + }, + } + + if caBundle == nil { + webhook.Annotations = map[string]string{ + "cert-manager.io/inject-ca-from": fmt.Sprintf("%s/serving-cert", ns), + } + } else { + webhook.Webhooks[0].ClientConfig.CABundle = caBundle + } + + return webhook +} + +func NewPodMutatingWebhookConfiguration(ns string, caBundle []byte) *admissionregistrationv1.MutatingWebhookConfiguration { webhook := &admissionregistrationv1.MutatingWebhookConfiguration{ TypeMeta: metav1.TypeMeta{ Kind: "MutatingWebhookConfiguration", @@ -554,7 +681,9 @@ func (a *instrumentorResourceManager) InstallFromScratch(ctx context.Context) er } resources = append([]kube.Object{NewInstrumentorTLSSecret(a.ns, &cert), - NewMutatingWebhookConfiguration(a.ns, []byte(cert.Cert)), + NewPodMutatingWebhookConfiguration(a.ns, []byte(cert.Cert)), + NewSourceMutatingWebhookConfiguration(a.ns, []byte(cert.Cert)), + NewSourceValidatingWebhookConfiguration(a.ns, []byte(cert.Cert)), }, resources...) diff --git a/helm/odigos/templates/instrumentor/webhook.yaml b/helm/odigos/templates/instrumentor/webhook-pod.yaml similarity index 100% rename from helm/odigos/templates/instrumentor/webhook.yaml rename to helm/odigos/templates/instrumentor/webhook-pod.yaml diff --git a/helm/odigos/templates/instrumentor/webhook-source.yaml b/helm/odigos/templates/instrumentor/webhook-source.yaml new file mode 100644 index 000000000..3e8447183 --- /dev/null +++ b/helm/odigos/templates/instrumentor/webhook-source.yaml @@ -0,0 +1,67 @@ +{{- $altNames := list (printf "odigos-instrumentor.%s.svc" .Release.Namespace) (printf "odigos-instrumentor.%s.svc.cluster.local" .Release.Namespace) -}} +{{- $ca := genCA "serving-cert" 365 -}} +{{- $cert := genSignedCert "serving-cert" nil $altNames 365 $ca -}} +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: source-mutating-webhook-configuration + labels: + app.kubernetes.io/name: source-mutating-webhook + app.kubernetes.io/instance: source-mutating-webhook-configuration + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: instrumentor + app.kubernetes.io/part-of: odigos +webhooks: + - name: source-mutating-webhook.odigos.io + clientConfig: + caBundle: {{ $ca.Cert | b64enc }} + service: + name: odigos-instrumentor + namespace: {{ .Release.Namespace }} + path: /mutate-odigos-io-v1alpha1-source + port: 9443 + rules: + - operations: + - CREATE + - UPDATE + apiGroups: ["odigos.io"] + apiVersions: ["v1alpha1"] + resources: ["sources"] + scope: Namespaced + failurePolicy: Ignore + reinvocationPolicy: IfNeeded + sideEffects: None + timeoutSeconds: 10 + admissionReviewVersions: ["v1"] +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: source-validating-webhook-configuration + labels: + app.kubernetes.io/name: source-validating-webhook + app.kubernetes.io/instance: source-validating-webhook-configuration + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: instrumentor + app.kubernetes.io/part-of: odigos +webhooks: + - name: source-validating-webhook.odigos.io + clientConfig: + caBundle: {{ $ca.Cert | b64enc }} + service: + name: odigos-instrumentor + namespace: {{ .Release.Namespace }} + path: /validate-odigos-io-v1alpha1-source + port: 9443 + rules: + - operations: + - CREATE + - UPDATE + apiGroups: ["odigos.io"] + apiVersions: ["v1alpha1"] + resources: ["sources"] + scope: Namespaced + failurePolicy: Ignore + sideEffects: None + timeoutSeconds: 10 + admissionReviewVersions: ["v1"] \ No newline at end of file diff --git a/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go index d1620ade6..3c31b17b3 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go @@ -54,16 +54,6 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if source.DeletionTimestamp.IsZero() == v1alpha1.IsWorkloadExcludedSource(source) { logger.Info("Reconciling workload for Source object", "name", req.Name, "namespace", req.Namespace) - if result, err := r.setSourceLabelsIfNecessary(ctx, source); err != nil { - return result, err - } - if v1alpha1.IsWorkloadExcludedSource(source) && !controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { - controllerutil.AddFinalizer(source, consts.StartLangDetectionFinalizer) - if err := r.Update(ctx, source); err != nil { - return k8sutils.K8SUpdateErrorHandler(err) - } - } - if source.Spec.Workload.Kind == "Namespace" { logger.V(2).Info("Uninstrumenting workloads for Namespace Source", "name", req.Name, "namespace", req.Namespace) @@ -72,77 +62,59 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr workload.WorkloadKindDeployment, workload.WorkloadKindStatefulSet, } { - result, err := r.listAndSyncWorkloadList(ctx, req, kind) - if err != nil { - return result, err - } + err = errors.Join(err, r.listAndSyncWorkloadList(ctx, req, kind)) } } else { // This is a Source for a specific workload, not an entire namespace - obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) - err = r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) - if err != nil { - // TODO: Deleted objects should be filtered in the event filter - return ctrl.Result{}, err - } - - sourceList, err := v1alpha1.GetWorkloadSources(ctx, r.Client, obj) - if err != nil { - return ctrl.Result{}, err - } - if sourceList.Namespace == nil || - (sourceList.Namespace != nil && !sourceList.Namespace.DeletionTimestamp.IsZero()) || - (sourceList.Workload != nil && sourceList.Workload.DeletionTimestamp.IsZero() && v1alpha1.IsWorkloadExcludedSource(source)) { - // if this workload doesn't have a live Namespace instrumentation, or it has a live exclusion source, uninstrument it - err = errors.Join(err, deleteWorkloadInstrumentationConfig(ctx, r.Client, obj)) - err = errors.Join(err, removeReportedNameAnnotation(ctx, r.Client, obj)) - if err != nil { - return ctrl.Result{}, err - } - } + err = errors.Join(err, r.syncWorkload(ctx, source)) } - if !v1alpha1.IsWorkloadExcludedSource(source) && controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { + if !v1alpha1.IsWorkloadExcludedSource(source) && + !source.DeletionTimestamp.IsZero() && + controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { controllerutil.RemoveFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) if err := r.Update(ctx, source); err != nil { return k8sutils.K8SUpdateErrorHandler(err) } } } - return ctrl.Result{}, nil + + return ctrl.Result{}, client.IgnoreNotFound(err) } -// TODO: Move to mutating webhook -func (r *SourceReconciler) setSourceLabelsIfNecessary(ctx context.Context, source *v1alpha1.Source) (ctrl.Result, error) { - if source.Labels == nil { - source.Labels = make(map[string]string) +func (r *SourceReconciler) syncWorkload(ctx context.Context, source *v1alpha1.Source) error { + // This is a Source for a specific workload, not an entire namespace + obj := workload.ClientObjectFromWorkloadKind(source.Spec.Workload.Kind) + err := r.Client.Get(ctx, types.NamespacedName{Name: source.Spec.Workload.Name, Namespace: source.Spec.Workload.Namespace}, obj) + if err != nil { + // TODO: Deleted objects should be filtered in the event filter + return err } - if source.Labels[consts.WorkloadNameLabel] != source.Spec.Workload.Name || - source.Labels[consts.WorkloadNamespaceLabel] != source.Spec.Workload.Namespace || - source.Labels[consts.WorkloadKindLabel] != string(source.Spec.Workload.Kind) { - - source.Labels[consts.WorkloadNameLabel] = source.Spec.Workload.Name - source.Labels[consts.WorkloadNamespaceLabel] = source.Spec.Workload.Namespace - source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) - - if err := r.Update(ctx, source); err != nil { - return k8sutils.K8SUpdateErrorHandler(err) - } + sourceList, err := v1alpha1.GetWorkloadSources(ctx, r.Client, obj) + if err != nil { + return err } - return ctrl.Result{}, nil + if sourceList.Namespace == nil || + (sourceList.Namespace != nil && !sourceList.Namespace.DeletionTimestamp.IsZero()) || + (sourceList.Workload != nil && sourceList.Workload.DeletionTimestamp.IsZero() && v1alpha1.IsWorkloadExcludedSource(source)) { + // if this workload doesn't have a live Namespace instrumentation, or it has a live exclusion source, uninstrument it + err = errors.Join(err, deleteWorkloadInstrumentationConfig(ctx, r.Client, obj)) + err = errors.Join(err, removeReportedNameAnnotation(ctx, r.Client, obj)) + } + return err } func (r *SourceReconciler) listAndSyncWorkloadList(ctx context.Context, req ctrl.Request, - kind workload.WorkloadKind) (ctrl.Result, error) { + kind workload.WorkloadKind) error { logger := log.FromContext(ctx) logger.V(2).Info("Uninstrumenting workloads for Namespace Source", "name", req.Name, "namespace", req.Namespace, "kind", kind) workloads := workload.ClientListObjectFromWorkloadKind(kind) err := r.Client.List(ctx, workloads, client.InNamespace(req.Name)) - if err != nil { - return ctrl.Result{}, err + if client.IgnoreNotFound(err) != nil { + return err } switch obj := workloads.(type) { @@ -150,25 +122,25 @@ func (r *SourceReconciler) listAndSyncWorkloadList(ctx context.Context, for _, dep := range obj.Items { err = r.syncWorkloadList(ctx, kind, client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}) if err != nil { - return ctrl.Result{}, err + return err } } case *appsv1.DaemonSetList: for _, dep := range obj.Items { err = r.syncWorkloadList(ctx, kind, client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}) if err != nil { - return ctrl.Result{}, err + return err } } case *appsv1.StatefulSetList: for _, dep := range obj.Items { err = r.syncWorkloadList(ctx, kind, client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}) if err != nil { - return ctrl.Result{}, err + return err } } } - return ctrl.Result{}, err + return err } func (r *SourceReconciler) syncWorkloadList(ctx context.Context, diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 07a03e979..0965e4e34 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -2,6 +2,7 @@ package startlangdetection import ( "context" + "errors" v1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" @@ -34,16 +35,6 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // If this is a regular Source that is being created, or an Exclusion Source that is being deleted, // Attempt to reconcile the workloads for instrumentation. if source.DeletionTimestamp.IsZero() != v1alpha1.IsWorkloadExcludedSource(source) { - if result, err := r.setSourceLabelsIfNecessary(ctx, source); err != nil { - return result, err - } - if !v1alpha1.IsWorkloadExcludedSource(source) && !controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { - controllerutil.AddFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) - if err := r.Update(ctx, source); err != nil { - return k8sutils.K8SUpdateErrorHandler(err) - } - } - if source.Spec.Workload.Kind == "Namespace" { // pre-process existing Sources for specific workloads so we don't have to make a bunch of API calls // This is used to check if a workload already has an explicit Source, so we don't overwrite its InstrumentationConfig @@ -66,13 +57,10 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr workload.WorkloadKindDeployment, workload.WorkloadKindStatefulSet, } { - result, err := r.listAndReconcileWorkloadList(ctx, source, kind, namespaceKindSources) - if err != nil { - return result, err - } + err = errors.Join(err, r.listAndReconcileWorkloadList(ctx, source, kind, namespaceKindSources)) } } else { - _, err = reconcileWorkload(ctx, + _, reconcileErr := reconcileWorkload(ctx, r.Client, source.Spec.Workload.Kind, ctrl.Request{ @@ -82,12 +70,14 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr }, }, r.Scheme) - if err != nil { - return ctrl.Result{}, err + if reconcileErr != nil { + err = errors.Join(err, reconcileErr) } } - if v1alpha1.IsWorkloadExcludedSource(source) && controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { + if v1alpha1.IsWorkloadExcludedSource(source) && + !source.DeletionTimestamp.IsZero() && + controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { controllerutil.RemoveFinalizer(source, consts.StartLangDetectionFinalizer) if err := r.Update(ctx, source); err != nil { return k8sutils.K8SUpdateErrorHandler(err) @@ -95,39 +85,18 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } } - return ctrl.Result{}, err -} - -// TODO: Move to mutating webhook -func (r *SourceReconciler) setSourceLabelsIfNecessary(ctx context.Context, source *v1alpha1.Source) (ctrl.Result, error) { - if source.Labels == nil { - source.Labels = make(map[string]string) - } - - if source.Labels[consts.WorkloadNameLabel] != source.Spec.Workload.Name || - source.Labels[consts.WorkloadNamespaceLabel] != source.Spec.Workload.Namespace || - source.Labels[consts.WorkloadKindLabel] != string(source.Spec.Workload.Kind) { - - source.Labels[consts.WorkloadNameLabel] = source.Spec.Workload.Name - source.Labels[consts.WorkloadNamespaceLabel] = source.Spec.Workload.Namespace - source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) - - if err := r.Update(ctx, source); err != nil { - return k8sutils.K8SUpdateErrorHandler(err) - } - } - return ctrl.Result{}, nil + return ctrl.Result{}, client.IgnoreNotFound(err) } func (r *SourceReconciler) listAndReconcileWorkloadList(ctx context.Context, source *v1alpha1.Source, kind workload.WorkloadKind, - namespaceKindSources map[workload.WorkloadKind]map[string]struct{}) (ctrl.Result, error) { + namespaceKindSources map[workload.WorkloadKind]map[string]struct{}) error { deps := workload.ClientListObjectFromWorkloadKind(kind) err := r.Client.List(ctx, deps, client.InNamespace(source.Spec.Workload.Name)) if client.IgnoreNotFound(err) != nil { - return ctrl.Result{}, err + return err } switch obj := deps.(type) { @@ -135,25 +104,25 @@ func (r *SourceReconciler) listAndReconcileWorkloadList(ctx context.Context, for _, dep := range obj.Items { err = r.reconcileWorkloadList(ctx, ctrl.Request{NamespacedName: client.ObjectKey{Name: dep.Name, Namespace: dep.Namespace}}, kind, namespaceKindSources) if err != nil { - return ctrl.Result{}, err + return err } } case *v1.DaemonSetList: for _, dep := range obj.Items { err = r.reconcileWorkloadList(ctx, ctrl.Request{NamespacedName: client.ObjectKey{Name: dep.Name, Namespace: dep.Namespace}}, kind, namespaceKindSources) if err != nil { - return ctrl.Result{}, err + return err } } case *v1.StatefulSetList: for _, dep := range obj.Items { err = r.reconcileWorkloadList(ctx, ctrl.Request{NamespacedName: client.ObjectKey{Name: dep.Name, Namespace: dep.Namespace}}, kind, namespaceKindSources) if err != nil { - return ctrl.Result{}, err + return err } } } - return ctrl.Result{}, nil + return nil } func (r *SourceReconciler) reconcileWorkloadList(ctx context.Context, diff --git a/instrumentor/main.go b/instrumentor/main.go index b979f7bb0..37e552a6b 100644 --- a/instrumentor/main.go +++ b/instrumentor/main.go @@ -30,6 +30,7 @@ import ( corev1 "k8s.io/api/core/v1" runtimemigration "github.com/odigos-io/odigos/instrumentor/runtimemigration" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager/signals" @@ -188,6 +189,30 @@ func main() { os.Exit(1) } + err = builder. + WebhookManagedBy(mgr). + For(&odigosv1.Source{}). + WithDefaulter(&SourcesDefaulter{ + Client: mgr.GetClient(), + }). + Complete() + if err != nil { + setupLog.Error(err, "unable to create Sources mutating webhook") + os.Exit(1) + } + + err = builder. + WebhookManagedBy(mgr). + For(&odigosv1.Source{}). + WithValidator(&SourcesValidator{ + Client: mgr.GetClient(), + }). + Complete() + if err != nil { + setupLog.Error(err, "unable to create Sources validating webhook") + os.Exit(1) + } + //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/instrumentor/sources_webhooks.go b/instrumentor/sources_webhooks.go new file mode 100644 index 000000000..9fcb610ef --- /dev/null +++ b/instrumentor/sources_webhooks.go @@ -0,0 +1,263 @@ +/* +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 main + +import ( + "context" + "fmt" + "strings" + + "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +type SourcesDefaulter struct { + client.Client +} + +var _ webhook.CustomDefaulter = &SourcesDefaulter{} + +func (s *SourcesDefaulter) Default(ctx context.Context, obj runtime.Object) error { + source, ok := obj.(*v1alpha1.Source) + if !ok { + return fmt.Errorf("expected a Source but got a %T", obj) + } + + if source.Labels == nil { + source.Labels = make(map[string]string) + } + + if _, ok := source.Labels[consts.WorkloadNameLabel]; !ok { + source.Labels[consts.WorkloadNameLabel] = source.Spec.Workload.Name + } + if _, ok := source.Labels[consts.WorkloadNamespaceLabel]; !ok { + source.Labels[consts.WorkloadNamespaceLabel] = source.Spec.Workload.Namespace + } + if _, ok := source.Labels[consts.WorkloadKindLabel]; !ok { + source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) + } + + if !v1alpha1.IsWorkloadExcludedSource(source) && + source.DeletionTimestamp.IsZero() && + !controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { + controllerutil.AddFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) + } + if v1alpha1.IsWorkloadExcludedSource(source) && + source.DeletionTimestamp.IsZero() && + !controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { + controllerutil.AddFinalizer(source, consts.StartLangDetectionFinalizer) + } + + return nil +} + +type SourcesValidator struct { + client.Client +} + +var _ webhook.CustomValidator = &SourcesValidator{} + +func (s *SourcesValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + var allErrs field.ErrorList + source, ok := obj.(*v1alpha1.Source) + if !ok { + return nil, fmt.Errorf("expected a Source but got a %T", obj) + } + + errs := s.validateSourceFields(ctx, source) + if len(errs) > 0 { + allErrs = append(allErrs, errs...) + } + + if len(allErrs) == 0 { + return nil, nil + } + + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "odigos.io", Kind: "Source"}, + source.Name, allErrs) +} + +func (s *SourcesValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + var allErrs field.ErrorList + old, ok := oldObj.(*v1alpha1.Source) + if !ok { + return nil, fmt.Errorf("expected a Source but got a %T", old) + } + new, ok := newObj.(*v1alpha1.Source) + if !ok { + return nil, fmt.Errorf("expected a Source but got a %T", new) + } + + if new.GetName() != old.GetName() { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("name"), + new.GetName(), + "Source name is immutable", + )) + } + + if new.GetNamespace() != old.GetNamespace() { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("namespace"), + new.GetNamespace(), + "Source namespace is immutable", + )) + } + + if new.Labels[consts.WorkloadKindLabel] != old.Labels[consts.WorkloadKindLabel] { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("labels"), + new.Labels[consts.WorkloadKindLabel], + "Source workload-kind label is immutable", + )) + } + if new.Labels[consts.WorkloadNameLabel] != old.Labels[consts.WorkloadNameLabel] { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("labels"), + new.Labels[consts.WorkloadNameLabel], + "Source workload-name label is immutable", + )) + } + if new.Labels[consts.WorkloadNamespaceLabel] != old.Labels[consts.WorkloadNamespaceLabel] { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("labels"), + new.Labels[consts.WorkloadNamespaceLabel], + "Source workload-namespace label is immutable", + )) + } + if new.Spec.Workload != old.Spec.Workload { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec").Child("workload"), + new.Spec.Workload, + "Source workload is immutable", + )) + } + + errs := s.validateSourceFields(ctx, new) + if len(errs) > 0 { + allErrs = append(allErrs, errs...) + } + + if len(allErrs) == 0 { + return nil, nil + } + + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "odigos.io", Kind: "Source"}, + new.Name, allErrs) +} + +func (s *SourcesValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil +} + +func (s *SourcesValidator) validateSourceFields(ctx context.Context, source *v1alpha1.Source) field.ErrorList { + allErrs := make([]*field.Error, 0) + + if source.Labels[consts.WorkloadNameLabel] != source.Spec.Workload.Name { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("labels"), + source.Labels[consts.WorkloadNameLabel], + "odigos.io/workload-name must match spec.workload.name", + )) + } + + if source.Labels[consts.WorkloadNamespaceLabel] != source.Spec.Workload.Namespace { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("labels"), + source.Labels[consts.WorkloadNamespaceLabel], + "odigos.io/workload-namespace must match spec.workload.namespace", + )) + } + + if source.Labels[consts.WorkloadKindLabel] != string(source.Spec.Workload.Kind) { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("labels"), + source.Labels[consts.WorkloadKindLabel], + "odigos.io/workload-kind must match spec.workload.kind", + )) + } + + err := s.validateSourceUniqueness(ctx, source) + if err != nil { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec").Child("workload"), + source.Spec.Workload, + err.Error(), + )) + } + + if source.Spec.Workload.Kind == "Namespace" && + (source.Spec.Workload.Name != source.Spec.Workload.Namespace) { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec").Child("workload").Child("namespace"), + source.Spec.Workload.Namespace, + "namespace Source must have matching workload.name and workload.namespace", + )) + } + + workloadKind := workload.WorkloadKindFromString(string(source.Spec.Workload.Kind)) + if workloadKind == "" && source.Spec.Workload.Kind != "Namespace" { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec").Child("workload").Child("kind"), + source.Spec.Workload.Kind, + "workload kind must be one of (Deployment, DaemonSet, StatefulSet, Namespace)", + )) + } + + return allErrs +} + +func (s *SourcesValidator) validateSourceUniqueness(ctx context.Context, source *v1alpha1.Source) error { + sourceList := &v1alpha1.SourceList{} + selector := labels.SelectorFromSet(labels.Set{ + consts.WorkloadNameLabel: source.Labels[consts.WorkloadNameLabel], + consts.WorkloadNamespaceLabel: source.Labels[consts.WorkloadNamespaceLabel], + consts.WorkloadKindLabel: source.Labels[consts.WorkloadKindLabel], + }) + err := s.Client.List(ctx, sourceList, &client.ListOptions{LabelSelector: selector}) + if err != nil { + return err + } + if len(sourceList.Items) > 0 { + duplicates := []string{} + // In theory, there should only ever be at most 1 duplicate. But loop through all to be thorough + for _, dupe := range sourceList.Items { + // during an update, this source will show up as existing already + if dupe.GetName() != source.GetName() || dupe.GetNamespace() != source.GetNamespace() { + duplicates = append(duplicates, dupe.GetName()) + } + } + if len(duplicates) > 0 { + return fmt.Errorf("duplicate source(s) exist for workload: %s", strings.Join(duplicates, ",")) + } + } + + return nil +} diff --git a/tests/e2e/source-webhooks/01-assert-source-without-labels.yaml b/tests/e2e/source-webhooks/01-assert-source-without-labels.yaml new file mode 100644 index 000000000..c0e362d5a --- /dev/null +++ b/tests/e2e/source-webhooks/01-assert-source-without-labels.yaml @@ -0,0 +1,14 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 01-source-without-labels + namespace: default + labels: + odigos.io/workload-kind: Deployment + odigos.io/workload-namespace: default + odigos.io/workload-name: source-without-labels +spec: + workload: + name: source-without-labels + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/01-source-without-labels.yaml b/tests/e2e/source-webhooks/01-source-without-labels.yaml new file mode 100644 index 000000000..54e22db8b --- /dev/null +++ b/tests/e2e/source-webhooks/01-source-without-labels.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 01-source-without-labels + namespace: default +spec: + workload: + name: source-without-labels + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/02-assert-source-with-labels.yaml b/tests/e2e/source-webhooks/02-assert-source-with-labels.yaml new file mode 100644 index 000000000..c0e362d5a --- /dev/null +++ b/tests/e2e/source-webhooks/02-assert-source-with-labels.yaml @@ -0,0 +1,14 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 01-source-without-labels + namespace: default + labels: + odigos.io/workload-kind: Deployment + odigos.io/workload-namespace: default + odigos.io/workload-name: source-without-labels +spec: + workload: + name: source-without-labels + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/02-source-with-labels.yaml b/tests/e2e/source-webhooks/02-source-with-labels.yaml new file mode 100644 index 000000000..9a5ed575f --- /dev/null +++ b/tests/e2e/source-webhooks/02-source-with-labels.yaml @@ -0,0 +1,14 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 02-source-with-labels + namespace: default + labels: + odigos.io/workload-kind: Deployment + odigos.io/workload-namespace: default + odigos.io/workload-name: source-with-labels +spec: + workload: + name: source-with-labels + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/03-assert-source-with-incorrect-labels.yaml b/tests/e2e/source-webhooks/03-assert-source-with-incorrect-labels.yaml new file mode 100644 index 000000000..29905b518 --- /dev/null +++ b/tests/e2e/source-webhooks/03-assert-source-with-incorrect-labels.yaml @@ -0,0 +1,14 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 03-source-with-incorrect-labels + namespace: default + labels: + odigos.io/workload-kind: Deployment + odigos.io/workload-namespace: default + odigos.io/workload-name: source-with-incorrect-labels +spec: + workload: + name: source-with-incorrect-labels + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/03-source-with-incorrect-labels.yaml b/tests/e2e/source-webhooks/03-source-with-incorrect-labels.yaml new file mode 100644 index 000000000..5119bb634 --- /dev/null +++ b/tests/e2e/source-webhooks/03-source-with-incorrect-labels.yaml @@ -0,0 +1,14 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 03-source-with-incorrect-labels + namespace: default + labels: + odigos.io/workload-kind: StatefulSet + odigos.io/workload-namespace: kube-system + odigos.io/workload-name: foo +spec: + workload: + name: source-with-incorrect-labels + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/04-assert-deleteinstrumentationconfig-finalizer.yaml b/tests/e2e/source-webhooks/04-assert-deleteinstrumentationconfig-finalizer.yaml new file mode 100644 index 000000000..6f8e6f7e4 --- /dev/null +++ b/tests/e2e/source-webhooks/04-assert-deleteinstrumentationconfig-finalizer.yaml @@ -0,0 +1,12 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 04-deleteinstrumentationconfig-finalizer + namespace: default + finalizers: + - odigos.io/source-deleteinstrumentationconfig-finalizer +spec: + workload: + name: deleteinstrumentationconfig-finalizer + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/04-deleteinstrumentationconfig-finalizer.yaml b/tests/e2e/source-webhooks/04-deleteinstrumentationconfig-finalizer.yaml new file mode 100644 index 000000000..1c40815a0 --- /dev/null +++ b/tests/e2e/source-webhooks/04-deleteinstrumentationconfig-finalizer.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 04-deleteinstrumentationconfig-finalizer + namespace: default +spec: + workload: + name: deleteinstrumentationconfig-finalizer + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml b/tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml new file mode 100644 index 000000000..3411af6e0 --- /dev/null +++ b/tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml @@ -0,0 +1,14 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 05-startlangdetection-finalizer + namespace: default + labels: + odigos.io/workload-excluded: "true" + finalizers: + - odigos.io/source-startlangdetection-finalizer +spec: + workload: + name: startlangdetection-finalizer + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml b/tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml new file mode 100644 index 000000000..679bd4d9e --- /dev/null +++ b/tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml @@ -0,0 +1,12 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 05-startlangdetection-finalizer + namespace: default + labels: + odigos.io/workload-excluded: "true" +spec: + workload: + name: startlangdetection-finalizer + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/06-normal-source.yaml b/tests/e2e/source-webhooks/06-normal-source.yaml new file mode 100644 index 000000000..5b93c8110 --- /dev/null +++ b/tests/e2e/source-webhooks/06-normal-source.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 06-normal-source + namespace: default +spec: + workload: + name: 06-normal-source + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/07-excluded-source.yaml b/tests/e2e/source-webhooks/07-excluded-source.yaml new file mode 100644 index 000000000..ae152cbcf --- /dev/null +++ b/tests/e2e/source-webhooks/07-excluded-source.yaml @@ -0,0 +1,12 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 07-excluded-source + namespace: default + labels: + odigos.io/workload-excluded: "true" +spec: + workload: + name: 07-excluded-source + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/08-namespace-source.yaml b/tests/e2e/source-webhooks/08-namespace-source.yaml new file mode 100644 index 000000000..018fe76b6 --- /dev/null +++ b/tests/e2e/source-webhooks/08-namespace-source.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 08-namespace-source + namespace: default +spec: + workload: + name: default + namespace: default + kind: Namespace \ No newline at end of file diff --git a/tests/e2e/source-webhooks/09-duplicate-normal-source.yaml b/tests/e2e/source-webhooks/09-duplicate-normal-source.yaml new file mode 100644 index 000000000..7582faad6 --- /dev/null +++ b/tests/e2e/source-webhooks/09-duplicate-normal-source.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 09-duplicate-normal-source + namespace: default +spec: + workload: + name: 06-normal-source + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml b/tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml new file mode 100644 index 000000000..0aa229b0b --- /dev/null +++ b/tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml @@ -0,0 +1,12 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 10-duplicate-excluded-source + namespace: default + labels: + odigos.io/workload-excluded: "true" +spec: + workload: + name: 07-excluded-source + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/11-duplicate-namespace-source.yaml b/tests/e2e/source-webhooks/11-duplicate-namespace-source.yaml new file mode 100644 index 000000000..eb70a75d8 --- /dev/null +++ b/tests/e2e/source-webhooks/11-duplicate-namespace-source.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 11-duplicate-namespace-source + namespace: default +spec: + workload: + name: default + namespace: default + kind: Namespace \ No newline at end of file diff --git a/tests/e2e/source-webhooks/12-valid-workload-existing-ns-source.yaml b/tests/e2e/source-webhooks/12-valid-workload-existing-ns-source.yaml new file mode 100644 index 000000000..2ee004e1b --- /dev/null +++ b/tests/e2e/source-webhooks/12-valid-workload-existing-ns-source.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 12-valid-workload-existing-ns + namespace: default +spec: + workload: + name: 12-valid-workload-existing-ns + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/13-invalid-namespace-name-source.yaml b/tests/e2e/source-webhooks/13-invalid-namespace-name-source.yaml new file mode 100644 index 000000000..a6e193d87 --- /dev/null +++ b/tests/e2e/source-webhooks/13-invalid-namespace-name-source.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 13-invalid-namespace-name + namespace: default +spec: + workload: + name: default + namespace: kube-system + kind: Namespace \ No newline at end of file diff --git a/tests/e2e/source-webhooks/14-invalid-workload-kind-source.yaml b/tests/e2e/source-webhooks/14-invalid-workload-kind-source.yaml new file mode 100644 index 000000000..137e69ec7 --- /dev/null +++ b/tests/e2e/source-webhooks/14-invalid-workload-kind-source.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 14-invalid-workload-kind + namespace: default +spec: + workload: + name: 14-invalid-workload-kind + namespace: default + kind: CronJob \ No newline at end of file diff --git a/tests/e2e/source-webhooks/chainsaw-test.yaml b/tests/e2e/source-webhooks/chainsaw-test.yaml new file mode 100644 index 000000000..7b7617b3b --- /dev/null +++ b/tests/e2e/source-webhooks/chainsaw-test.yaml @@ -0,0 +1,168 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: source +spec: + description: This e2e test runs a multi-apps scenario with Source instrumentation + skipDelete: true + steps: + - name: Install Odigos + try: + - script: + content: | + if [ "$MODE" = "cross-cloud-tests" ]; then + ../../../cli/odigos install --namespace odigos-test --version "$COMMIT_HASH" --image-prefix=public.ecr.aws/y2v0v6s7 + else + ../../../cli/odigos install --namespace odigos-test --version e2e-test + fi + timeout: 60s + - assert: + file: ../../common/assert/odigos-installed.yaml + - name: "[Defaulting] - Source without labels has labels added" + try: + - apply: + file: 01-source-without-labels.yaml + - assert: + timeout: 2m + file: 01-assert-source-without-labels.yaml + - name: "[Defaulting] - Source with correct labels is unaffected" + try: + - apply: + file: 02-source-with-labels.yaml + - assert: + timeout: 2m + file: 02-assert-source-with-labels.yaml + - name: "[Defaulting/Validating] - Source with incorrect labels is rejected (not modified to be correct)" + try: + - apply: + file: 03-source-with-incorrect-labels.yaml + expect: + - check: + ($error != null): true + - name: "[Defaulting] - Normal Source has deleteinstrumentationconfig finalizer added" + try: + - apply: + file: 04-deleteinstrumentationconfig-finalizer.yaml + - assert: + timeout: 2m + file: 04-assert-deleteinstrumentationconfig-finalizer.yaml + - name: "[Defaulting] - Finalizer does not block normal Source from being deleted" + try: + - script: + timeout: 70s + content: kubectl delete source 04-deleteinstrumentationconfig-finalizer + check: + ($error == null): true + - name: "[Defaulting] - Workload excluded Source has startlangdetection finalizer added" + try: + - apply: + file: 05-startlangdetection-finalizer.yaml + - assert: + timeout: 2m + file: 05-assert-startlangdetection-finalizer.yaml + - name: "[Defaulting] - Finalizer does not block exclusion Source from being deleted" + try: + - script: + timeout: 70s + content: kubectl delete source 05-startlangdetection-finalizer + check: + ($error == null): true + - name: "[Validating] - Normal Source passes validation" + try: + - apply: + file: 06-normal-source.yaml + - name: "[Validating] - Workload excluded Source passes validation" + try: + - apply: + file: 07-excluded-source.yaml + - name: "[Validating] - Namespace Source passes validation + Namespace Source with existing workload Source is not a duplicate (valid)" + try: + - apply: + file: 08-namespace-source.yaml + - name: "[Validating] - Duplicate normal Source does not pass validation" + try: + - apply: + file: 09-duplicate-normal-source.yaml + expect: + - check: + ($error != null): true + - name: "[Validating] - Duplicate workload excluded Source does not pass validation" + try: + - apply: + file: 10-duplicate-excluded-source.yaml + expect: + - check: + ($error != null): true + - name: "[Validating] - Duplicate namespace Source does not pass validation" + try: + - apply: + file: 11-duplicate-namespace-source.yaml + expect: + - check: + ($error != null): true + - name: "[Validating] - Workload Source with existing namespace Source is not a duplicate (valid)" + try: + - apply: + file: 12-valid-workload-existing-ns-source.yaml + - name: "[Validating] - Namespace Source with name!=namespace does not pass validation" + try: + - apply: + file: 13-invalid-namespace-name-source.yaml + expect: + - check: + ($error != null): true + - name: "[Validating] - Source with invalid kind does not pass validation" + try: + - apply: + file: 14-invalid-workload-kind-source.yaml + expect: + - check: + ($error != null): true + - name: "[Validating] - Source workload-kind label is immutable" + try: + - script: + timeout: 70s + content: kubectl label --overwrite source/06-normal-source odigos.io/workload-kind="Namespace" + check: + ($error != null): true + (contains($stderr, 'Invalid value')): true + - name: "[Validating] - Source workload-name label is immutable" + try: + - script: + timeout: 70s + content: kubectl label --overwrite source/06-normal-source odigos.io/workload-name="06-normal-source-patched" + check: + ($error != null): true + (contains($stderr, 'Invalid value')): true + - name: "[Validating] - Source workload-namespace label is immutable" + try: + - script: + timeout: 70s + content: kubectl label --overwrite source/06-normal-source odigos.io/workload-namespace="kube-system" + check: + ($error != null): true + (contains($stderr, 'Invalid value')): true + - name: "[Validating] - Source workload.name is immutable" + try: + - script: + timeout: 70s + content: kubectl patch source/06-normal-source --type=merge -p '{"spec":{"workload":{"name":"06-normal-source-patched"}}}' + check: + ($error != null): true + (contains($stderr, 'Invalid value')): true + - name: "[Validating] - Source workload.namespace is immutable" + try: + - script: + timeout: 70s + content: kubectl patch source/06-normal-source --type=merge -p '{"spec":{"workload":{"namespace":"kube-system"}}}' + check: + ($error != null): true + (contains($stderr, 'Invalid value')): true + - name: "[Validating] - Source workload.kind is immutable" + try: + - script: + timeout: 70s + content: kubectl patch source/06-normal-source --type=merge -p '{"spec":{"workload":{"kind":"DaemonSet"}}}' + check: + ($error != null): true + (contains($stderr, 'Invalid value')): true From 46d917e356eecf2eb95aa96d645e65bd56615f17 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Wed, 8 Jan 2025 15:17:58 -0500 Subject: [PATCH 207/259] Remove feature branch from workflows on main --- .github/workflows/build.yaml | 1 - .github/workflows/e2e.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1e6cd1f17..18576d68f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,7 +6,6 @@ on: branches: - main - stable - - feature/source-crd - greatwall jobs: diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index e5136fa76..378727858 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -6,7 +6,6 @@ on: branches: - main - stable - - feature/source-crd - greatwall concurrency: From 6c239c8006252deca9e4bfa96322fa0be9ff2852 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 9 Jan 2025 11:45:56 -0500 Subject: [PATCH 208/259] Update uninstall for Sources --- cli/cmd/uninstall.go | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/cli/cmd/uninstall.go b/cli/cmd/uninstall.go index 608fa981f..8b3665fda 100644 --- a/cli/cmd/uninstall.go +++ b/cli/cmd/uninstall.go @@ -101,6 +101,9 @@ var uninstallCmd = &cobra.Command{ createKubeResourceWithLogging(ctx, "Uninstalling Odigos MutatingWebhookConfigurations", client, cmd, ns, uninstallMutatingWebhookConfigs) + createKubeResourceWithLogging(ctx, "Uninstalling Odigos ValidatingWebhookConfigurations", + client, cmd, ns, uninstallValidatingWebhookConfigs) + fmt.Printf("\n\u001B[32mSUCCESS:\u001B[0m Odigos uninstalled.\n") }, } @@ -378,15 +381,6 @@ func uninstallConfigMaps(ctx context.Context, cmd *cobra.Command, client *kube.C } func uninstallCRDs(ctx context.Context, cmd *cobra.Command, client *kube.Client, ns string) error { - list, err := client.ApiExtensions.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{ - LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ - MatchLabels: labels.OdigosSystem, - }), - }) - if err != nil { - return err - } - // Clear finalizers from Source objects so they can be uninstalled sources, err := client.OdigosClient.Sources("").List(ctx, metav1.ListOptions{}) for _, i := range sources.Items { @@ -401,6 +395,15 @@ func uninstallCRDs(ctx context.Context, cmd *cobra.Command, client *kube.Client, } } + list, err := client.ApiExtensions.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{ + LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ + MatchLabels: labels.OdigosSystem, + }), + }) + if err != nil { + return err + } + for _, i := range list.Items { err = client.ApiExtensions.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, i.Name, metav1.DeleteOptions{}) if err != nil { @@ -431,6 +434,26 @@ func uninstallMutatingWebhookConfigs(ctx context.Context, cmd *cobra.Command, cl return nil } +func uninstallValidatingWebhookConfigs(ctx context.Context, cmd *cobra.Command, client *kube.Client, ns string) error { + list, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(ctx, metav1.ListOptions{ + LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ + MatchLabels: labels.OdigosSystem, + }), + }) + if err != nil { + return err + } + + for _, webhook := range list.Items { + err = client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(ctx, webhook.Name, metav1.DeleteOptions{}) + if err != nil { + return err + } + } + + return nil +} + func uninstallRBAC(ctx context.Context, cmd *cobra.Command, client *kube.Client, ns string) error { list, err := client.RbacV1().ClusterRoles().List(ctx, metav1.ListOptions{ LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ From aca2b603986387ee640058b79ad42f2780f5ce93 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 9 Jan 2025 11:56:14 -0500 Subject: [PATCH 209/259] Change GetWorkloadSources->GetSources, add IsTerminating, move exclude const to k8sutils --- api/odigos/v1alpha1/source_types.go | 7 +++--- common/consts/consts.go | 1 - .../deleteinstrumentationconfig/common.go | 5 ++-- .../instrumentationconfig_controller.go | 2 +- .../namespace_controller.go | 21 +++++++++-------- .../source_controller.go | 23 +++++++++++-------- .../namespace_controller.go | 4 ++-- .../startlangdetection/source_controller.go | 13 ++++++----- .../workload_controllers.go | 5 ++-- k8sutils/pkg/consts/consts.go | 9 ++++---- k8sutils/pkg/utils/object.go | 9 ++++++++ 11 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 k8sutils/pkg/utils/object.go diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index 1d3bfe38b..b58d80055 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -24,7 +24,6 @@ import ( "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" - commonconsts "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" ) @@ -78,10 +77,10 @@ type WorkloadSources struct { Namespace *Source } -// GetWorkloadSources returns a WorkloadSources listing the Workload and Namespace Source +// GetSources returns a WorkloadSources listing the Workload and Namespace Source // that currently apply to the given object. In theory, this should only ever return at most // 1 Namespace and/or 1 Workload Source for an object. If more are found, an error is returned. -func GetWorkloadSources(ctx context.Context, kubeClient client.Client, obj client.Object) (*WorkloadSources, error) { +func GetSources(ctx context.Context, kubeClient client.Client, obj client.Object) (*WorkloadSources, error) { var err error workloadSources := &WorkloadSources{} @@ -127,7 +126,7 @@ func GetWorkloadSources(ctx context.Context, kubeClient client.Client, obj clien // IsWorkloadExcludedSource returns true if the Source is used to exclude a workload. // Otherwise, it returns false. func IsWorkloadExcludedSource(source *Source) bool { - if val, exists := source.Labels[commonconsts.OdigosWorkloadExcludedLabel]; exists && val == "true" { + if val, exists := source.Labels[consts.OdigosWorkloadExcludedLabel]; exists && val == "true" { return true } return false diff --git a/common/consts/consts.go b/common/consts/consts.go index 81d9cf148..540088447 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -18,7 +18,6 @@ const ( OdigosNamespaceAnnotation = "odigos.io/workload-namespace" OdigosWorkloadKindAnnotation = "odigos.io/workload-kind" OdigosWorkloadNameAnnotation = "odigos.io/workload-name" - OdigosWorkloadExcludedLabel = "odigos.io/workload-excluded" OdigosReportedNameAnnotation = "odigos.io/reported-name" RolloutTriggerAnnotation = "rollout-trigger" diff --git a/instrumentor/controllers/deleteinstrumentationconfig/common.go b/instrumentor/controllers/deleteinstrumentationconfig/common.go index dd0dcedaf..deb302c42 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/common.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/common.go @@ -25,7 +25,7 @@ func reconcileWorkloadObject(ctx context.Context, kubeClient client.Client, work } // Check if a Source object exists for this workload - sourceList, err := odigosv1.GetWorkloadSources(ctx, kubeClient, workloadObject) + sourceList, err := odigosv1.GetSources(ctx, kubeClient, workloadObject) if err != nil { return err } @@ -54,7 +54,7 @@ func deleteWorkloadInstrumentationConfig(ctx context.Context, kubeClient client. name := workloadObject.GetName() kind := workload.WorkloadKindFromClientObject(workloadObject) instrumentationConfigName := workload.CalculateWorkloadRuntimeObjectName(name, kind) - logger.V(1).Info("deleting instrumentationconfig", "name", instrumentationConfigName, "kind", kind) + logger.V(1).Info("deleting instrumentationconfig", "name", instrumentationConfigName, "namespace", ns) instConfigErr := kubeClient.Delete(ctx, &odigosv1.InstrumentationConfig{ ObjectMeta: metav1.ObjectMeta{ @@ -67,7 +67,6 @@ func deleteWorkloadInstrumentationConfig(ctx context.Context, kubeClient client. return client.IgnoreNotFound(instConfigErr) } - logger.V(1).Info("instrumentationconfig deleted", "namespace", ns, "name", name, "kind", kind) return nil } diff --git a/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go index 561644100..f112aaac9 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/instrumentationconfig_controller.go @@ -92,7 +92,7 @@ func (r *InstrumentationConfigReconciler) Reconcile(ctx context.Context, req ctr if !instEffectiveEnabled { // Check if a Source object exists for this workload - sourceList, err := v1alpha1.GetWorkloadSources(ctx, r.Client, workloadObject) + sourceList, err := v1alpha1.GetSources(ctx, r.Client, workloadObject) if err != nil { return ctrl.Result{}, err } diff --git a/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go index 32398ee60..92c30254d 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/namespace_controller.go @@ -22,6 +22,7 @@ import ( "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" + k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/utils" "github.com/odigos-io/odigos/k8sutils/pkg/workload" appsv1 "k8s.io/api/apps/v1" @@ -165,22 +166,24 @@ func syncGenericWorkloadListToNs(ctx context.Context, c client.Client, kind work return err } -// this function indicates that the odigos instrumentation label is missing from the workload object manifest. -// when reconciling the namespace, the usecase is to delete instrumentation for workloads that were only -// instrumented due to the label on the namespace. These are workloads with the label missing. -// (they inherit the instrumentation from the namespace this way) +// this function checks whether a workload is instrumented via inheriting from namespace, or whether it has its own instrumentation. +// It checks: +// 1. Does a Workload Source exist for the workload +// 2. Does a Namespace Source exist for the workload +// 3. Is the workload labeled for instrumentation +// This is used to decide whether to uninstrument the workload. func isInheritingInstrumentationFromNs(ctx context.Context, c client.Client, obj client.Object) (bool, error) { - sourceList, err := v1alpha1.GetWorkloadSources(ctx, c, obj) + sourceList, err := v1alpha1.GetSources(ctx, c, obj) if err != nil { return false, err } - if sourceList.Namespace != nil && sourceList.Namespace.DeletionTimestamp.IsZero() { - return true, nil + if sourceList.Workload != nil && !k8sutils.IsTerminating(sourceList.Workload) { + return false, nil } - if sourceList.Workload != nil && sourceList.Workload.DeletionTimestamp.IsZero() { - return false, nil + if sourceList.Namespace != nil && !k8sutils.IsTerminating(sourceList.Namespace) { + return true, nil } labels := obj.GetLabels() diff --git a/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go b/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go index 3c31b17b3..db8d6860b 100644 --- a/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go +++ b/instrumentor/controllers/deleteinstrumentationconfig/source_controller.go @@ -41,7 +41,6 @@ type SourceReconciler struct { func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) - logger.Info("Reconciling Source object", "name", req.Name, "namespace", req.Namespace) source := &v1alpha1.Source{} err := r.Get(ctx, req.NamespacedName, source) @@ -51,12 +50,16 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // If this is a regular Source that's being deleted, or a workload Exclusion Source // that's being created, try to uninstrument relevant workloads. - if source.DeletionTimestamp.IsZero() == v1alpha1.IsWorkloadExcludedSource(source) { - logger.Info("Reconciling workload for Source object", "name", req.Name, "namespace", req.Namespace) + // if (terminating && !exclude) || (!terminating && exclude) + if k8sutils.IsTerminating(source) != v1alpha1.IsWorkloadExcludedSource(source) { + logger.Info("Reconciling workload for Source object", + "name", req.Name, + "namespace", req.Namespace, + "kind", source.Spec.Workload.Kind, + "excluded", v1alpha1.IsWorkloadExcludedSource(source), + "terminating", k8sutils.IsTerminating(source)) if source.Spec.Workload.Kind == "Namespace" { - logger.V(2).Info("Uninstrumenting workloads for Namespace Source", "name", req.Name, "namespace", req.Namespace) - for _, kind := range []workload.WorkloadKind{ workload.WorkloadKindDaemonSet, workload.WorkloadKindDeployment, @@ -70,7 +73,7 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } if !v1alpha1.IsWorkloadExcludedSource(source) && - !source.DeletionTimestamp.IsZero() && + k8sutils.IsTerminating(source) && controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { controllerutil.RemoveFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) if err := r.Update(ctx, source); err != nil { @@ -91,13 +94,13 @@ func (r *SourceReconciler) syncWorkload(ctx context.Context, source *v1alpha1.So return err } - sourceList, err := v1alpha1.GetWorkloadSources(ctx, r.Client, obj) + sources, err := v1alpha1.GetSources(ctx, r.Client, obj) if err != nil { return err } - if sourceList.Namespace == nil || - (sourceList.Namespace != nil && !sourceList.Namespace.DeletionTimestamp.IsZero()) || - (sourceList.Workload != nil && sourceList.Workload.DeletionTimestamp.IsZero() && v1alpha1.IsWorkloadExcludedSource(source)) { + if sources.Namespace == nil || + (sources.Namespace != nil && k8sutils.IsTerminating(sources.Namespace)) || + (sources.Workload != nil && !k8sutils.IsTerminating(sources.Workload) && v1alpha1.IsWorkloadExcludedSource(source)) { // if this workload doesn't have a live Namespace instrumentation, or it has a live exclusion source, uninstrument it err = errors.Join(err, deleteWorkloadInstrumentationConfig(ctx, r.Client, obj)) err = errors.Join(err, removeReportedNameAnnotation(ctx, r.Client, obj)) diff --git a/instrumentor/controllers/startlangdetection/namespace_controller.go b/instrumentor/controllers/startlangdetection/namespace_controller.go index 6f3e63b45..6874965b4 100644 --- a/instrumentor/controllers/startlangdetection/namespace_controller.go +++ b/instrumentor/controllers/startlangdetection/namespace_controller.go @@ -30,11 +30,11 @@ func (n *NamespacesReconciler) Reconcile(ctx context.Context, request ctrl.Reque } if !k8sutils.IsObjectLabeledForInstrumentation(&ns) { - sourceList, err := v1alpha1.GetWorkloadSources(ctx, n.Client, &ns) + sources, err := v1alpha1.GetSources(ctx, n.Client, &ns) if err != nil { return ctrl.Result{}, err } - if sourceList.Namespace == nil { + if sources.Namespace == nil { return ctrl.Result{}, nil } } diff --git a/instrumentor/controllers/startlangdetection/source_controller.go b/instrumentor/controllers/startlangdetection/source_controller.go index 0965e4e34..985d930fa 100644 --- a/instrumentor/controllers/startlangdetection/source_controller.go +++ b/instrumentor/controllers/startlangdetection/source_controller.go @@ -34,7 +34,8 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // If this is a regular Source that is being created, or an Exclusion Source that is being deleted, // Attempt to reconcile the workloads for instrumentation. - if source.DeletionTimestamp.IsZero() != v1alpha1.IsWorkloadExcludedSource(source) { + // if (terminating && exclude) || (!terminating && !exclude) + if k8sutils.IsTerminating(source) == v1alpha1.IsWorkloadExcludedSource(source) { if source.Spec.Workload.Kind == "Namespace" { // pre-process existing Sources for specific workloads so we don't have to make a bunch of API calls // This is used to check if a workload already has an explicit Source, so we don't overwrite its InstrumentationConfig @@ -44,12 +45,12 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } namespaceKindSources := make(map[workload.WorkloadKind]map[string]struct{}) - for _, source := range sourceList.Items { - if _, exists := namespaceKindSources[source.Spec.Workload.Kind]; !exists { - namespaceKindSources[source.Spec.Workload.Kind] = make(map[string]struct{}) + for _, s := range sourceList.Items { + if _, exists := namespaceKindSources[s.Spec.Workload.Kind]; !exists { + namespaceKindSources[s.Spec.Workload.Kind] = make(map[string]struct{}) } // ex: map["Deployment"]["my-app"] = ... - namespaceKindSources[source.Spec.Workload.Kind][source.Spec.Workload.Name] = struct{}{} + namespaceKindSources[s.Spec.Workload.Kind][s.Spec.Workload.Name] = struct{}{} } for _, kind := range []workload.WorkloadKind{ @@ -76,7 +77,7 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } if v1alpha1.IsWorkloadExcludedSource(source) && - !source.DeletionTimestamp.IsZero() && + k8sutils.IsTerminating(source) && controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { controllerutil.RemoveFinalizer(source, consts.StartLangDetectionFinalizer) if err := r.Update(ctx, source); err != nil { diff --git a/instrumentor/controllers/startlangdetection/workload_controllers.go b/instrumentor/controllers/startlangdetection/workload_controllers.go index 181ccf3ae..ed5b2cd02 100644 --- a/instrumentor/controllers/startlangdetection/workload_controllers.go +++ b/instrumentor/controllers/startlangdetection/workload_controllers.go @@ -10,6 +10,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/utils" "github.com/odigos-io/odigos/k8sutils/pkg/workload" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -59,7 +60,7 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor if !instrumented { // Check if a Source object exists for this workload - sourceList, err := odigosv1.GetWorkloadSources(ctx, k8sClient, obj) + sourceList, err := odigosv1.GetSources(ctx, k8sClient, obj) if err != nil { return ctrl.Result{}, err } @@ -68,7 +69,7 @@ func reconcileWorkload(ctx context.Context, k8sClient client.Client, objKind wor } // if this is explicitly excluded (and the excluded Source isn't being deleted), skip if sourceList.Workload != nil { - if odigosv1.IsWorkloadExcludedSource(sourceList.Workload) && sourceList.Workload.DeletionTimestamp.IsZero() { + if odigosv1.IsWorkloadExcludedSource(sourceList.Workload) && !k8sutils.IsTerminating(sourceList.Workload) { return ctrl.Result{}, nil } } diff --git a/k8sutils/pkg/consts/consts.go b/k8sutils/pkg/consts/consts.go index defcce72f..17f4f8bf3 100644 --- a/k8sutils/pkg/consts/consts.go +++ b/k8sutils/pkg/consts/consts.go @@ -70,14 +70,15 @@ const ( // StartLangDetectionFinalizer is used for Workload exclusion Sources. When a Workload exclusion Source // is deleted, we want to go to the startlangdetection controller. There, we will check if the Workload should // start inheriting Namespace instrumentation. - StartLangDetectionFinalizer = "odigos.io/source-startlangdetection-finalizer" + StartLangDetectionFinalizer = "odigos.io/source-startlangdetection-finalizer" // DeleteInstrumentationConfigFinalizer is used for all non-exclusion (normal) Sources. When a normal Source // is deleted, we want to go to the deleteinstrumentationconfig controller to un-instrument the workload/namespace. DeleteInstrumentationConfigFinalizer = "odigos.io/source-deleteinstrumentationconfig-finalizer" - WorkloadNameLabel = "odigos.io/workload-name" - WorkloadNamespaceLabel = "odigos.io/workload-namespace" - WorkloadKindLabel = "odigos.io/workload-kind" + WorkloadNameLabel = "odigos.io/workload-name" + WorkloadNamespaceLabel = "odigos.io/workload-namespace" + WorkloadKindLabel = "odigos.io/workload-kind" + OdigosWorkloadExcludedLabel = "odigos.io/workload-excluded" OdigosCloudApiKeySecretKey = "odigos-cloud-api-key" OdigosOnpremTokenSecretKey = "odigos-onprem-token" diff --git a/k8sutils/pkg/utils/object.go b/k8sutils/pkg/utils/object.go new file mode 100644 index 000000000..361d772e9 --- /dev/null +++ b/k8sutils/pkg/utils/object.go @@ -0,0 +1,9 @@ +package utils + +import "sigs.k8s.io/controller-runtime/pkg/client" + +// IsTerminating returns true if a client.Object has a non-zero DeletionTimestamp. +// Otherwise, it returns false. +func IsTerminating(obj client.Object) bool { + return !obj.GetDeletionTimestamp().IsZero() +} \ No newline at end of file From 5c376aa7fd523bb006092eec68fd2f859b8af13a Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 9 Jan 2025 11:57:10 -0500 Subject: [PATCH 210/259] Webhook: check valid kind casing and namespace match. Update tests --- instrumentor/sources_webhooks.go | 21 +++++++++++++------ k8sutils/pkg/workload/workloadkinds.go | 8 +++++++ .../02-assert-source-with-labels.yaml | 14 ------------- ...5-namespace-workload-namespace-source.yaml | 10 +++++++++ tests/e2e/source-webhooks/chainsaw-test.yaml | 9 +++++++- 5 files changed, 41 insertions(+), 21 deletions(-) delete mode 100644 tests/e2e/source-webhooks/02-assert-source-with-labels.yaml create mode 100644 tests/e2e/source-webhooks/15-namespace-workload-namespace-source.yaml diff --git a/instrumentor/sources_webhooks.go b/instrumentor/sources_webhooks.go index 9fcb610ef..ca870ca9c 100644 --- a/instrumentor/sources_webhooks.go +++ b/instrumentor/sources_webhooks.go @@ -23,6 +23,7 @@ import ( "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/consts" + k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/utils" "github.com/odigos-io/odigos/k8sutils/pkg/workload" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -63,12 +64,12 @@ func (s *SourcesDefaulter) Default(ctx context.Context, obj runtime.Object) erro } if !v1alpha1.IsWorkloadExcludedSource(source) && - source.DeletionTimestamp.IsZero() && + !k8sutils.IsTerminating(source) && !controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { controllerutil.AddFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) } if v1alpha1.IsWorkloadExcludedSource(source) && - source.DeletionTimestamp.IsZero() && + !k8sutils.IsTerminating(source) && !controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { controllerutil.AddFinalizer(source, consts.StartLangDetectionFinalizer) } @@ -107,11 +108,11 @@ func (s *SourcesValidator) ValidateUpdate(ctx context.Context, oldObj, newObj ru var allErrs field.ErrorList old, ok := oldObj.(*v1alpha1.Source) if !ok { - return nil, fmt.Errorf("expected a Source but got a %T", old) + return nil, fmt.Errorf("expected old Source but got a %T", old) } new, ok := newObj.(*v1alpha1.Source) if !ok { - return nil, fmt.Errorf("expected a Source but got a %T", new) + return nil, fmt.Errorf("expected new Source but got a %T", new) } if new.GetName() != old.GetName() { @@ -222,8 +223,8 @@ func (s *SourcesValidator) validateSourceFields(ctx context.Context, source *v1a )) } - workloadKind := workload.WorkloadKindFromString(string(source.Spec.Workload.Kind)) - if workloadKind == "" && source.Spec.Workload.Kind != "Namespace" { + validKind := workload.IsValidWorkloadKind(source.Spec.Workload.Kind) + if !validKind && source.Spec.Workload.Kind != "Namespace" { allErrs = append(allErrs, field.Invalid( field.NewPath("spec").Child("workload").Child("kind"), source.Spec.Workload.Kind, @@ -231,6 +232,14 @@ func (s *SourcesValidator) validateSourceFields(ctx context.Context, source *v1a )) } + if source.Spec.Workload.Namespace != source.GetNamespace() { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec").Child("workload").Child("namespace"), + source.Spec.Workload.Namespace, + "Source namespace must match spec.workload.namespace", + )) + } + return allErrs } diff --git a/k8sutils/pkg/workload/workloadkinds.go b/k8sutils/pkg/workload/workloadkinds.go index c03d9dbf2..6e74b89a6 100644 --- a/k8sutils/pkg/workload/workloadkinds.go +++ b/k8sutils/pkg/workload/workloadkinds.go @@ -44,6 +44,14 @@ func IgnoreErrorKindNotSupported(err error) error { return err } +func IsValidWorkloadKind(kind WorkloadKind) bool { + switch kind { + case WorkloadKindDeployment, WorkloadKindDaemonSet, WorkloadKindStatefulSet: + return true + } + return false +} + func WorkloadKindLowerCaseFromKind(pascalCase WorkloadKind) WorkloadKindLowerCase { switch pascalCase { case WorkloadKindDeployment: diff --git a/tests/e2e/source-webhooks/02-assert-source-with-labels.yaml b/tests/e2e/source-webhooks/02-assert-source-with-labels.yaml deleted file mode 100644 index c0e362d5a..000000000 --- a/tests/e2e/source-webhooks/02-assert-source-with-labels.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: odigos.io/v1alpha1 -kind: Source -metadata: - name: 01-source-without-labels - namespace: default - labels: - odigos.io/workload-kind: Deployment - odigos.io/workload-namespace: default - odigos.io/workload-name: source-without-labels -spec: - workload: - name: source-without-labels - namespace: default - kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/15-namespace-workload-namespace-source.yaml b/tests/e2e/source-webhooks/15-namespace-workload-namespace-source.yaml new file mode 100644 index 000000000..78d171843 --- /dev/null +++ b/tests/e2e/source-webhooks/15-namespace-workload-namespace-source.yaml @@ -0,0 +1,10 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 15-namespace-workload-namespace + namespace: default +spec: + workload: + name: 15-namespace-workload-namespace + namespace: kube-system + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/chainsaw-test.yaml b/tests/e2e/source-webhooks/chainsaw-test.yaml index 7b7617b3b..dabeb79b9 100644 --- a/tests/e2e/source-webhooks/chainsaw-test.yaml +++ b/tests/e2e/source-webhooks/chainsaw-test.yaml @@ -31,7 +31,7 @@ spec: file: 02-source-with-labels.yaml - assert: timeout: 2m - file: 02-assert-source-with-labels.yaml + file: 02-source-with-labels.yaml - name: "[Defaulting/Validating] - Source with incorrect labels is rejected (not modified to be correct)" try: - apply: @@ -166,3 +166,10 @@ spec: check: ($error != null): true (contains($stderr, 'Invalid value')): true + - name: "[Validating] - Source namespace must match source.spec.workload.namespace" + try: + - apply: + file: 15-namespace-workload-namespace-source.yaml + expect: + - check: + ($error != null): true From d529466b079b35716d6dc28614bf9c999d3644ab Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 9 Jan 2025 11:57:20 -0500 Subject: [PATCH 211/259] Update cli/cmd/resource/README.md --- cli/cmd/resources/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/cmd/resources/README.md b/cli/cmd/resources/README.md index 986a59b33..a3d4c9557 100644 --- a/cli/cmd/resources/README.md +++ b/cli/cmd/resources/README.md @@ -21,6 +21,7 @@ In this doc, we'll keep track of the permissions requested across different reso | Instrumentor | apps | daemonsets, deployments, statefulsets | get, list, watch, update, patch | Adjusts pod specifications for instrumentation. | | Instrumentor | odigos.io | instrumentedapplications | delete, get, list, watch | Delete deprecated CR. | | | Instrumentor | odigos.io | instrumentationconfigs | create, delete, get, list, patch, update, watch | Manages instrumentation configurations. | +| Instrumentor | odigos.io | sources | create, delete, get, list, patch, update, watch | Manages Source configurations. | | Scheduler | odigos.io | instrumentationconfigs | get, list, watch | Monitors changes in instrumentation configurations for scheduling updates. | | Autoscaler | odigos.io | instrumentationconfigs | get, list, watch | Reads instrumentation configurations to populate the `data-collector` configmaps. | | Collector | "" | nodes/stats, nodes/proxy | get, list | Retrieves metrics for telemetry purposes. | From 943bf5a5ad6be5b2c8b11d13cea95e1b6a49fe55 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 9 Jan 2025 12:13:14 -0500 Subject: [PATCH 212/259] Add instrumentationDisabled field --- api/config/crd/bases/odigos.io_sources.yaml | 3 +++ .../applyconfiguration/odigos/v1alpha1/sourcespec.go | 11 ++++++++++- api/odigos/v1alpha1/source_types.go | 11 +++++------ helm/odigos/templates/crds/odigos.io_sources.yaml | 3 +++ k8sutils/pkg/consts/consts.go | 1 - .../05-assert-startlangdetection-finalizer.yaml | 3 +-- .../05-startlangdetection-finalizer.yaml | 3 +-- tests/e2e/source-webhooks/07-excluded-source.yaml | 3 +-- .../source-webhooks/10-duplicate-excluded-source.yaml | 3 +-- tests/e2e/source/08-source.yaml | 2 +- tests/e2e/source/09-source.yaml | 2 +- 11 files changed, 27 insertions(+), 18 deletions(-) diff --git a/api/config/crd/bases/odigos.io_sources.yaml b/api/config/crd/bases/odigos.io_sources.yaml index c59f98ae2..69461c4c4 100644 --- a/api/config/crd/bases/odigos.io_sources.yaml +++ b/api/config/crd/bases/odigos.io_sources.yaml @@ -48,6 +48,9 @@ spec: type: object spec: properties: + instrumentationDisabled: + description: InstrumentationDisabled excludes this workload from auto-instrumentation. + type: boolean workload: description: |- Workload represents the workload or namespace to be instrumented. diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go index ae7774ba8..7aa515e9f 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/sourcespec.go @@ -24,7 +24,8 @@ import ( // SourceSpecApplyConfiguration represents a declarative configuration of the SourceSpec type for use // with apply. type SourceSpecApplyConfiguration struct { - Workload *workload.PodWorkload `json:"workload,omitempty"` + Workload *workload.PodWorkload `json:"workload,omitempty"` + InstrumentationDisabled *bool `json:"instrumentationDisabled,omitempty"` } // SourceSpecApplyConfiguration constructs a declarative configuration of the SourceSpec type for use with @@ -40,3 +41,11 @@ func (b *SourceSpecApplyConfiguration) WithWorkload(value workload.PodWorkload) b.Workload = &value return b } + +// WithInstrumentationDisabled sets the InstrumentationDisabled 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 InstrumentationDisabled field is set to the value of the last call. +func (b *SourceSpecApplyConfiguration) WithInstrumentationDisabled(value bool) *SourceSpecApplyConfiguration { + b.InstrumentationDisabled = &value + return b +} diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index b58d80055..df39b9b1d 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -50,6 +50,9 @@ type SourceSpec struct { // This field is required upon creation and cannot be modified. // +kubebuilder:validation:Required Workload workload.PodWorkload `json:"workload"` + // InstrumentationDisabled excludes this workload from auto-instrumentation. + // +kubebuilder:validation:Optional + InstrumentationDisabled bool `json:"instrumentationDisabled,omitempty"` } type SourceStatus struct { @@ -123,13 +126,9 @@ func GetSources(ctx context.Context, kubeClient client.Client, obj client.Object return workloadSources, nil } -// IsWorkloadExcludedSource returns true if the Source is used to exclude a workload. -// Otherwise, it returns false. +// IsWorkloadExcludedSource returns true if the Source is disabling instrumentation. func IsWorkloadExcludedSource(source *Source) bool { - if val, exists := source.Labels[consts.OdigosWorkloadExcludedLabel]; exists && val == "true" { - return true - } - return false + return source.Spec.InstrumentationDisabled } func init() { diff --git a/helm/odigos/templates/crds/odigos.io_sources.yaml b/helm/odigos/templates/crds/odigos.io_sources.yaml index c59f98ae2..69461c4c4 100644 --- a/helm/odigos/templates/crds/odigos.io_sources.yaml +++ b/helm/odigos/templates/crds/odigos.io_sources.yaml @@ -48,6 +48,9 @@ spec: type: object spec: properties: + instrumentationDisabled: + description: InstrumentationDisabled excludes this workload from auto-instrumentation. + type: boolean workload: description: |- Workload represents the workload or namespace to be instrumented. diff --git a/k8sutils/pkg/consts/consts.go b/k8sutils/pkg/consts/consts.go index 17f4f8bf3..1546c3a36 100644 --- a/k8sutils/pkg/consts/consts.go +++ b/k8sutils/pkg/consts/consts.go @@ -78,7 +78,6 @@ const ( WorkloadNameLabel = "odigos.io/workload-name" WorkloadNamespaceLabel = "odigos.io/workload-namespace" WorkloadKindLabel = "odigos.io/workload-kind" - OdigosWorkloadExcludedLabel = "odigos.io/workload-excluded" OdigosCloudApiKeySecretKey = "odigos-cloud-api-key" OdigosOnpremTokenSecretKey = "odigos-onprem-token" diff --git a/tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml b/tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml index 3411af6e0..8ab38521a 100644 --- a/tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml +++ b/tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml @@ -3,11 +3,10 @@ kind: Source metadata: name: 05-startlangdetection-finalizer namespace: default - labels: - odigos.io/workload-excluded: "true" finalizers: - odigos.io/source-startlangdetection-finalizer spec: + instrumentationDisabled: true workload: name: startlangdetection-finalizer namespace: default diff --git a/tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml b/tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml index 679bd4d9e..80254f41a 100644 --- a/tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml +++ b/tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml @@ -3,9 +3,8 @@ kind: Source metadata: name: 05-startlangdetection-finalizer namespace: default - labels: - odigos.io/workload-excluded: "true" spec: + instrumentationDisabled: true workload: name: startlangdetection-finalizer namespace: default diff --git a/tests/e2e/source-webhooks/07-excluded-source.yaml b/tests/e2e/source-webhooks/07-excluded-source.yaml index ae152cbcf..c52a93a56 100644 --- a/tests/e2e/source-webhooks/07-excluded-source.yaml +++ b/tests/e2e/source-webhooks/07-excluded-source.yaml @@ -3,9 +3,8 @@ kind: Source metadata: name: 07-excluded-source namespace: default - labels: - odigos.io/workload-excluded: "true" spec: + instrumentationDisabled: true workload: name: 07-excluded-source namespace: default diff --git a/tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml b/tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml index 0aa229b0b..77cbdb533 100644 --- a/tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml +++ b/tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml @@ -3,9 +3,8 @@ kind: Source metadata: name: 10-duplicate-excluded-source namespace: default - labels: - odigos.io/workload-excluded: "true" spec: + instrumentationDisabled: true workload: name: 07-excluded-source namespace: default diff --git a/tests/e2e/source/08-source.yaml b/tests/e2e/source/08-source.yaml index 8aa6d43b5..5815c10c0 100644 --- a/tests/e2e/source/08-source.yaml +++ b/tests/e2e/source/08-source.yaml @@ -5,8 +5,8 @@ metadata: namespace: default labels: odigos.io/e2e: source-excluded - odigos.io/workload-excluded: "true" spec: + instrumentationDisabled: true workload: name: coupon namespace: default diff --git a/tests/e2e/source/09-source.yaml b/tests/e2e/source/09-source.yaml index 19845f01b..3fe5e6717 100644 --- a/tests/e2e/source/09-source.yaml +++ b/tests/e2e/source/09-source.yaml @@ -5,8 +5,8 @@ metadata: namespace: default labels: odigos.io/e2e: source-excluded - odigos.io/workload-excluded: "true" spec: + instrumentationDisabled: true workload: name: membership namespace: default From 24f23825d3569b068d55984b3e81988b93a2e025 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Thu, 9 Jan 2025 14:47:31 -0500 Subject: [PATCH 213/259] Support toggling instrumentationDisabled/finalizers in Defaulter + tests --- instrumentor/sources_webhooks.go | 36 ++++++++++--- tests/e2e/source-webhooks/chainsaw-test.yaml | 54 ++++++++++++------- ...ting-01-assert-source-without-labels.yaml} | 0 ... defaulting-01-source-without-labels.yaml} | 0 ... => defaulting-02-source-with-labels.yaml} | 0 ...-assert-source-with-incorrect-labels.yaml} | 0 ...ting-03-source-with-incorrect-labels.yaml} | 0 ...-assert-delete-to-startlang-finalizer.yaml | 12 +++++ ...eleteinstrumentationconfig-finalizer.yaml} | 0 ...eleteinstrumentationconfig-finalizer.yaml} | 0 ...-assert-startlang-to-delete-finalizer.yaml | 12 +++++ ...-assert-startlangdetection-finalizer.yaml} | 0 ...ting-05-startlangdetection-finalizer.yaml} | 0 ....yaml => validating-01-normal-source.yaml} | 0 ...aml => validating-02-excluded-source.yaml} | 0 ...ml => validating-03-namespace-source.yaml} | 0 ...alidating-04-duplicate-normal-source.yaml} | 0 ...idating-05-duplicate-excluded-source.yaml} | 0 ...dating-06-duplicate-namespace-source.yaml} | 0 ...07-valid-workload-existing-ns-source.yaml} | 0 ...ing-08-invalid-namespace-name-source.yaml} | 0 ...ting-09-invalid-workload-kind-source.yaml} | 0 ...-namespace-workload-namespace-source.yaml} | 0 tests/e2e/source/10-workloads.yaml | 44 +++++++++++++++ tests/e2e/source/README.md | 2 + tests/e2e/source/chainsaw-test.yaml | 45 +++++++++++++++- 26 files changed, 177 insertions(+), 28 deletions(-) rename tests/e2e/source-webhooks/{01-assert-source-without-labels.yaml => defaulting-01-assert-source-without-labels.yaml} (100%) rename tests/e2e/source-webhooks/{01-source-without-labels.yaml => defaulting-01-source-without-labels.yaml} (100%) rename tests/e2e/source-webhooks/{02-source-with-labels.yaml => defaulting-02-source-with-labels.yaml} (100%) rename tests/e2e/source-webhooks/{03-assert-source-with-incorrect-labels.yaml => defaulting-03-assert-source-with-incorrect-labels.yaml} (100%) rename tests/e2e/source-webhooks/{03-source-with-incorrect-labels.yaml => defaulting-03-source-with-incorrect-labels.yaml} (100%) create mode 100644 tests/e2e/source-webhooks/defaulting-04-assert-delete-to-startlang-finalizer.yaml rename tests/e2e/source-webhooks/{04-assert-deleteinstrumentationconfig-finalizer.yaml => defaulting-04-assert-deleteinstrumentationconfig-finalizer.yaml} (100%) rename tests/e2e/source-webhooks/{04-deleteinstrumentationconfig-finalizer.yaml => defaulting-04-deleteinstrumentationconfig-finalizer.yaml} (100%) create mode 100644 tests/e2e/source-webhooks/defaulting-05-assert-startlang-to-delete-finalizer.yaml rename tests/e2e/source-webhooks/{05-assert-startlangdetection-finalizer.yaml => defaulting-05-assert-startlangdetection-finalizer.yaml} (100%) rename tests/e2e/source-webhooks/{05-startlangdetection-finalizer.yaml => defaulting-05-startlangdetection-finalizer.yaml} (100%) rename tests/e2e/source-webhooks/{06-normal-source.yaml => validating-01-normal-source.yaml} (100%) rename tests/e2e/source-webhooks/{07-excluded-source.yaml => validating-02-excluded-source.yaml} (100%) rename tests/e2e/source-webhooks/{08-namespace-source.yaml => validating-03-namespace-source.yaml} (100%) rename tests/e2e/source-webhooks/{09-duplicate-normal-source.yaml => validating-04-duplicate-normal-source.yaml} (100%) rename tests/e2e/source-webhooks/{10-duplicate-excluded-source.yaml => validating-05-duplicate-excluded-source.yaml} (100%) rename tests/e2e/source-webhooks/{11-duplicate-namespace-source.yaml => validating-06-duplicate-namespace-source.yaml} (100%) rename tests/e2e/source-webhooks/{12-valid-workload-existing-ns-source.yaml => validating-07-valid-workload-existing-ns-source.yaml} (100%) rename tests/e2e/source-webhooks/{13-invalid-namespace-name-source.yaml => validating-08-invalid-namespace-name-source.yaml} (100%) rename tests/e2e/source-webhooks/{14-invalid-workload-kind-source.yaml => validating-09-invalid-workload-kind-source.yaml} (100%) rename tests/e2e/source-webhooks/{15-namespace-workload-namespace-source.yaml => validating-10-namespace-workload-namespace-source.yaml} (100%) create mode 100644 tests/e2e/source/10-workloads.yaml diff --git a/instrumentor/sources_webhooks.go b/instrumentor/sources_webhooks.go index ca870ca9c..f7a9e120c 100644 --- a/instrumentor/sources_webhooks.go +++ b/instrumentor/sources_webhooks.go @@ -63,15 +63,26 @@ func (s *SourcesDefaulter) Default(ctx context.Context, obj runtime.Object) erro source.Labels[consts.WorkloadKindLabel] = string(source.Spec.Workload.Kind) } - if !v1alpha1.IsWorkloadExcludedSource(source) && - !k8sutils.IsTerminating(source) && - !controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { - controllerutil.AddFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) + // Make sure the Source has the right finalizer, so the right controller handles it for deletion. + // If a normal source has `spec.instrumentationDisabled` updated to `true`, it is now an excluded Source. + // Vice versa for an excluded Source that has `spec.instrumentationDisabled` removed. + // These checks make sure that the right type of Source has the right type of finalizer + // by toggling what finalizer is set. + if !v1alpha1.IsWorkloadExcludedSource(source) { + if !controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) && !k8sutils.IsTerminating(source) { + controllerutil.AddFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) + } + if controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { + controllerutil.RemoveFinalizer(source, consts.StartLangDetectionFinalizer) + } } - if v1alpha1.IsWorkloadExcludedSource(source) && - !k8sutils.IsTerminating(source) && - !controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { - controllerutil.AddFinalizer(source, consts.StartLangDetectionFinalizer) + if v1alpha1.IsWorkloadExcludedSource(source) { + if controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) { + controllerutil.RemoveFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) + } + if !controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) && !k8sutils.IsTerminating(source) { + controllerutil.AddFinalizer(source, consts.StartLangDetectionFinalizer) + } } return nil @@ -181,6 +192,15 @@ func (s *SourcesValidator) ValidateDelete(ctx context.Context, obj runtime.Objec func (s *SourcesValidator) validateSourceFields(ctx context.Context, source *v1alpha1.Source) field.ErrorList { allErrs := make([]*field.Error, 0) + if controllerutil.ContainsFinalizer(source, consts.DeleteInstrumentationConfigFinalizer) && + controllerutil.ContainsFinalizer(source, consts.StartLangDetectionFinalizer) { + allErrs = append(allErrs, field.Invalid( + field.NewPath("metadata").Child("finalizers"), + source.Finalizers, + "Source may only have one finalizer", + )) + } + if source.Labels[consts.WorkloadNameLabel] != source.Spec.Workload.Name { allErrs = append(allErrs, field.Invalid( field.NewPath("metadata").Child("labels"), diff --git a/tests/e2e/source-webhooks/chainsaw-test.yaml b/tests/e2e/source-webhooks/chainsaw-test.yaml index dabeb79b9..72e7b38ce 100644 --- a/tests/e2e/source-webhooks/chainsaw-test.yaml +++ b/tests/e2e/source-webhooks/chainsaw-test.yaml @@ -21,31 +21,39 @@ spec: - name: "[Defaulting] - Source without labels has labels added" try: - apply: - file: 01-source-without-labels.yaml + file: defaulting-01-source-without-labels.yaml - assert: timeout: 2m - file: 01-assert-source-without-labels.yaml + file: defaulting-01-assert-source-without-labels.yaml - name: "[Defaulting] - Source with correct labels is unaffected" try: - apply: - file: 02-source-with-labels.yaml + file: defaulting-02-source-with-labels.yaml - assert: timeout: 2m - file: 02-source-with-labels.yaml + file: defaulting-02-source-with-labels.yaml - name: "[Defaulting/Validating] - Source with incorrect labels is rejected (not modified to be correct)" try: - apply: - file: 03-source-with-incorrect-labels.yaml + file: defaulting-03-source-with-incorrect-labels.yaml expect: - check: ($error != null): true - name: "[Defaulting] - Normal Source has deleteinstrumentationconfig finalizer added" try: - apply: - file: 04-deleteinstrumentationconfig-finalizer.yaml + file: defaulting-04-deleteinstrumentationconfig-finalizer.yaml - assert: timeout: 2m - file: 04-assert-deleteinstrumentationconfig-finalizer.yaml + file: defaulting-04-assert-deleteinstrumentationconfig-finalizer.yaml + - name: "[Defaulting] - Normal Source updated to instrumentationDisabled=true has deleteinstrumentationconfig finalizer removed and startlangdetection finalizer added" + try: + - script: + timeout: 70s + content: kubectl patch source/04-deleteinstrumentationconfig-finalizer --type=merge -p '{"spec":{"instrumentationDisabled":true}}' + - assert: + timeout: 2m + file: defaulting-04-assert-delete-to-startlang-finalizer.yaml - name: "[Defaulting] - Finalizer does not block normal Source from being deleted" try: - script: @@ -56,10 +64,18 @@ spec: - name: "[Defaulting] - Workload excluded Source has startlangdetection finalizer added" try: - apply: - file: 05-startlangdetection-finalizer.yaml + file: defaulting-05-startlangdetection-finalizer.yaml + - assert: + timeout: 2m + file: defaulting-05-assert-startlangdetection-finalizer.yaml + - name: "[Defaulting] - Workload excluded Source updated to instrumentationDisabled=false has deleteinstrumentationconfig finalizer added and startlangdetection finalizer removed" + try: + - script: + timeout: 70s + content: kubectl patch source/05-startlangdetection-finalizer --type=merge -p '{"spec":{"instrumentationDisabled":false}}' - assert: timeout: 2m - file: 05-assert-startlangdetection-finalizer.yaml + file: defaulting-05-assert-startlang-to-delete-finalizer.yaml - name: "[Defaulting] - Finalizer does not block exclusion Source from being deleted" try: - script: @@ -70,51 +86,51 @@ spec: - name: "[Validating] - Normal Source passes validation" try: - apply: - file: 06-normal-source.yaml + file: validating-01-normal-source.yaml - name: "[Validating] - Workload excluded Source passes validation" try: - apply: - file: 07-excluded-source.yaml + file: validating-02-excluded-source.yaml - name: "[Validating] - Namespace Source passes validation + Namespace Source with existing workload Source is not a duplicate (valid)" try: - apply: - file: 08-namespace-source.yaml + file: validating-03-namespace-source.yaml - name: "[Validating] - Duplicate normal Source does not pass validation" try: - apply: - file: 09-duplicate-normal-source.yaml + file: validating-04-duplicate-normal-source.yaml expect: - check: ($error != null): true - name: "[Validating] - Duplicate workload excluded Source does not pass validation" try: - apply: - file: 10-duplicate-excluded-source.yaml + file: validating-05-duplicate-excluded-source.yaml expect: - check: ($error != null): true - name: "[Validating] - Duplicate namespace Source does not pass validation" try: - apply: - file: 11-duplicate-namespace-source.yaml + file: validating-06-duplicate-namespace-source.yaml expect: - check: ($error != null): true - name: "[Validating] - Workload Source with existing namespace Source is not a duplicate (valid)" try: - apply: - file: 12-valid-workload-existing-ns-source.yaml + file: validating-07-valid-workload-existing-ns-source.yaml - name: "[Validating] - Namespace Source with name!=namespace does not pass validation" try: - apply: - file: 13-invalid-namespace-name-source.yaml + file: validating-08-invalid-namespace-name-source.yaml expect: - check: ($error != null): true - name: "[Validating] - Source with invalid kind does not pass validation" try: - apply: - file: 14-invalid-workload-kind-source.yaml + file: validating-09-invalid-workload-kind-source.yaml expect: - check: ($error != null): true @@ -169,7 +185,7 @@ spec: - name: "[Validating] - Source namespace must match source.spec.workload.namespace" try: - apply: - file: 15-namespace-workload-namespace-source.yaml + file: validating-10-namespace-workload-namespace-source.yaml expect: - check: ($error != null): true diff --git a/tests/e2e/source-webhooks/01-assert-source-without-labels.yaml b/tests/e2e/source-webhooks/defaulting-01-assert-source-without-labels.yaml similarity index 100% rename from tests/e2e/source-webhooks/01-assert-source-without-labels.yaml rename to tests/e2e/source-webhooks/defaulting-01-assert-source-without-labels.yaml diff --git a/tests/e2e/source-webhooks/01-source-without-labels.yaml b/tests/e2e/source-webhooks/defaulting-01-source-without-labels.yaml similarity index 100% rename from tests/e2e/source-webhooks/01-source-without-labels.yaml rename to tests/e2e/source-webhooks/defaulting-01-source-without-labels.yaml diff --git a/tests/e2e/source-webhooks/02-source-with-labels.yaml b/tests/e2e/source-webhooks/defaulting-02-source-with-labels.yaml similarity index 100% rename from tests/e2e/source-webhooks/02-source-with-labels.yaml rename to tests/e2e/source-webhooks/defaulting-02-source-with-labels.yaml diff --git a/tests/e2e/source-webhooks/03-assert-source-with-incorrect-labels.yaml b/tests/e2e/source-webhooks/defaulting-03-assert-source-with-incorrect-labels.yaml similarity index 100% rename from tests/e2e/source-webhooks/03-assert-source-with-incorrect-labels.yaml rename to tests/e2e/source-webhooks/defaulting-03-assert-source-with-incorrect-labels.yaml diff --git a/tests/e2e/source-webhooks/03-source-with-incorrect-labels.yaml b/tests/e2e/source-webhooks/defaulting-03-source-with-incorrect-labels.yaml similarity index 100% rename from tests/e2e/source-webhooks/03-source-with-incorrect-labels.yaml rename to tests/e2e/source-webhooks/defaulting-03-source-with-incorrect-labels.yaml diff --git a/tests/e2e/source-webhooks/defaulting-04-assert-delete-to-startlang-finalizer.yaml b/tests/e2e/source-webhooks/defaulting-04-assert-delete-to-startlang-finalizer.yaml new file mode 100644 index 000000000..6fbb8f23c --- /dev/null +++ b/tests/e2e/source-webhooks/defaulting-04-assert-delete-to-startlang-finalizer.yaml @@ -0,0 +1,12 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 04-deleteinstrumentationconfig-finalizer + namespace: default + finalizers: + - odigos.io/source-startlangdetection-finalizer +spec: + workload: + name: deleteinstrumentationconfig-finalizer + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/04-assert-deleteinstrumentationconfig-finalizer.yaml b/tests/e2e/source-webhooks/defaulting-04-assert-deleteinstrumentationconfig-finalizer.yaml similarity index 100% rename from tests/e2e/source-webhooks/04-assert-deleteinstrumentationconfig-finalizer.yaml rename to tests/e2e/source-webhooks/defaulting-04-assert-deleteinstrumentationconfig-finalizer.yaml diff --git a/tests/e2e/source-webhooks/04-deleteinstrumentationconfig-finalizer.yaml b/tests/e2e/source-webhooks/defaulting-04-deleteinstrumentationconfig-finalizer.yaml similarity index 100% rename from tests/e2e/source-webhooks/04-deleteinstrumentationconfig-finalizer.yaml rename to tests/e2e/source-webhooks/defaulting-04-deleteinstrumentationconfig-finalizer.yaml diff --git a/tests/e2e/source-webhooks/defaulting-05-assert-startlang-to-delete-finalizer.yaml b/tests/e2e/source-webhooks/defaulting-05-assert-startlang-to-delete-finalizer.yaml new file mode 100644 index 000000000..5545ce3bf --- /dev/null +++ b/tests/e2e/source-webhooks/defaulting-05-assert-startlang-to-delete-finalizer.yaml @@ -0,0 +1,12 @@ +apiVersion: odigos.io/v1alpha1 +kind: Source +metadata: + name: 05-startlangdetection-finalizer + namespace: default + finalizers: + - odigos.io/source-deleteinstrumentationconfig-finalizer +spec: + workload: + name: startlangdetection-finalizer + namespace: default + kind: Deployment \ No newline at end of file diff --git a/tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml b/tests/e2e/source-webhooks/defaulting-05-assert-startlangdetection-finalizer.yaml similarity index 100% rename from tests/e2e/source-webhooks/05-assert-startlangdetection-finalizer.yaml rename to tests/e2e/source-webhooks/defaulting-05-assert-startlangdetection-finalizer.yaml diff --git a/tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml b/tests/e2e/source-webhooks/defaulting-05-startlangdetection-finalizer.yaml similarity index 100% rename from tests/e2e/source-webhooks/05-startlangdetection-finalizer.yaml rename to tests/e2e/source-webhooks/defaulting-05-startlangdetection-finalizer.yaml diff --git a/tests/e2e/source-webhooks/06-normal-source.yaml b/tests/e2e/source-webhooks/validating-01-normal-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/06-normal-source.yaml rename to tests/e2e/source-webhooks/validating-01-normal-source.yaml diff --git a/tests/e2e/source-webhooks/07-excluded-source.yaml b/tests/e2e/source-webhooks/validating-02-excluded-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/07-excluded-source.yaml rename to tests/e2e/source-webhooks/validating-02-excluded-source.yaml diff --git a/tests/e2e/source-webhooks/08-namespace-source.yaml b/tests/e2e/source-webhooks/validating-03-namespace-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/08-namespace-source.yaml rename to tests/e2e/source-webhooks/validating-03-namespace-source.yaml diff --git a/tests/e2e/source-webhooks/09-duplicate-normal-source.yaml b/tests/e2e/source-webhooks/validating-04-duplicate-normal-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/09-duplicate-normal-source.yaml rename to tests/e2e/source-webhooks/validating-04-duplicate-normal-source.yaml diff --git a/tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml b/tests/e2e/source-webhooks/validating-05-duplicate-excluded-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/10-duplicate-excluded-source.yaml rename to tests/e2e/source-webhooks/validating-05-duplicate-excluded-source.yaml diff --git a/tests/e2e/source-webhooks/11-duplicate-namespace-source.yaml b/tests/e2e/source-webhooks/validating-06-duplicate-namespace-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/11-duplicate-namespace-source.yaml rename to tests/e2e/source-webhooks/validating-06-duplicate-namespace-source.yaml diff --git a/tests/e2e/source-webhooks/12-valid-workload-existing-ns-source.yaml b/tests/e2e/source-webhooks/validating-07-valid-workload-existing-ns-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/12-valid-workload-existing-ns-source.yaml rename to tests/e2e/source-webhooks/validating-07-valid-workload-existing-ns-source.yaml diff --git a/tests/e2e/source-webhooks/13-invalid-namespace-name-source.yaml b/tests/e2e/source-webhooks/validating-08-invalid-namespace-name-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/13-invalid-namespace-name-source.yaml rename to tests/e2e/source-webhooks/validating-08-invalid-namespace-name-source.yaml diff --git a/tests/e2e/source-webhooks/14-invalid-workload-kind-source.yaml b/tests/e2e/source-webhooks/validating-09-invalid-workload-kind-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/14-invalid-workload-kind-source.yaml rename to tests/e2e/source-webhooks/validating-09-invalid-workload-kind-source.yaml diff --git a/tests/e2e/source-webhooks/15-namespace-workload-namespace-source.yaml b/tests/e2e/source-webhooks/validating-10-namespace-workload-namespace-source.yaml similarity index 100% rename from tests/e2e/source-webhooks/15-namespace-workload-namespace-source.yaml rename to tests/e2e/source-webhooks/validating-10-namespace-workload-namespace-source.yaml diff --git a/tests/e2e/source/10-workloads.yaml b/tests/e2e/source/10-workloads.yaml new file mode 100644 index 000000000..96e405df3 --- /dev/null +++ b/tests/e2e/source/10-workloads.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: coupon + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "6" + generation: 8 + name: frontend + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: inventory + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "10" + generation: 12 + name: membership + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "8" + generation: 10 + name: pricing + namespace: default \ No newline at end of file diff --git a/tests/e2e/source/README.md b/tests/e2e/source/README.md index cbc8e2b14..1bba37c62 100644 --- a/tests/e2e/source/README.md +++ b/tests/e2e/source/README.md @@ -41,6 +41,8 @@ It has the following phases: 9. **Workload exclusion (2)** - Create an Excluded Source in an already-instrumented namespace. Verify: 9.1 Only the newly excluded workload rolls out a new revision. 9.2 InstrumentationConfigs exist for all workloads except newly excluded workload. + 9.3 Setting instrumentationDisabled=false on excluded workload includes it + 9.4 Setting instrumentationDisabled=true on included workload excludes it ## Workload generations and revisions diff --git a/tests/e2e/source/chainsaw-test.yaml b/tests/e2e/source/chainsaw-test.yaml index 81f108514..53a5065b1 100644 --- a/tests/e2e/source/chainsaw-test.yaml +++ b/tests/e2e/source/chainsaw-test.yaml @@ -453,4 +453,47 @@ spec: content: kubectl get instrumentationconfigs/deployment-membership check: ($error != null): true - + - name: Set instrumentationDisabled=false on excluded Source + try: + - script: + timeout: 70s + content: kubectl patch source/membership-excluded --type=merge -p '{"spec":{"instrumentationDisabled":false}}' + - name: Wait for updated source to roll out new revisions + try: + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Assert runtime detected for no-longer-excluded workload + try: + - assert: + timeout: 2m + file: ../../common/assert/simple-demo-runtime-detected.yaml + - assert: + timeout: 2m + file: 10-workloads.yaml + - name: Set instrumentationDisabled=true on previously-excluded Source + try: + - script: + timeout: 70s + content: kubectl patch source/membership-excluded --type=merge -p '{"spec":{"instrumentationDisabled":true}}' + - name: Wait for updated source to roll out new revisions + try: + - script: + timeout: 70s + content: | + kubectl rollout status deployment -l app=coupon + kubectl rollout status deployment -l app=frontend + kubectl rollout status deployment -l app=inventory + kubectl rollout status deployment -l app=pricing + kubectl rollout status deployment -l app=membership + - name: Assert runtime not detected for re-excluded (membership) + try: + - script: + content: kubectl get instrumentationconfigs/deployment-membership + check: + ($error != null): true From 72b7cff085da0782bf6fc5d392e307f93ca152aa Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Fri, 10 Jan 2025 15:42:26 +0200 Subject: [PATCH 214/259] chore: remove duplicate consts, use from k8sutils --- common/consts/consts.go | 3 --- frontend/services/sources.go | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/common/consts/consts.go b/common/consts/consts.go index 540088447..64bd69c89 100644 --- a/common/consts/consts.go +++ b/common/consts/consts.go @@ -15,9 +15,6 @@ const ( OdigosInstrumentationLabel = "odigos-instrumentation" InstrumentationEnabled = "enabled" InstrumentationDisabled = "disabled" - OdigosNamespaceAnnotation = "odigos.io/workload-namespace" - OdigosWorkloadKindAnnotation = "odigos.io/workload-kind" - OdigosWorkloadNameAnnotation = "odigos.io/workload-name" OdigosReportedNameAnnotation = "odigos.io/reported-name" RolloutTriggerAnnotation = "rollout-trigger" diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 9dda39229..4e97d329e 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -11,6 +11,7 @@ import ( "github.com/odigos-io/odigos/frontend/graph/model" "github.com/odigos-io/odigos/frontend/kube" "github.com/odigos-io/odigos/k8sutils/pkg/client" + k8sconsts "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" appsv1 "k8s.io/api/apps/v1" @@ -269,9 +270,9 @@ func updateAnnotations(annotations map[string]string, reportedName string) map[s func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) (*v1alpha1.Source, error) { list, err := kube.DefaultClient.OdigosClient.Sources(nsName).List(ctx, metav1.ListOptions{ LabelSelector: labels.SelectorFromSet(labels.Set{ - consts.OdigosNamespaceAnnotation: nsName, - consts.OdigosWorkloadNameAnnotation: workloadName, - consts.OdigosWorkloadKindAnnotation: string(workloadKind), + k8sconsts.WorkloadNamespaceLabel: nsName, + k8sconsts.WorkloadNameLabel: workloadName, + k8sconsts.WorkloadKindLabel: string(workloadKind), }).String(), }) From 90767bf860511ef4d71ae45ef0f96be75f7baa2b Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Fri, 10 Jan 2025 15:53:39 +0200 Subject: [PATCH 215/259] refactor: simplify GetSourceCRD to return the first source item directly --- frontend/services/sources.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/services/sources.go b/frontend/services/sources.go index 4e97d329e..fe0b6ff55 100644 --- a/frontend/services/sources.go +++ b/frontend/services/sources.go @@ -286,10 +286,7 @@ func GetSourceCRD(ctx context.Context, nsName string, workloadName string, workl return nil, fmt.Errorf(`expected to get 1 source "%s", got %d`, workloadName, len(list.Items)) } - crdName := list.Items[0].Name - crd, err := kube.DefaultClient.OdigosClient.Sources(nsName).Get(ctx, crdName, metav1.GetOptions{}) - - return crd, err + return &list.Items[0], err } func createSourceCRD(ctx context.Context, nsName string, workloadName string, workloadKind WorkloadKind) error { From 9d7a6cac02253152b706cb939ddf53c6b48b41af Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Fri, 10 Jan 2025 10:00:16 -0500 Subject: [PATCH 216/259] Add client.InNamespace to GetSources --- api/odigos/v1alpha1/source_types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/odigos/v1alpha1/source_types.go b/api/odigos/v1alpha1/source_types.go index df39b9b1d..b18430a20 100644 --- a/api/odigos/v1alpha1/source_types.go +++ b/api/odigos/v1alpha1/source_types.go @@ -94,7 +94,7 @@ func GetSources(ctx context.Context, kubeClient client.Client, obj client.Object consts.WorkloadNamespaceLabel: obj.GetNamespace(), consts.WorkloadKindLabel: obj.GetObjectKind().GroupVersionKind().Kind, }) - err := kubeClient.List(ctx, &sourceList, &client.ListOptions{LabelSelector: selector}) + err := kubeClient.List(ctx, &sourceList, &client.ListOptions{LabelSelector: selector}, client.InNamespace(obj.GetNamespace())) if err != nil { return nil, err } @@ -112,7 +112,7 @@ func GetSources(ctx context.Context, kubeClient client.Client, obj client.Object consts.WorkloadNamespaceLabel: obj.GetNamespace(), consts.WorkloadKindLabel: "Namespace", }) - err = kubeClient.List(ctx, &namespaceSourceList, &client.ListOptions{LabelSelector: namespaceSelector}) + err = kubeClient.List(ctx, &namespaceSourceList, &client.ListOptions{LabelSelector: namespaceSelector}, client.InNamespace(obj.GetNamespace())) if err != nil { return nil, err } From b25e8ac0639a5bb6abd98ee00ddf32596b95a3cf Mon Sep 17 00:00:00 2001 From: Ben Elferink Date: Sun, 12 Jan 2025 13:08:09 +0200 Subject: [PATCH 217/259] refactor: frontend server handlers --- frontend/main.go | 70 ++++++++++++++++++----------------- frontend/services/describe.go | 7 +++- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/frontend/main.go b/frontend/main.go index d89ba2380..59593733f 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -14,29 +14,24 @@ import ( "sync" "syscall" - "github.com/go-logr/logr" + _ "net/http/pprof" + "github.com/99designs/gqlgen/graphql/handler" + "github.com/99designs/gqlgen/graphql/playground" + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + "github.com/go-logr/logr" "github.com/odigos-io/odigos/common" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/destinations" - "github.com/odigos-io/odigos/k8sutils/pkg/env" - - "github.com/gin-contrib/cors" - + "github.com/odigos-io/odigos/frontend/graph" "github.com/odigos-io/odigos/frontend/kube" "github.com/odigos-io/odigos/frontend/kube/watchers" "github.com/odigos-io/odigos/frontend/services" collectormetrics "github.com/odigos-io/odigos/frontend/services/collector_metrics" "github.com/odigos-io/odigos/frontend/services/sse" "github.com/odigos-io/odigos/frontend/version" - - "github.com/gin-gonic/gin" - - _ "net/http/pprof" - - "github.com/99designs/gqlgen/graphql/handler" - "github.com/99designs/gqlgen/graphql/playground" - "github.com/odigos-io/odigos/frontend/graph" + "github.com/odigos-io/odigos/k8sutils/pkg/env" ) const ( @@ -109,17 +104,22 @@ func startHTTPServer(flags *Flags, odigosMetrics *collectormetrics.OdigosMetrics MetricsConsumer: odigosMetrics, }, })) - r.POST("/graphql", func(c *gin.Context) { gqlHandler.ServeHTTP(c.Writer, c.Request) }) r.GET("/playground", gin.WrapH(playground.Handler("GraphQL Playground", "/graphql"))) + // SSE handler + r.GET("/api/events", sse.HandleSSEConnections) + + // Remote CLI handlers + r.GET("/describe/odigos", services.DescribeOdigos) + r.GET("/describe/source/namespace/:namespace/kind/:kind/name/:name", services.DescribeSource) + return r, nil } func httpFileServerWith404(fs http.FileSystem) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := fs.Open(r.URL.Path) if err != nil { @@ -135,6 +135,25 @@ func httpFileServerWith404(fs http.FileSystem) http.Handler { }) } +func startWatchers(ctx context.Context, flags *Flags) error { + err := watchers.StartInstrumentationConfigWatcher(ctx, "") + if err != nil { + return fmt.Errorf("error starting InstrumentationConfig watcher: %v", err) + } + + err = watchers.StartDestinationWatcher(ctx, flags.Namespace) + if err != nil { + return fmt.Errorf("error starting Destination watcher: %v", err) + } + + err = watchers.StartInstrumentationInstanceWatcher(ctx, "") + if err != nil { + return fmt.Errorf("error starting InstrumentationInstance watcher: %v", err) + } + + return nil +} + func main() { flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) flags := parseFlags() @@ -181,28 +200,11 @@ func main() { } // Start watchers - err = watchers.StartInstrumentationConfigWatcher(ctx, "") - if err != nil { - log.Printf("Error starting InstrumentationConfig watcher: %v", err) - } - - err = watchers.StartDestinationWatcher(ctx, flags.Namespace) - if err != nil { - log.Printf("Error starting Destination watcher: %v", err) - } - - err = watchers.StartInstrumentationInstanceWatcher(ctx, "") + err = startWatchers(ctx, &flags) if err != nil { - log.Printf("Error starting InstrumentationInstance watcher: %v", err) + log.Fatalf("Error starting watchers: %s", err) } - r.GET("/api/events", sse.HandleSSEConnections) - r.GET("/describe/odigos", func(c *gin.Context) { - services.DescribeOdigos(c) - }) - r.GET("/describe/source/namespace/:namespace/kind/:kind/name/:name", func(c *gin.Context) { - services.DescribeSource(c, c.Param("namespace"), c.Param("kind"), c.Param("name")) - }) log.Printf("Odigos UI is available at: http://%s:%d", flags.Address, flags.Port) go func() { err = r.Run(fmt.Sprintf("%s:%d", flags.Address, flags.Port)) diff --git a/frontend/services/describe.go b/frontend/services/describe.go index 0f06d8a74..0761aff5d 100644 --- a/frontend/services/describe.go +++ b/frontend/services/describe.go @@ -34,8 +34,13 @@ func DescribeOdigos(c *gin.Context) { } } -func DescribeSource(c *gin.Context, ns string, kind string, name string) { +func DescribeSource(c *gin.Context) { ctx := c.Request.Context() + + ns := c.Param("namespace") + name := c.Param("name") + kind := c.Param("kind") + var desc *source.SourceAnalyze var err error switch kind { From 86274ebea3eba0e21ec18c5c019caa4f6c0b485b Mon Sep 17 00:00:00 2001 From: Avihu Hanya <34632558+AvihuHenya@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:33:02 +0200 Subject: [PATCH 218/259] feat: cli remote update token (#2173) declaring remote flag in cli pro command for enabling update token via Odigos ui --------- Co-authored-by: Ben Elferink --- cli/cmd/describe.go | 15 +- cli/cmd/pro.go | 63 +- cli/cmd/resources/ui.go | 5 +- frontend/gqlgen.yml | 2 + frontend/graph/common.go | 30 + frontend/graph/generated.go | 549 +++ frontend/graph/model/models_gen.go | 10 + frontend/graph/schema.graphqls | 10 + frontend/graph/schema.resolvers.go | 41 + .../odigos_describe/odigos_describe.go | 2 + .../webapp/assets/icons/common/copy-icon.tsx | 16 + frontend/webapp/assets/icons/common/index.ts | 3 + .../webapp/assets/icons/common/key-icon.tsx | 16 + .../assets/icons/common/terminal-icon.tsx | 16 + .../webapp/components/main/header/index.tsx | 13 +- .../notification/notification-manager.tsx | 2 +- .../overview/all-drawers/cli-drawer.tsx | 107 + .../overview/all-drawers/describe-drawer.tsx | 51 - .../components/overview/all-drawers/index.tsx | 6 +- .../dynamic-fields/index.tsx | 22 +- .../graphql/queries/compute-platform.ts | 6 + frontend/webapp/graphql/queries/describe.ts | 12 + frontend/webapp/graphql/queries/index.ts | 2 +- frontend/webapp/hooks/common/index.ts | 1 + frontend/webapp/hooks/common/useCopy.ts | 22 + .../webapp/hooks/common/useOnClickOutside.ts | 5 +- .../hooks/describe/useDescribeOdigos.ts | 4 +- .../destinations/useDestinationFormData.ts | 15 - frontend/webapp/package.json | 33 +- .../reuseable-components/badge/index.tsx | 3 +- .../reuseable-components/code/index.tsx | 9 +- .../data-card/data-card-fields/index.tsx | 27 +- .../reuseable-components/data-tab/index.tsx | 4 +- .../reuseable-components/dropdown/index.tsx | 26 +- .../fade-loader/index.tsx | 15 +- frontend/webapp/reuseable-components/index.ts | 1 + .../reuseable-components/input-list/index.tsx | 19 +- .../input-table/index.tsx | 22 +- .../interactive-table/index.tsx | 112 + .../key-value-input-list/index.tsx | 24 +- .../monitoring-checkboxes/index.tsx | 16 +- frontend/webapp/store/useDrawerStore.ts | 2 +- frontend/webapp/types/compute-platform.ts | 12 +- frontend/webapp/types/describe.ts | 2 + frontend/webapp/utils/constants/monitors.tsx | 52 +- frontend/webapp/utils/constants/string.tsx | 1 + .../formatters/safe-json-parse/index.ts | 8 +- frontend/webapp/yarn.lock | 3935 ++++------------- k8sutils/pkg/consts/consts.go | 5 + k8sutils/pkg/pro/common.go | 60 + 50 files changed, 2195 insertions(+), 3239 deletions(-) create mode 100644 frontend/graph/common.go create mode 100644 frontend/webapp/assets/icons/common/copy-icon.tsx create mode 100644 frontend/webapp/assets/icons/common/key-icon.tsx create mode 100644 frontend/webapp/assets/icons/common/terminal-icon.tsx create mode 100644 frontend/webapp/components/overview/all-drawers/cli-drawer.tsx delete mode 100644 frontend/webapp/components/overview/all-drawers/describe-drawer.tsx create mode 100644 frontend/webapp/hooks/common/useCopy.ts create mode 100644 frontend/webapp/reuseable-components/interactive-table/index.tsx create mode 100644 k8sutils/pkg/pro/common.go diff --git a/cli/cmd/describe.go b/cli/cmd/describe.go index eaebeb0e9..5c899e86d 100644 --- a/cli/cmd/describe.go +++ b/cli/cmd/describe.go @@ -8,6 +8,7 @@ import ( "github.com/odigos-io/odigos/cli/cmd/resources" "github.com/odigos-io/odigos/cli/pkg/kube" cmdcontext "github.com/odigos-io/odigos/cli/pkg/cmd_context" + k8sconsts "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/k8sutils/pkg/describe" "github.com/spf13/cobra" ) @@ -141,7 +142,7 @@ var describeSourceStatefulSetCmd = &cobra.Command{ } func executeRemoteOdigosDescribe(ctx context.Context, client *kube.Client, odigosNs string) string { - uiSvcProxyEndpoint := getUiServiceOdigosEndpoint(ctx, client, odigosNs) + uiSvcProxyEndpoint := fmt.Sprintf("/api/v1/namespaces/%s/services/%s:%d/proxy/api/describe/odigos", odigosNs, k8sconsts.OdigosUiServiceName, k8sconsts.OdigosUiServicePort) request := client.Clientset.RESTClient().Get().AbsPath(uiSvcProxyEndpoint).Do(ctx) response, err := request.Raw() if err != nil { @@ -162,13 +163,6 @@ func executeRemoteSourceDescribe(ctx context.Context, client *kube.Client, workl } } -func getUiServiceOdigosEndpoint(ctx context.Context, client *kube.Client, odigosNs string) string { - uiServiceName := "ui" - uiServicePort := 3000 - - return fmt.Sprintf("/api/v1/namespaces/%s/services/%s:%d/proxy/api/describe/odigos", odigosNs, uiServiceName, uiServicePort) -} - func getUiServiceSourceEndpoint(ctx context.Context, client *kube.Client, workloadKind string, workloadNs string, workloadName string) string { ns, err := resources.GetOdigosNamespace(client, ctx) if resources.IsErrNoOdigosNamespaceFound(err) { @@ -179,10 +173,7 @@ func getUiServiceSourceEndpoint(ctx context.Context, client *kube.Client, worklo os.Exit(1) } - uiServiceName := "ui" - uiServicePort := 3000 - - return fmt.Sprintf("/api/v1/namespaces/%s/services/%s:%d/proxy/api/describe/source/namespace/%s/kind/%s/name/%s", ns, uiServiceName, uiServicePort, workloadNs, workloadKind, workloadName) + return fmt.Sprintf("/api/v1/namespaces/%s/services/%s:%d/proxy/api/describe/source/namespace/%s/kind/%s/name/%s", ns, k8sconsts.OdigosUiServiceName, k8sconsts.OdigosUiServicePort, workloadNs, workloadKind, workloadName) } func init() { diff --git a/cli/cmd/pro.go b/cli/cmd/pro.go index 775c520ff..a13ddc018 100644 --- a/cli/cmd/pro.go +++ b/cli/cmd/pro.go @@ -4,16 +4,16 @@ import ( "context" "fmt" "os" - "time" - + "github.com/odigos-io/odigos/k8sutils/pkg/pro" + k8sconsts "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/cli/cmd/resources" cmdcontext "github.com/odigos-io/odigos/cli/pkg/cmd_context" "github.com/odigos-io/odigos/cli/pkg/kube" - "github.com/odigos-io/odigos/k8sutils/pkg/consts" - odigosconsts "github.com/odigos-io/odigos/common/consts" "github.com/spf13/cobra" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var ( + updateRemoteFlag bool ) var proCmd = &cobra.Command{ @@ -32,52 +32,32 @@ var proCmd = &cobra.Command{ os.Exit(1) } onPremToken := cmd.Flag("onprem-token").Value.String() - err = updateOdigosToken(ctx, client, ns, onPremToken) + if updateRemoteFlag{ + err = executeRemoteUpdateToken(ctx, client, ns) + } else { + err = pro.UpdateOdigosToken(ctx, client, ns, onPremToken) + } + if err != nil { fmt.Println("\033[31mERROR\033[0m Failed to update token:") fmt.Println(err) os.Exit(1) + } else { + fmt.Println() + fmt.Println("\u001B[32mSUCCESS:\u001B[0m Token updated successfully") } - - fmt.Println() - fmt.Println("\u001B[32mSUCCESS:\u001B[0m Token updated successfully") }, } -func updateOdigosToken(ctx context.Context, client *kube.Client, namespace string, onPremToken string) error { - secret, err := client.CoreV1().Secrets(namespace).Get(ctx, consts.OdigosProSecretName, metav1.GetOptions{}) +func executeRemoteUpdateToken(ctx context.Context, client *kube.Client, namespace string) error { + uiSvcProxyEndpoint := fmt.Sprintf("/api/v1/namespaces/%s/services/%s:%d/proxy/api/token/update", namespace, k8sconsts.OdigosUiServiceName, k8sconsts.OdigosUiServicePort) + request := client.Clientset.RESTClient().Get().AbsPath(uiSvcProxyEndpoint).Do(ctx) + _, err := request.Raw() if err != nil { - if apierrors.IsNotFound(err) { - return fmt.Errorf("Tokens are not available in the open-source version of Odigos. Please contact Odigos team to inquire about pro version.") - } return err + } else { + return nil } - secret.Data[consts.OdigosOnpremTokenSecretKey] = []byte(onPremToken) - - _, err = client.CoreV1().Secrets(namespace).Update(ctx, secret, metav1.UpdateOptions{}) - if err != nil { - return err - } - - daemonSet, err := client.AppsV1().DaemonSets(namespace).Get(ctx, "odiglet", metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("failed to get DaemonSet odiglet in namespace %s: %v", namespace, err) - } - - // Modify the DaemonSet spec.template to trigger a rollout - if daemonSet.Spec.Template.Annotations == nil { - daemonSet.Spec.Template.Annotations = make(map[string]string) - } - daemonSet.Spec.Template.Annotations[odigosconsts.RolloutTriggerAnnotation] = time.Now().Format(time.RFC3339) - - _, err = client.AppsV1().DaemonSets(namespace).Update(ctx, daemonSet, metav1.UpdateOptions{}) - if err != nil { - fmt.Printf("Failed to restart Odiglets. Reason: %s\n", err) - fmt.Printf("To trigger a restart manually, run the following command:\n") - fmt.Printf("kubectl rollout restart daemonset odiglet -n %s\n", daemonSet.Namespace) - } - - return nil } func init() { @@ -85,4 +65,5 @@ func init() { proCmd.Flags().String("onprem-token", "", "On-prem token for Odigos") proCmd.MarkFlagRequired("onprem-token") + proCmd.PersistentFlags().BoolVarP(&updateRemoteFlag, "remote", "r", false, "use odigos ui service in the cluster to update the onprem token") } diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index 4aa0a13a6..0e0dfbe00 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -12,6 +12,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sconsts "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/cli/cmd/resources/resourcemanager" "github.com/odigos-io/odigos/cli/pkg/kube" @@ -288,8 +289,8 @@ func NewUIService(ns string) *corev1.Service { }, Ports: []corev1.ServicePort{ { - Name: "ui", - Port: 3000, + Name: k8sconsts.OdigosUiServiceName, + Port: k8sconsts.OdigosUiServicePort, }, { Name: "otlp", diff --git a/frontend/gqlgen.yml b/frontend/gqlgen.yml index dd9393893..7456843b2 100644 --- a/frontend/gqlgen.yml +++ b/frontend/gqlgen.yml @@ -44,6 +44,8 @@ models: ComputePlatform: fields: + apiTokens: + resolver: true k8sActualNamespace: resolver: true k8sActualNamespaces: diff --git a/frontend/graph/common.go b/frontend/graph/common.go new file mode 100644 index 000000000..ac241dda8 --- /dev/null +++ b/frontend/graph/common.go @@ -0,0 +1,30 @@ +package graph + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "strings" +) + +func extractJWTPayload(token string) (map[string]interface{}, error) { + parts := strings.Split(token, ".") + if len(parts) != 3 { + return nil, fmt.Errorf("invalid JWT token format") + } + + // Decode the payload (second part of the JWT) + payloadBytes, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, fmt.Errorf("failed to decode JWT payload: %w", err) + } + + // Parse the payload as JSON + var payload map[string]interface{} + err = json.Unmarshal(payloadBytes, &payload) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal JWT payload: %w", err) + } + + return payload, nil +} diff --git a/frontend/graph/generated.go b/frontend/graph/generated.go index 7028ba877..a4566f919 100644 --- a/frontend/graph/generated.go +++ b/frontend/graph/generated.go @@ -60,6 +60,13 @@ type ComplexityRoot struct { Type func(childComplexity int) int } + ApiToken struct { + ExpiresAt func(childComplexity int) int + IssuedAt func(childComplexity int) int + Name func(childComplexity int) int + Token func(childComplexity int) int + } + ClusterCollectorAnalyze struct { CollectorGroup func(childComplexity int) int CollectorReady func(childComplexity int) int @@ -79,6 +86,7 @@ type ComplexityRoot struct { } ComputePlatform struct { + APITokens func(childComplexity int) int Actions func(childComplexity int) int ComputePlatformType func(childComplexity int) int Destinations func(childComplexity int) int @@ -324,11 +332,13 @@ type ComplexityRoot struct { OdigosAnalyze struct { ClusterCollector func(childComplexity int) int HasErrors func(childComplexity int) int + InstallationMethod func(childComplexity int) int IsSettled func(childComplexity int) int NodeCollector func(childComplexity int) int NumberOfDestinations func(childComplexity int) int NumberOfSources func(childComplexity int) int OdigosVersion func(childComplexity int) int + Tier func(childComplexity int) int } OverviewMetricsResponse struct { @@ -470,6 +480,7 @@ type ComplexityRoot struct { } type ComputePlatformResolver interface { + APITokens(ctx context.Context, obj *model.ComputePlatform) ([]*model.APIToken, error) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) K8sActualNamespace(ctx context.Context, obj *model.ComputePlatform, name string) (*model.K8sActualNamespace, error) Sources(ctx context.Context, obj *model.ComputePlatform, nextPage string) (*model.PaginatedSources, error) @@ -579,6 +590,34 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.AddClusterInfoAction.Type(childComplexity), true + case "ApiToken.expiresAt": + if e.complexity.ApiToken.ExpiresAt == nil { + break + } + + return e.complexity.ApiToken.ExpiresAt(childComplexity), true + + case "ApiToken.issuedAt": + if e.complexity.ApiToken.IssuedAt == nil { + break + } + + return e.complexity.ApiToken.IssuedAt(childComplexity), true + + case "ApiToken.name": + if e.complexity.ApiToken.Name == nil { + break + } + + return e.complexity.ApiToken.Name(childComplexity), true + + case "ApiToken.token": + if e.complexity.ApiToken.Token == nil { + break + } + + return e.complexity.ApiToken.Token(childComplexity), true + case "ClusterCollectorAnalyze.collectorGroup": if e.complexity.ClusterCollectorAnalyze.CollectorGroup == nil { break @@ -663,6 +702,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ClusterInfo.AttributeStringValue(childComplexity), true + case "ComputePlatform.apiTokens": + if e.complexity.ComputePlatform.APITokens == nil { + break + } + + return e.complexity.ComputePlatform.APITokens(childComplexity), true + case "ComputePlatform.actions": if e.complexity.ComputePlatform.Actions == nil { break @@ -1765,6 +1811,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.OdigosAnalyze.HasErrors(childComplexity), true + case "OdigosAnalyze.installationMethod": + if e.complexity.OdigosAnalyze.InstallationMethod == nil { + break + } + + return e.complexity.OdigosAnalyze.InstallationMethod(childComplexity), true + case "OdigosAnalyze.isSettled": if e.complexity.OdigosAnalyze.IsSettled == nil { break @@ -1800,6 +1853,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.OdigosAnalyze.OdigosVersion(childComplexity), true + case "OdigosAnalyze.tier": + if e.complexity.OdigosAnalyze.Tier == nil { + break + } + + return e.complexity.OdigosAnalyze.Tier(childComplexity), true + case "OverviewMetricsResponse.destinations": if e.complexity.OverviewMetricsResponse.Destinations == nil { break @@ -3236,6 +3296,182 @@ func (ec *executionContext) fieldContext_AddClusterInfoAction_details(_ context. return fc, nil } +func (ec *executionContext) _ApiToken_token(ctx context.Context, field graphql.CollectedField, obj *model.APIToken) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ApiToken_token(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Token, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ApiToken_token(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApiToken", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApiToken_name(ctx context.Context, field graphql.CollectedField, obj *model.APIToken) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ApiToken_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ApiToken_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApiToken", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApiToken_issuedAt(ctx context.Context, field graphql.CollectedField, obj *model.APIToken) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ApiToken_issuedAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IssuedAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ApiToken_issuedAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApiToken", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApiToken_expiresAt(ctx context.Context, field graphql.CollectedField, obj *model.APIToken) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ApiToken_expiresAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiresAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ApiToken_expiresAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApiToken", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _ClusterCollectorAnalyze_enabled(ctx context.Context, field graphql.CollectedField, obj *model.ClusterCollectorAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ClusterCollectorAnalyze_enabled(ctx, field) if err != nil { @@ -3884,6 +4120,60 @@ func (ec *executionContext) fieldContext_ComputePlatform_computePlatformType(_ c return fc, nil } +func (ec *executionContext) _ComputePlatform_apiTokens(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ComputePlatform_apiTokens(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.ComputePlatform().APITokens(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.APIToken) + fc.Result = res + return ec.marshalNApiToken2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐAPIToken(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ComputePlatform_apiTokens(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ComputePlatform", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "token": + return ec.fieldContext_ApiToken_token(ctx, field) + case "name": + return ec.fieldContext_ApiToken_name(ctx, field) + case "issuedAt": + return ec.fieldContext_ApiToken_issuedAt(ctx, field) + case "expiresAt": + return ec.fieldContext_ApiToken_expiresAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ApiToken", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _ComputePlatform_k8sActualNamespaces(ctx context.Context, field graphql.CollectedField, obj *model.ComputePlatform) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) if err != nil { @@ -10848,6 +11138,114 @@ func (ec *executionContext) fieldContext_OdigosAnalyze_odigosVersion(_ context.C return fc, nil } +func (ec *executionContext) _OdigosAnalyze_tier(ctx context.Context, field graphql.CollectedField, obj *model.OdigosAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OdigosAnalyze_tier(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Tier, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.EntityProperty) + fc.Result = res + return ec.marshalNEntityProperty2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐEntityProperty(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OdigosAnalyze_tier(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OdigosAnalyze", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_EntityProperty_name(ctx, field) + case "value": + return ec.fieldContext_EntityProperty_value(ctx, field) + case "status": + return ec.fieldContext_EntityProperty_status(ctx, field) + case "explain": + return ec.fieldContext_EntityProperty_explain(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _OdigosAnalyze_installationMethod(ctx context.Context, field graphql.CollectedField, obj *model.OdigosAnalyze) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OdigosAnalyze_installationMethod(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.InstallationMethod, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.EntityProperty) + fc.Result = res + return ec.marshalNEntityProperty2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐEntityProperty(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OdigosAnalyze_installationMethod(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OdigosAnalyze", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_EntityProperty_name(ctx, field) + case "value": + return ec.fieldContext_EntityProperty_value(ctx, field) + case "status": + return ec.fieldContext_EntityProperty_status(ctx, field) + case "explain": + return ec.fieldContext_EntityProperty_explain(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type EntityProperty", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _OdigosAnalyze_numberOfDestinations(ctx context.Context, field graphql.CollectedField, obj *model.OdigosAnalyze) (ret graphql.Marshaler) { fc, err := ec.fieldContext_OdigosAnalyze_numberOfDestinations(ctx, field) if err != nil { @@ -12892,6 +13290,8 @@ func (ec *executionContext) fieldContext_Query_computePlatform(_ context.Context switch field.Name { case "computePlatformType": return ec.fieldContext_ComputePlatform_computePlatformType(ctx, field) + case "apiTokens": + return ec.fieldContext_ComputePlatform_apiTokens(ctx, field) case "k8sActualNamespaces": return ec.fieldContext_ComputePlatform_k8sActualNamespaces(ctx, field) case "k8sActualNamespace": @@ -13200,6 +13600,10 @@ func (ec *executionContext) fieldContext_Query_describeOdigos(_ context.Context, switch field.Name { case "odigosVersion": return ec.fieldContext_OdigosAnalyze_odigosVersion(ctx, field) + case "tier": + return ec.fieldContext_OdigosAnalyze_tier(ctx, field) + case "installationMethod": + return ec.fieldContext_OdigosAnalyze_installationMethod(ctx, field) case "numberOfDestinations": return ec.fieldContext_OdigosAnalyze_numberOfDestinations(ctx, field) case "numberOfSources": @@ -17844,6 +18248,60 @@ func (ec *executionContext) _AddClusterInfoAction(ctx context.Context, sel ast.S return out } +var apiTokenImplementors = []string{"ApiToken"} + +func (ec *executionContext) _ApiToken(ctx context.Context, sel ast.SelectionSet, obj *model.APIToken) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, apiTokenImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ApiToken") + case "token": + out.Values[i] = ec._ApiToken_token(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec._ApiToken_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "issuedAt": + out.Values[i] = ec._ApiToken_issuedAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "expiresAt": + out.Values[i] = ec._ApiToken_expiresAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var clusterCollectorAnalyzeImplementors = []string{"ClusterCollectorAnalyze"} func (ec *executionContext) _ClusterCollectorAnalyze(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterCollectorAnalyze) graphql.Marshaler { @@ -17964,6 +18422,42 @@ func (ec *executionContext) _ComputePlatform(ctx context.Context, sel ast.Select if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } + case "apiTokens": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ComputePlatform_apiTokens(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "k8sActualNamespaces": field := field @@ -19981,6 +20475,16 @@ func (ec *executionContext) _OdigosAnalyze(ctx context.Context, sel ast.Selectio if out.Values[i] == graphql.Null { out.Invalids++ } + case "tier": + out.Values[i] = ec._OdigosAnalyze_tier(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "installationMethod": + out.Values[i] = ec._OdigosAnalyze_installationMethod(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "numberOfDestinations": out.Values[i] = ec._OdigosAnalyze_numberOfDestinations(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -21494,6 +21998,44 @@ func (ec *executionContext) unmarshalNActionInput2githubᚗcomᚋodigosᚑioᚋo return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNApiToken2ᚕᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐAPIToken(ctx context.Context, sel ast.SelectionSet, v []*model.APIToken) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalOApiToken2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐAPIToken(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { res, err := graphql.UnmarshalBoolean(v) return res, graphql.ErrorOnPath(ctx, err) @@ -23232,6 +23774,13 @@ func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel a return res } +func (ec *executionContext) marshalOApiToken2ᚖgithubᚗcomᚋodigosᚑioᚋodigosᚋfrontendᚋgraphᚋmodelᚐAPIToken(ctx context.Context, sel ast.SelectionSet, v *model.APIToken) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._ApiToken(ctx, sel, v) +} + func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v interface{}) (bool, error) { res, err := graphql.UnmarshalBoolean(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/frontend/graph/model/models_gen.go b/frontend/graph/model/models_gen.go index fd895d00b..7823c9938 100644 --- a/frontend/graph/model/models_gen.go +++ b/frontend/graph/model/models_gen.go @@ -54,6 +54,13 @@ func (this AddClusterInfoAction) GetSignals() []SignalType { return interfaceSlice } +type APIToken struct { + Token string `json:"token"` + Name string `json:"name"` + IssuedAt int `json:"issuedAt"` + ExpiresAt int `json:"expiresAt"` +} + type ClusterCollectorAnalyze struct { Enabled *EntityProperty `json:"enabled"` CollectorGroup *EntityProperty `json:"collectorGroup"` @@ -74,6 +81,7 @@ type ClusterInfo struct { type ComputePlatform struct { ComputePlatformType ComputePlatformType `json:"computePlatformType"` + APITokens []*APIToken `json:"apiTokens"` K8sActualNamespaces []*K8sActualNamespace `json:"k8sActualNamespaces"` K8sActualNamespace *K8sActualNamespace `json:"k8sActualNamespace,omitempty"` Sources *PaginatedSources `json:"sources"` @@ -386,6 +394,8 @@ type NodeCollectorAnalyze struct { type OdigosAnalyze struct { OdigosVersion *EntityProperty `json:"odigosVersion"` + Tier *EntityProperty `json:"tier"` + InstallationMethod *EntityProperty `json:"installationMethod"` NumberOfDestinations int `json:"numberOfDestinations"` NumberOfSources int `json:"numberOfSources"` ClusterCollector *ClusterCollectorAnalyze `json:"clusterCollector"` diff --git a/frontend/graph/schema.graphqls b/frontend/graph/schema.graphqls index e25e8eca4..d0abebda6 100644 --- a/frontend/graph/schema.graphqls +++ b/frontend/graph/schema.graphqls @@ -189,8 +189,16 @@ input MessagingPayloadCollectionInput { dropPartialPayloads: Boolean } +type ApiToken { + token: String! + name: String! + issuedAt: Int! + expiresAt: Int! +} + type ComputePlatform { computePlatformType: ComputePlatformType! + apiTokens: [ApiToken]! k8sActualNamespaces: [K8sActualNamespace]! k8sActualNamespace(name: String!): K8sActualNamespace sources(nextPage: String!): PaginatedSources! @@ -544,6 +552,8 @@ type NodeCollectorAnalyze { type OdigosAnalyze { odigosVersion: EntityProperty! + tier: EntityProperty! + installationMethod: EntityProperty! numberOfDestinations: Int! numberOfSources: Int! clusterCollector: ClusterCollectorAnalyze! diff --git a/frontend/graph/schema.resolvers.go b/frontend/graph/schema.resolvers.go index b2714b224..1e74f8ba3 100644 --- a/frontend/graph/schema.resolvers.go +++ b/frontend/graph/schema.resolvers.go @@ -19,11 +19,52 @@ import ( "github.com/odigos-io/odigos/frontend/services/describe/odigos_describe" "github.com/odigos-io/odigos/frontend/services/describe/source_describe" testconnection "github.com/odigos-io/odigos/frontend/services/test_connection" + k8sconsts "github.com/odigos-io/odigos/k8sutils/pkg/consts" "github.com/odigos-io/odigos/k8sutils/pkg/workload" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) +// APITokens is the resolver for the apiTokens field. +func (r *computePlatformResolver) APITokens(ctx context.Context, obj *model.ComputePlatform) ([]*model.APIToken, error) { + // The result should always be 0 or 1: + // If it's 0, it means this is the OSS version. + // If it's 1, it means this is the Enterprise version. + secret, err := kube.DefaultClient.CoreV1().Secrets(services.OdigosSystemNamespace).Get(ctx, k8sconsts.OdigosProSecretName, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return make([]*model.APIToken, 0), nil + } + return nil, err + } + + token := string(secret.Data[k8sconsts.OdigosOnpremTokenSecretKey]) + + // Extract the payload from the JWT + tokenPayload, err := extractJWTPayload(token) + if err != nil { + return nil, fmt.Errorf("failed to extract JWT payload: %w", err) + } + + // Extract values from the token payload + aud, _ := tokenPayload["aud"].(string) + iat, _ := tokenPayload["iat"].(float64) + exp, _ := tokenPayload["exp"].(float64) + + // We need to return an array (even if it's just 1 token), because in the future we will have to support multiple platforms. + secrets := []*model.APIToken{ + { + Token: token, + Name: aud, + IssuedAt: int(iat) * 1000, // Convert to milliseconds + ExpiresAt: int(exp) * 1000, // Convert to milliseconds + }, + } + + return secrets, nil +} + // K8sActualNamespaces is the resolver for the k8sActualNamespaces field. func (r *computePlatformResolver) K8sActualNamespaces(ctx context.Context, obj *model.ComputePlatform) ([]*model.K8sActualNamespace, error) { namespacesResponse := services.GetK8SNamespaces(ctx) diff --git a/frontend/services/describe/odigos_describe/odigos_describe.go b/frontend/services/describe/odigos_describe/odigos_describe.go index a36db3e51..0fc70007b 100644 --- a/frontend/services/describe/odigos_describe/odigos_describe.go +++ b/frontend/services/describe/odigos_describe/odigos_describe.go @@ -30,6 +30,8 @@ func convertOdigosToGQL(odigos *odigos.OdigosAnalyze) *model.OdigosAnalyze { } return &model.OdigosAnalyze{ OdigosVersion: describe_utils.ConvertEntityPropertyToGQL(&odigos.OdigosVersion), + Tier: describe_utils.ConvertEntityPropertyToGQL(&odigos.Tier), + InstallationMethod: describe_utils.ConvertEntityPropertyToGQL(&odigos.InstallationMethod), NumberOfDestinations: odigos.NumberOfDestinations, NumberOfSources: odigos.NumberOfSources, ClusterCollector: convertClusterCollectorToGQL(&odigos.ClusterCollector), diff --git a/frontend/webapp/assets/icons/common/copy-icon.tsx b/frontend/webapp/assets/icons/common/copy-icon.tsx new file mode 100644 index 000000000..51a9af15a --- /dev/null +++ b/frontend/webapp/assets/icons/common/copy-icon.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { SVG } from '@/assets'; +import theme from '@/styles/theme'; + +export const CopyIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate = 0, onClick }) => { + return ( + + + + ); +}; diff --git a/frontend/webapp/assets/icons/common/index.ts b/frontend/webapp/assets/icons/common/index.ts index 927b05909..0a282aff6 100644 --- a/frontend/webapp/assets/icons/common/index.ts +++ b/frontend/webapp/assets/icons/common/index.ts @@ -5,6 +5,7 @@ export * from './check-icon'; export * from './cross-circled-icon'; export * from './code-brackets-icon'; export * from './code-icon'; +export * from './copy-icon'; export * from './cross-icon'; export * from './edit-icon'; export * from './error-round-icon'; @@ -15,12 +16,14 @@ export * from './eye-open-icon'; export * from './filter-icon'; export * from './folder-icon'; export * from './info-icon'; +export * from './key-icon'; export * from './list-icon'; export * from './no-data-icon'; export * from './notebook-icon'; export * from './notification-icon'; export * from './plus-icon'; export * from './search-icon'; +export * from './terminal-icon'; export * from './trash-icon'; export * from './warning-triangle-icon'; export * from './x-icon'; diff --git a/frontend/webapp/assets/icons/common/key-icon.tsx b/frontend/webapp/assets/icons/common/key-icon.tsx new file mode 100644 index 000000000..51970e04e --- /dev/null +++ b/frontend/webapp/assets/icons/common/key-icon.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { SVG } from '@/assets'; +import theme from '@/styles/theme'; + +export const KeyIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate = 0, onClick }) => { + return ( + + + + ); +}; diff --git a/frontend/webapp/assets/icons/common/terminal-icon.tsx b/frontend/webapp/assets/icons/common/terminal-icon.tsx new file mode 100644 index 000000000..5a7938dd2 --- /dev/null +++ b/frontend/webapp/assets/icons/common/terminal-icon.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { SVG } from '@/assets'; +import theme from '@/styles/theme'; + +export const TerminalIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate = 0, onClick }) => { + return ( + + + + ); +}; diff --git a/frontend/webapp/components/main/header/index.tsx b/frontend/webapp/components/main/header/index.tsx index 75efd6140..a3d5d0c39 100644 --- a/frontend/webapp/components/main/header/index.tsx +++ b/frontend/webapp/components/main/header/index.tsx @@ -1,11 +1,12 @@ import React from 'react'; +import theme from '@/styles/theme'; import { FlexRow } from '@/styles'; import { SLACK_LINK } from '@/utils'; import styled from 'styled-components'; import { PlatformTypes } from '@/types'; import { PlatformTitle } from './cp-title'; import { NotificationManager } from '@/components'; -import { OdigosLogo, OdigosLogoText, SlackLogo } from '@/assets'; +import { OdigosLogoText, SlackLogo, TerminalIcon } from '@/assets'; import { ConnectionStatus, IconButton } from '@/reuseable-components'; import { DRAWER_OTHER_TYPES, useConnectionStore, useDrawerStore } from '@/store'; @@ -34,7 +35,7 @@ export const MainHeader: React.FC = () => { const { setSelectedItem } = useDrawerStore(); const { connecting, active, title, message } = useConnectionStore(); - const handleClickDescribe = () => setSelectedItem({ type: DRAWER_OTHER_TYPES.DESCRIBE_ODIGOS, id: DRAWER_OTHER_TYPES.DESCRIBE_ODIGOS }); + const handleClickCli = () => setSelectedItem({ type: DRAWER_OTHER_TYPES.ODIGOS_CLI, id: DRAWER_OTHER_TYPES.ODIGOS_CLI }); const handleClickSlack = () => window.open(SLACK_LINK, '_blank', 'noopener noreferrer'); return ( @@ -46,10 +47,12 @@ export const MainHeader: React.FC = () => { - - - + + + + + diff --git a/frontend/webapp/components/notification/notification-manager.tsx b/frontend/webapp/components/notification/notification-manager.tsx index d90188bdf..33662da7d 100644 --- a/frontend/webapp/components/notification/notification-manager.tsx +++ b/frontend/webapp/components/notification/notification-manager.tsx @@ -84,7 +84,7 @@ export const NotificationManager = () => { return ( - + {isOpen && ( diff --git a/frontend/webapp/components/overview/all-drawers/cli-drawer.tsx b/frontend/webapp/components/overview/all-drawers/cli-drawer.tsx new file mode 100644 index 000000000..e2731bb88 --- /dev/null +++ b/frontend/webapp/components/overview/all-drawers/cli-drawer.tsx @@ -0,0 +1,107 @@ +import React, { useState } from 'react'; +import { FlexRow } from '@/styles'; +import styled from 'styled-components'; +import { NOTIFICATION_TYPE } from '@/types'; +import { DATA_CARDS, getStatusIcon, safeJsonStringify } from '@/utils'; +import OverviewDrawer from '@/containers/main/overview/overview-drawer'; +import { CodeBracketsIcon, CodeIcon, CopyIcon, KeyIcon, ListIcon } from '@/assets'; +import { useComputePlatform, useCopy, useDescribeOdigos, useTimeAgo } from '@/hooks'; +import { DataCard, DataCardFieldTypes, IconButton, Segment } from '@/reuseable-components'; + +interface Props {} + +const DataContainer = styled.div` + display: flex; + flex-direction: column; + gap: 12px; +`; + +export const CliDrawer: React.FC = () => { + const timeAgo = useTimeAgo(); + const { data: cp } = useComputePlatform(); + const { isCopied, copiedIndex, clickCopy } = useCopy(); + const { data: describe, restructureForPrettyMode } = useDescribeOdigos(); + + const [isPrettyMode, setIsPrettyMode] = useState(true); + + const tokens = cp?.computePlatform.apiTokens || []; + + return ( + + + {!!tokens?.length && ( + [ + { columnKey: 'icon', icon: KeyIcon }, + { columnKey: 'name', value: name }, + { columnKey: 'expires_at', value: `${timeAgo.format(expiresAt)} (${new Date(expiresAt).toDateString().split(' ').slice(1).join(' ')})` }, + { columnKey: 'token', value: `${new Array(15).fill('•').join('')}` }, + { + columnKey: 'actions', + component: () => { + const SuccessIcon = getStatusIcon(NOTIFICATION_TYPE.SUCCESS); + + return ( + + clickCopy(token, idx)}> + {isCopied && copiedIndex === idx ? : } + + + {/* + + {}}> + + */} + + ); + }, + }, + ]), + }, + width: 'inherit', + }, + ]} + /> + )} + + + } + data={[ + { + type: DataCardFieldTypes.CODE, + value: JSON.stringify({ + language: 'json', + code: safeJsonStringify(isPrettyMode ? restructureForPrettyMode(describe) : describe), + pretty: isPrettyMode, + }), + width: 'inherit', + }, + ]} + /> + + + ); +}; diff --git a/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx b/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx deleted file mode 100644 index 2a176a1cc..000000000 --- a/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useState } from 'react'; -import styled from 'styled-components'; -import { useDescribeOdigos } from '@/hooks'; -import { DATA_CARDS, safeJsonStringify } from '@/utils'; -import { CodeBracketsIcon, CodeIcon, ListIcon } from '@/assets'; -import OverviewDrawer from '@/containers/main/overview/overview-drawer'; -import { DataCard, DataCardFieldTypes, Segment } from '@/reuseable-components'; - -interface Props {} - -const DataContainer = styled.div` - display: flex; - flex-direction: column; - gap: 12px; -`; - -export const DescribeDrawer: React.FC = () => { - const { data: describe, restructureForPrettyMode } = useDescribeOdigos(); - const [isPrettyMode, setIsPrettyMode] = useState(true); - - return ( - - - - } - data={[ - { - type: DataCardFieldTypes.CODE, - value: JSON.stringify({ - language: 'json', - code: safeJsonStringify(isPrettyMode ? restructureForPrettyMode(describe) : describe), - pretty: isPrettyMode, - }), - width: 'inherit', - }, - ]} - /> - - - ); -}; diff --git a/frontend/webapp/components/overview/all-drawers/index.tsx b/frontend/webapp/components/overview/all-drawers/index.tsx index 8f9329437..a1eaba051 100644 --- a/frontend/webapp/components/overview/all-drawers/index.tsx +++ b/frontend/webapp/components/overview/all-drawers/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; +import { CliDrawer } from './cli-drawer'; import { OVERVIEW_ENTITY_TYPES } from '@/types'; -import { DescribeDrawer } from './describe-drawer'; import { DRAWER_OTHER_TYPES, useDrawerStore } from '@/store'; import { ActionDrawer, DestinationDrawer, RuleDrawer, SourceDrawer } from '@/containers'; @@ -22,8 +22,8 @@ const AllDrawers = () => { case OVERVIEW_ENTITY_TYPES.DESTINATION: return ; - case DRAWER_OTHER_TYPES.DESCRIBE_ODIGOS: - return ; + case DRAWER_OTHER_TYPES.ODIGOS_CLI: + return ; default: return <>; diff --git a/frontend/webapp/containers/main/destinations/destination-form-body/dynamic-fields/index.tsx b/frontend/webapp/containers/main/destinations/destination-form-body/dynamic-fields/index.tsx index e99717fc1..bb2057c08 100644 --- a/frontend/webapp/containers/main/destinations/destination-form-body/dynamic-fields/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-form-body/dynamic-fields/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type { DynamicField } from '@/types'; -import { compareCondition, INPUT_TYPES } from '@/utils'; +import { compareCondition, INPUT_TYPES, safeJsonParse } from '@/utils'; import { Dropdown, Input, TextArea, InputList, KeyValueInputsList, Checkbox } from '@/reuseable-components'; interface Props { @@ -31,9 +31,25 @@ export const DestinationDynamicFields: React.FC = ({ fields, onChange, fo /> ); case INPUT_TYPES.MULTI_INPUT: - return onChange(field.name, JSON.stringify(value))} errorMessage={formErrors[field.name]} />; + return ( + onChange(field.name, JSON.stringify(value))} + errorMessage={formErrors[field.name]} + /> + ); case INPUT_TYPES.KEY_VALUE_PAIR: - return onChange(field.name, JSON.stringify(value))} errorMessage={formErrors[field.name]} />; + return ( + onChange(field.name, JSON.stringify(value))} + errorMessage={formErrors[field.name]} + /> + ); case INPUT_TYPES.TEXTAREA: return