From 32190bf78b24385b58e50919c76f3891f665ae34 Mon Sep 17 00:00:00 2001 From: Ron Federman <73110295+RonFed@users.noreply.github.com> Date: Sun, 19 May 2024 11:02:28 +0300 Subject: [PATCH] Add condition for instrumentedApplication (#1203) --- .../odigos.io_instrumentedapplications.yaml | 72 +++++++++++++++++++ .../v1alpha1/instrumentedapplication.go | 9 ++- .../v1alpha1/instrumentedapplicationstatus.go | 47 ++++++++++++ .../odigos/applyconfiguration/utils.go | 2 + .../v1alpha1/instrumentedapplication_types.go | 2 + api/odigos/v1alpha1/zz_generated.deepcopy.go | 9 ++- cli/cmd/resources/crds/instrumentedapps.go | 4 ++ .../instrumentationdevice/common.go | 8 +++ .../instrumentation/instrumentation.go | 10 ++- 9 files changed, 155 insertions(+), 8 deletions(-) create mode 100644 api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentedapplicationstatus.go diff --git a/api/config/crd/bases/odigos.io_instrumentedapplications.yaml b/api/config/crd/bases/odigos.io_instrumentedapplications.yaml index 29cc2d202..35fdec10f 100644 --- a/api/config/crd/bases/odigos.io_instrumentedapplications.yaml +++ b/api/config/crd/bases/odigos.io_instrumentedapplications.yaml @@ -119,6 +119,78 @@ spec: status: description: InstrumentedApplicationStatus defines the observed state of InstrumentedApplication + properties: + conditions: + description: Represents the observations of a nstrumentedApplication's + current state. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + 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. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 type: object type: object served: true diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentedapplication.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentedapplication.go index 1adfcf676..59d7e3ea6 100644 --- a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentedapplication.go +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentedapplication.go @@ -18,7 +18,6 @@ limitations under the License. package v1alpha1 import ( - odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" v1 "k8s.io/client-go/applyconfigurations/meta/v1" @@ -29,8 +28,8 @@ import ( type InstrumentedApplicationApplyConfiguration struct { v1.TypeMetaApplyConfiguration `json:",inline"` *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` - Spec *InstrumentedApplicationSpecApplyConfiguration `json:"spec,omitempty"` - Status *odigosv1alpha1.InstrumentedApplicationStatus `json:"status,omitempty"` + Spec *InstrumentedApplicationSpecApplyConfiguration `json:"spec,omitempty"` + Status *InstrumentedApplicationStatusApplyConfiguration `json:"status,omitempty"` } // InstrumentedApplication constructs an declarative configuration of the InstrumentedApplication type for use with @@ -213,7 +212,7 @@ func (b *InstrumentedApplicationApplyConfiguration) WithSpec(value *Instrumented // 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 *InstrumentedApplicationApplyConfiguration) WithStatus(value odigosv1alpha1.InstrumentedApplicationStatus) *InstrumentedApplicationApplyConfiguration { - b.Status = &value +func (b *InstrumentedApplicationApplyConfiguration) WithStatus(value *InstrumentedApplicationStatusApplyConfiguration) *InstrumentedApplicationApplyConfiguration { + b.Status = value return b } diff --git a/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentedapplicationstatus.go b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentedapplicationstatus.go new file mode 100644 index 000000000..1d75804e9 --- /dev/null +++ b/api/generated/odigos/applyconfiguration/odigos/v1alpha1/instrumentedapplicationstatus.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" +) + +// InstrumentedApplicationStatusApplyConfiguration represents an declarative configuration of the InstrumentedApplicationStatus type for use +// with apply. +type InstrumentedApplicationStatusApplyConfiguration struct { + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` +} + +// InstrumentedApplicationStatusApplyConfiguration constructs an declarative configuration of the InstrumentedApplicationStatus type for use with +// apply. +func InstrumentedApplicationStatus() *InstrumentedApplicationStatusApplyConfiguration { + return &InstrumentedApplicationStatusApplyConfiguration{} +} + +// 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 *InstrumentedApplicationStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *InstrumentedApplicationStatusApplyConfiguration { + 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 1105a097d..8c509f8e1 100644 --- a/api/generated/odigos/applyconfiguration/utils.go +++ b/api/generated/odigos/applyconfiguration/utils.go @@ -52,6 +52,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &odigosv1alpha1.InstrumentedApplicationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("InstrumentedApplicationSpec"): return &odigosv1alpha1.InstrumentedApplicationSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("InstrumentedApplicationStatus"): + return &odigosv1alpha1.InstrumentedApplicationStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("OdigosConfiguration"): return &odigosv1alpha1.OdigosConfigurationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("OdigosConfigurationSpec"): diff --git a/api/odigos/v1alpha1/instrumentedapplication_types.go b/api/odigos/v1alpha1/instrumentedapplication_types.go index e7334ea59..674f8e4d4 100644 --- a/api/odigos/v1alpha1/instrumentedapplication_types.go +++ b/api/odigos/v1alpha1/instrumentedapplication_types.go @@ -60,6 +60,8 @@ type InstrumentedApplicationSpec struct { // InstrumentedApplicationStatus defines the observed state of InstrumentedApplication type InstrumentedApplicationStatus struct { + // Represents the observations of a nstrumentedApplication's current state. + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" protobuf:"bytes,1,rep,name=conditions"` } //+genclient diff --git a/api/odigos/v1alpha1/zz_generated.deepcopy.go b/api/odigos/v1alpha1/zz_generated.deepcopy.go index 6e60c7b34..6b2da87d3 100644 --- a/api/odigos/v1alpha1/zz_generated.deepcopy.go +++ b/api/odigos/v1alpha1/zz_generated.deepcopy.go @@ -395,7 +395,7 @@ func (in *InstrumentedApplication) DeepCopyInto(out *InstrumentedApplication) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentedApplication. @@ -480,6 +480,13 @@ func (in *InstrumentedApplicationSpec) DeepCopy() *InstrumentedApplicationSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstrumentedApplicationStatus) DeepCopyInto(out *InstrumentedApplicationStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentedApplicationStatus. diff --git a/cli/cmd/resources/crds/instrumentedapps.go b/cli/cmd/resources/crds/instrumentedapps.go index 8162f5ddc..4b668290a 100644 --- a/cli/cmd/resources/crds/instrumentedapps.go +++ b/cli/cmd/resources/crds/instrumentedapps.go @@ -1,6 +1,7 @@ package crds import ( + "github.com/odigos-io/odigos/cli/cmd/resources/crds/common" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -187,6 +188,9 @@ func NewInstrumentedApp() *apiextensionsv1.CustomResourceDefinition { }, "status": { Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "conditions": common.Conditions, + }, }, }, }, diff --git a/instrumentor/controllers/instrumentationdevice/common.go b/instrumentor/controllers/instrumentationdevice/common.go index 1dc653c9b..a321a6c90 100644 --- a/instrumentor/controllers/instrumentationdevice/common.go +++ b/instrumentor/controllers/instrumentationdevice/common.go @@ -7,11 +7,13 @@ import ( "github.com/go-logr/logr" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common/consts" + "github.com/odigos-io/odigos/common/k8s" "github.com/odigos-io/odigos/common/utils" "github.com/odigos-io/odigos/instrumentor/instrumentation" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" @@ -25,6 +27,10 @@ const ( UnInstrumentReasonRemoveAll UnInstrumentReason = "Remove all" ) +const ( + appliedInstrumentationDeviceType = "AppliedInstrumentationDevice" +) + func clearInstrumentationEbpf(obj client.Object) { annotations := obj.GetAnnotations() if annotations == nil { @@ -80,8 +86,10 @@ func instrument(logger logr.Logger, ctx context.Context, kubeClient client.Clien }) if err != nil { + k8s.UpdateStatusConditions(ctx, kubeClient, runtimeDetails, &runtimeDetails.Status.Conditions, metav1.ConditionFalse, appliedInstrumentationDeviceType, "ErrApplyInstrumentationDevice", err.Error()) return err } + k8s.UpdateStatusConditions(ctx, kubeClient, runtimeDetails, &runtimeDetails.Status.Conditions, metav1.ConditionTrue, appliedInstrumentationDeviceType, string(result), "Successfully applied instrumentation device to pod template") if result != controllerutil.OperationResultNone { logger.V(0).Info("instrumented application", "name", obj.GetName(), "namespace", obj.GetNamespace()) diff --git a/instrumentor/instrumentation/instrumentation.go b/instrumentor/instrumentation/instrumentation.go index 7ab84761a..32754d615 100644 --- a/instrumentor/instrumentation/instrumentation.go +++ b/instrumentor/instrumentation/instrumentation.go @@ -2,6 +2,7 @@ package instrumentation import ( "encoding/json" + "errors" "fmt" "strings" @@ -15,6 +16,11 @@ import ( "k8s.io/apimachinery/pkg/api/resource" ) +var ( + ErrNoDefaultSDK = errors.New("no default sdks found") + ErrPatchEnvVars = errors.New("failed to patch env vars") +) + func ApplyInstrumentationDevicesToPodTemplate(original *v1.PodTemplateSpec, runtimeDetails *odigosv1.InstrumentedApplication, defaultSdks map[common.ProgrammingLanguage]common.OtelSdk, targetObj client.Object) error { // delete any existing instrumentation devices. @@ -32,7 +38,7 @@ func ApplyInstrumentationDevicesToPodTemplate(original *v1.PodTemplateSpec, runt otelSdk, found := defaultSdks[*containerLanguage] if !found { - return fmt.Errorf("default sdk not found for language %s", *containerLanguage) + return fmt.Errorf("%w for language: %s, container:%s", ErrNoDefaultSDK, *containerLanguage, container.Name) } instrumentationDeviceName := common.InstrumentationDeviceName(*containerLanguage, otelSdk) @@ -44,7 +50,7 @@ func ApplyInstrumentationDevicesToPodTemplate(original *v1.PodTemplateSpec, runt err := patchEnvVars(runtimeDetails, &container, targetObj) if err != nil { - return fmt.Errorf("failed to patch env vars: %v", err) + return fmt.Errorf("%w: %v", ErrPatchEnvVars, err) } modifiedContainers = append(modifiedContainers, container)