diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 70145e6f9..c14af1467 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,7 @@ updates: - /instrumentor - /odiglet - /opampserver + - /instrumentation schedule: day: monday interval: weekly diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index c4f20f8f9..3eabd25d9 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -66,7 +66,7 @@ jobs: kube-version: - '1.20.15' - '1.23' - - '1.30' + - '1.32' test-scenario: - 'ui' - 'multi-apps' @@ -78,8 +78,8 @@ jobs: kind-image: 'kindest/node:v1.20.15@sha256:a32bf55309294120616886b5338f95dd98a2f7231519c7dedcec32ba29699394' - kube-version: '1.23' kind-image: 'kindest/node:v1.23.17@sha256:14d0a9a892b943866d7e6be119a06871291c517d279aedb816a4b4bc0ec0a5b3' - - kube-version: '1.30' - kind-image: 'kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e' + - kube-version: '1.32' + kind-image: 'kindest/node:v1.32.0@sha256:2458b423d635d7b01637cac2d6de7e1c1dca1148a2ba2e90975e214ca849e7cb' steps: - name: Checkout uses: actions/checkout@v4 @@ -105,7 +105,7 @@ jobs: uses: helm/kind-action@v1.10.0 with: node_image: ${{ matrix.kind-image }} - version: 'v0.23.0' + version: 'v0.25.0' cluster_name: kind - name: Download and Load Docker Images to Kind diff --git a/.github/workflows/publish-modules.yml b/.github/workflows/publish-modules.yml index 5333dfd3b..e904785e8 100644 --- a/.github/workflows/publish-modules.yml +++ b/.github/workflows/publish-modules.yml @@ -62,6 +62,10 @@ jobs: run: | git tag opampserver/${{ steps.extract_tag.outputs.tag }} + - name: tag instrumentation + run: | + git tag instrumentation/${{ steps.extract_tag.outputs.tag }} + - name: Push Module Tags run: | git push origin --tags diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ebd41c28..b8ca973de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,15 +89,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }} - # TODO: Remove once we are ready to fully sunset managing charts in a separate repo - - name: Trigger Release PR in Odigos Charts - 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-charts/dispatches \ - -d '{"event_type": "create_release_pr", "client_payload": {"tag": "${{ env.TAG }}"}}' - - uses: ko-build/setup-ko@v0.7 - name: publish cli image to docker registries diff --git a/autoscaler/controllers/datacollection/daemonset.go b/autoscaler/controllers/datacollection/daemonset.go index 7049263d8..a8063bdaa 100644 --- a/autoscaler/controllers/datacollection/daemonset.go +++ b/autoscaler/controllers/datacollection/daemonset.go @@ -192,8 +192,10 @@ func getDesiredDaemonSet(datacollection *odigosv1.CollectorsGroup, rollingUpdate.MaxSurge = &maxSurge } - requestMemoryRequestQuantity := resource.MustParse(fmt.Sprintf("%dMi", datacollection.Spec.ResourcesSettings.MemoryRequestMiB)) - requestMemoryLimitQuantity := resource.MustParse(fmt.Sprintf("%dMi", datacollection.Spec.ResourcesSettings.MemoryLimitMiB)) + resourceMemoryRequestQuantity := resource.MustParse(fmt.Sprintf("%dMi", datacollection.Spec.ResourcesSettings.MemoryRequestMiB)) + resourceMemoryLimitQuantity := resource.MustParse(fmt.Sprintf("%dMi", datacollection.Spec.ResourcesSettings.MemoryLimitMiB)) + resourceCpuRequestQuantity := resource.MustParse(fmt.Sprintf("%dm", datacollection.Spec.ResourcesSettings.CpuRequestMillicores)) + resourceCpuLimitQuantity := resource.MustParse(fmt.Sprintf("%dm", datacollection.Spec.ResourcesSettings.CpuLimitMillicores)) desiredDs := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ @@ -326,10 +328,12 @@ func getDesiredDaemonSet(datacollection *odigosv1.CollectorsGroup, }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceMemory: requestMemoryRequestQuantity, + corev1.ResourceMemory: resourceMemoryRequestQuantity, + corev1.ResourceCPU: resourceCpuRequestQuantity, }, Limits: corev1.ResourceList{ - corev1.ResourceMemory: requestMemoryLimitQuantity, + corev1.ResourceMemory: resourceMemoryLimitQuantity, + corev1.ResourceCPU: resourceCpuLimitQuantity, }, }, SecurityContext: &corev1.SecurityContext{ diff --git a/autoscaler/controllers/gateway/deployment.go b/autoscaler/controllers/gateway/deployment.go index 002ec1b5c..a89a66fd1 100644 --- a/autoscaler/controllers/gateway/deployment.go +++ b/autoscaler/controllers/gateway/deployment.go @@ -171,6 +171,19 @@ func getDesiredDeployment(dests *odigosv1.DestinationList, configDataHash string Name: "GOMEMLIMIT", Value: fmt.Sprintf("%dMiB", gateway.Spec.ResourcesSettings.GomemlimitMiB), }, + { + // let the Go runtime know how many CPUs are available, + // without this, Go will assume all the cores are available. + Name: "GOMAXPROCS", + ValueFrom: &corev1.EnvVarSource{ + ResourceFieldRef: &corev1.ResourceFieldSelector{ + ContainerName: containerName, + // limitCPU, Kubernetes automatically rounds up the value to an integer + // (700m -> 1, 1200m -> 2) + Resource: "limits.cpu", + }, + }, + }, }, SecurityContext: &corev1.SecurityContext{ AllowPrivilegeEscalation: boolPtr(false), diff --git a/autoscaler/go.mod b/autoscaler/go.mod index 10cf5c1e5..01d6b965f 100644 --- a/autoscaler/go.mod +++ b/autoscaler/go.mod @@ -11,11 +11,11 @@ require ( github.com/odigos-io/odigos/k8sutils v0.0.0 github.com/odigos-io/opentelemetry-zap-bridge v0.0.5 github.com/stretchr/testify v1.10.0 - go.opentelemetry.io/otel v1.32.0 - k8s.io/api v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/client-go v0.31.3 - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + go.opentelemetry.io/otel v1.33.0 + k8s.io/api v0.32.0 + k8s.io/apimachinery v0.32.0 + k8s.io/client-go v0.32.0 + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 sigs.k8s.io/controller-runtime v0.19.3 ) @@ -35,7 +35,6 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/goccy/go-yaml v1.11.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect @@ -43,7 +42,6 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -60,33 +58,35 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/time v0.5.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/time v0.7.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // 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 + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // 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 // indirect ) diff --git a/autoscaler/go.sum b/autoscaler/go.sum index 50c423f84..b5201a9d0 100644 --- a/autoscaler/go.sum +++ b/autoscaler/go.sum @@ -47,8 +47,6 @@ github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= @@ -59,16 +57,14 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 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/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -97,10 +93,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/odigos-io/opentelemetry-zap-bridge v0.0.5 h1:UDEKtgab42nGVSvA/F3lLKUYFPETtpl7kpxM9BKBlWk= github.com/odigos-io/opentelemetry-zap-bridge v0.0.5/go.mod h1:K98wHhktQ6vCTqeFztcpBDtPTe88vxVgZFlbeGobt24= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -114,8 +110,8 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G 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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -128,14 +124,16 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= 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/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -147,8 +145,8 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -157,10 +155,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn 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/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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= @@ -169,22 +167,22 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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= @@ -199,8 +197,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1: 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= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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= @@ -208,30 +206,29 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM= -k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +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= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/cli/cmd/resources/odigosconfig.go b/cli/cmd/resources/odigosconfig.go index 6d66c4df0..ba7fe99e6 100644 --- a/cli/cmd/resources/odigosconfig.go +++ b/cli/cmd/resources/odigosconfig.go @@ -49,10 +49,15 @@ func (a *odigosConfigResourceManager) Name() string { return "OdigosConfig" } func (a *odigosConfigResourceManager) InstallFromScratch(ctx context.Context) error { sizingProfile := k8sprofiles.FilterSizeProfiles(a.config.Profiles) + collectorGatewayConfig := GetGatewayConfigBasedOnSize(sizingProfile) - collectorNodeConfig := GetNodeCollectorConfigBasedOnSize(sizingProfile) a.config.CollectorGateway = collectorGatewayConfig - if a.config.CollectorNode != nil { + + collectorNodeConfig := GetNodeCollectorConfigBasedOnSize(sizingProfile) + if a.config.CollectorNode != nil && a.config.CollectorNode.CollectorOwnMetricsPort != 0 { + if collectorNodeConfig == nil { + collectorNodeConfig = &common.CollectorNodeConfiguration{} + } collectorNodeConfig.CollectorOwnMetricsPort = a.config.CollectorNode.CollectorOwnMetricsPort } a.config.CollectorNode = collectorNodeConfig @@ -77,16 +82,22 @@ func GetNodeCollectorConfigBasedOnSize(profile common.ProfileName) *common.Colle return &common.CollectorNodeConfiguration{ RequestMemoryMiB: 150, LimitMemoryMiB: 300, + RequestCPUm: 150, + LimitCPUm: 300, } case k8sprofiles.SizeMProfile.ProfileName: return &common.CollectorNodeConfiguration{ RequestMemoryMiB: 250, LimitMemoryMiB: 500, + RequestCPUm: 250, + LimitCPUm: 500, } case k8sprofiles.SizeLProfile.ProfileName: return &common.CollectorNodeConfiguration{ RequestMemoryMiB: 500, LimitMemoryMiB: 750, + RequestCPUm: 500, + LimitCPUm: 750, } } } diff --git a/cli/cmd/resources/ui.go b/cli/cmd/resources/ui.go index 23e34ee09..00b4f86df 100644 --- a/cli/cmd/resources/ui.go +++ b/cli/cmd/resources/ui.go @@ -250,7 +250,7 @@ func NewUIClusterRole() *rbacv1.ClusterRole { }, { APIGroups: []string{"apps"}, - Resources: []string{"deployments", "statefulsets", "daemonsets"}, + Resources: []string{"deployments", "statefulsets", "daemonsets", "replicasets"}, Verbs: []string{"get", "list", "watch", "patch", "update"}, }, { @@ -314,7 +314,7 @@ func NewUIService(ns string) *corev1.Service { Port: 3000, }, { - Name: "beta-ui", + Name: "legacy-ui", Port: 3001, }, { diff --git a/cli/cmd/ui.go b/cli/cmd/ui.go index 963aea9cc..33a48ea37 100644 --- a/cli/cmd/ui.go +++ b/cli/cmd/ui.go @@ -26,8 +26,8 @@ import ( ) const ( - defaultPort = 3000 - betaDefaultPort = 3001 + defaultPort = 3000 + legacyDefaultPort = 3001 ) // uiCmd represents the ui command @@ -50,14 +50,15 @@ var uiCmd = &cobra.Command{ } } - betaFlag, _ := cmd.Flags().GetBool("beta") + legacyFlag, _ := cmd.Flags().GetBool("legacy") localPort := cmd.Flag("port").Value.String() clusterPort := defaultPort - if betaFlag { - clusterPort = betaDefaultPort + if legacyFlag { + clusterPort = legacyDefaultPort } - + fmt.Printf("Is legacy: %v\n", legacyFlag) + fmt.Printf("Cluster port: %d\n", clusterPort) localAddress := cmd.Flag("address").Value.String() uiPod, err := findOdigosUIPod(client, ctx, ns) if err != nil { @@ -163,5 +164,5 @@ func init() { rootCmd.AddCommand(uiCmd) uiCmd.Flags().Int("port", defaultPort, "Port to listen on") uiCmd.Flags().String("address", "localhost", "Address to listen on") - uiCmd.Flags().Bool("beta", false, "use new experimental UI") + uiCmd.Flags().Bool("legacy", false, "Use the legacy UI port") } diff --git a/cli/go.mod b/cli/go.mod index 7143f02a2..72073c3c8 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -11,10 +11,10 @@ require ( github.com/odigos-io/odigos/k8sutils v0.0.0 github.com/spf13/cobra v1.8.1 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.31.3 - k8s.io/apiextensions-apiserver v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/client-go v0.31.3 + k8s.io/api v0.32.0 + k8s.io/apiextensions-apiserver v0.32.0 + k8s.io/apimachinery v0.32.0 + k8s.io/client-go v0.32.0 sigs.k8s.io/yaml v1.4.0 ) @@ -25,11 +25,12 @@ require ( github.com/gorilla/websocket v1.5.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/moby/spdystream v0.4.0 // indirect + github.com/moby/spdystream v0.5.0 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect 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 ) @@ -47,7 +48,6 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -59,20 +59,20 @@ require ( go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect go.uber.org/multierr v1.11.0 - golang.org/x/net v0.29.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/time v0.6.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/time v0.7.0 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 // indirect - k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + 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 ) replace ( diff --git a/cli/go.sum b/cli/go.sum index 8eb289860..341f2ee71 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -47,16 +47,14 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 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/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -78,8 +76,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= +github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= 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= @@ -89,10 +87,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -126,8 +124,8 @@ 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/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -136,8 +134,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -148,30 +146,30 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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= @@ -179,32 +177,31 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= -k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= -k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= +k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= +k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 h1:1dWzkmJrrprYvjGwh9kEUxmcUV/CtNU8QM7h1FLWQOo= -k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +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-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +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= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/common/go.mod b/common/go.mod index 5f8b39c00..5a56cf5dc 100644 --- a/common/go.mod +++ b/common/go.mod @@ -10,7 +10,7 @@ require ( require ( github.com/kr/pretty v0.3.1 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect golang.org/x/crypto v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) @@ -24,7 +24,7 @@ require ( github.com/mattn/go-isatty v0.0.12 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opentelemetry.io/otel v1.29.0 - golang.org/x/sys v0.18.0 // indirect + golang.org/x/sys v0.20.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/common/go.sum b/common/go.sum index 0119df611..a3e62e022 100644 --- a/common/go.sum +++ b/common/go.sum @@ -34,8 +34,8 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK 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/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -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/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 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= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= @@ -46,8 +46,8 @@ golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/common/odigos_config.go b/common/odigos_config.go index bb82cdf91..33a028875 100644 --- a/common/odigos_config.go +++ b/common/odigos_config.go @@ -17,6 +17,16 @@ type CollectorNodeConfiguration struct { // default value is 2x the memory request. LimitMemoryMiB int `json:"limitMemoryMiB,omitempty"` + // RequestCPUm is the CPU request for the node collector daemonset. + // it will be embedded in the daemonset as a resource request of the form "cpu: m" + // default value is 250m + RequestCPUm int `json:"requestCPUm,omitempty"` + + // LimitCPUm is the CPU limit for the node collector daemonset. + // it will be embedded in the daemonset as a resource limit of the form "cpu: m" + // default value is 500m + LimitCPUm int `json:"limitCPUm,omitempty"` + // this parameter sets the "limit_mib" parameter in the memory limiter configuration for the node collector. // it is the hard limit after which a force garbage collection will be performed. // if not set, it will be 50Mi below the memory request. diff --git a/destinations/go.mod b/destinations/go.mod index 813187e17..1a0569699 100644 --- a/destinations/go.mod +++ b/destinations/go.mod @@ -3,7 +3,7 @@ module github.com/odigos-io/odigos/destinations go 1.23.0 require ( - github.com/odigos-io/odigos/common v1.0.48 + github.com/odigos-io/odigos/common v0.0.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/destinations/go.sum b/destinations/go.sum index e2ad6d10a..d7de70577 100644 --- a/destinations/go.sum +++ b/destinations/go.sum @@ -13,8 +13,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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/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/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 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= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= diff --git a/docs/pipeline/configuration.mdx b/docs/pipeline/configuration.mdx index 9053f232f..fac334997 100644 --- a/docs/pipeline/configuration.mdx +++ b/docs/pipeline/configuration.mdx @@ -36,11 +36,11 @@ Sizing Profiles `size_s`, `size_m`, `size_l` are pre-defined configurations desi **Node Data Collection Collector**: -| Profile | Request Memory (Mi) | Limit Memory (Mi) | -|----------|----------------------|-------------------| -| `size_s` | **150Mi** | **300Mi** | -| `size_m` | **250Mi** | **500Mi** | -| `size_l` | **500Mi** | **750Mi** | +| Profile | Request Memory (Mi) | Limit Memory (Mi) | Request CPU (m) | Limit CPU (m) +|----------|----------------------|-------------------| ---------------------|-------------------| +| `size_s` | **150Mi** | **300Mi** | **150m** | **300m** | +| `size_m` | **250Mi** | **500Mi** | **250m** | **500m** | +| `size_l` | **500Mi** | **750Mi** | **500m** | **750m** | To use profiles, you need to use the [Odigos CLI Command for Profiles](/cli/odigos_profile). @@ -96,27 +96,37 @@ collectorNode: # RequestMemoryMiB is the memory request for the node collector daemonset. # it will be embedded in the daemonset as a resource request of the form "memory: Mi" # default value is 250Mi - RequestMemoryMiB int `json:"requestMemoryMiB,omitempty"` + requestMemoryMiB: 250 # LimitMemoryMiB is the memory limit for the node collector daemonset. # it will be embedded in the daemonset as a resource limit of the form "memory: Mi" # default value is 2x the memory request. - LimitMemoryMiB int `json:"limitMemoryMiB,omitempty"` + limitMemoryMiB: 500 + + # RequestCPUm is the CPU request for the node collector daemonset. + # it will be embedded in the daemonset as a resource request of the form "cpu: m" + # default value is 250m + requestCPUm: 250 + + # LimitCPUm is the CPU limit for the node collector daemonset. + # it will be embedded in the daemonset as a resource limit of the form "cpu: m" + # default value is 500m + limitCPUm: 500 # this parameter sets the "limit_mib" parameter in the memory limiter configuration for the node collector. # it is the hard limit after which a force garbage collection will be performed. # if not set, it will be 50Mi below the memory request. - MemoryLimiterLimitMiB int `json:"memoryLimiterLimitMiB,omitempty"` + memoryLimiterLimitMiB: # this parameter sets the "spike_limit_mib" parameter in the memory limiter configuration for the node collector. # note that this is not the processor soft limit, but the diff in Mib between the hard limit and the soft limit. # if not set, this will be set to 20% of the hard limit (so the soft limit will be 80% of the hard limit). - MemoryLimiterSpikeLimitMiB int `json:"memoryLimiterSpikeLimitMiB,omitempty"` + memoryLimiterSpikeLimitMiB: # the GOMEMLIMIT environment variable value for the node collector daemonset. # this is when go runtime will start garbage collection. # if not specified, it will be set to 80% of the hard limit of the memory limiter. - GoMemLimitMib int `json:"goMemLimitMiB,omitempty"` + goMemLimitMiB: ``` diff --git a/docs/quickstart/installation.mdx b/docs/quickstart/installation.mdx index c9ae69973..70b347d8a 100644 --- a/docs/quickstart/installation.mdx +++ b/docs/quickstart/installation.mdx @@ -76,14 +76,13 @@ odigos ui By default, Odigos UI is available at http://localhost:3000. -## Experimental: New UI with --beta flag +## Using the Legacy UI ```bash -odigos ui --beta +odigos ui --legacy ``` - The --beta flag provides access to a new, improved UI. Keep in mind that this - is an experimental feature and may include changes or incomplete - functionality. + The new UI is the default experience. The legacy UI remains available for + users who prefer the previous version. diff --git a/frontend/main.go b/frontend/main.go index 1431fb740..74346aed8 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -42,14 +42,14 @@ import ( const ( defaultPort = 3000 - betaPort = 3001 + legacyPort = 3001 ) type Flags struct { Version bool Address string Port int - BetaPort int + LegacyPort int Debug bool KubeConfig string Namespace string @@ -68,7 +68,7 @@ func parseFlags() Flags { flag.BoolVar(&flags.Version, "version", false, "Print Odigos UI version.") flag.StringVar(&flags.Address, "address", "localhost", "Address to listen on") flag.IntVar(&flags.Port, "port", defaultPort, "Port to listen on") - flag.IntVar(&flags.BetaPort, "beta-port", betaPort, "Port to listen on for beta UI") + flag.IntVar(&flags.LegacyPort, "legacy-port", legacyPort, "Port to listen on for legacy UI") flag.BoolVar(&flags.Debug, "debug", false, "Enable debug mode") flag.StringVar(&flags.KubeConfig, "kubeconfig", defaultKubeConfig, "Path to kubeconfig file") flag.StringVar(&flags.Namespace, "namespace", consts.DefaultOdigosNamespace, "Kubernetes namespace where Odigos is installed") @@ -334,20 +334,18 @@ func main() { r.GET("/api/events", sse.HandleSSEConnections) d.GET("/api/events", sse.HandleSSEConnections) - log.Println("Starting Odigos UI...") - log.Printf("Odigos UI is available at: http://%s:%d", flags.Address, flags.BetaPort) + 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.BetaPort)) + err = r.Run(fmt.Sprintf("%s:%d", flags.Address, flags.Port)) if err != nil { log.Fatalf("Error starting server: %s", err) } }() go func() { - log.Println("Starting Odigos Dep UI...") - log.Printf("Odigos UI is available at: http://%s:%d", flags.Address, flags.Port) - err = d.Run(fmt.Sprintf("%s:%d", flags.Address, flags.Port)) + log.Printf("Odigos Legacy UI is available at: http://%s:%d", flags.Address, flags.LegacyPort) + err = d.Run(fmt.Sprintf("%s:%d", flags.Address, flags.LegacyPort)) if err != nil { log.Fatalf("Error starting server: %s", err) } diff --git a/frontend/webapp/app/(overview)/overview/page.tsx b/frontend/webapp/app/(overview)/overview/page.tsx index c24075f34..5d971be73 100644 --- a/frontend/webapp/app/(overview)/overview/page.tsx +++ b/frontend/webapp/app/(overview)/overview/page.tsx @@ -1,6 +1,7 @@ 'use client'; import React from 'react'; import dynamic from 'next/dynamic'; +import { useSSE } from '@/hooks'; const ToastList = dynamic(() => import('@/components/notification/toast-list'), { ssr: false }); const AllDrawers = dynamic(() => import('@/components/overview/all-drawers'), { ssr: false }); @@ -8,6 +9,8 @@ const AllModals = dynamic(() => import('@/components/overview/all-modals'), { ss const OverviewDataFlowContainer = dynamic(() => import('@/containers/main/overview/overview-data-flow'), { ssr: false }); export default function MainPage() { + useSSE(); + return ( <> diff --git a/frontend/webapp/app/globals.css b/frontend/webapp/app/globals.css index 302fd6694..37f548102 100644 --- a/frontend/webapp/app/globals.css +++ b/frontend/webapp/app/globals.css @@ -2,6 +2,7 @@ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Kode+Mono:wght@100;200;300;400;500;600;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap'); * { scrollbar-color: black transparent; diff --git a/frontend/webapp/app/layout.tsx b/frontend/webapp/app/layout.tsx index 62d808b5d..a662b286e 100644 --- a/frontend/webapp/app/layout.tsx +++ b/frontend/webapp/app/layout.tsx @@ -1,7 +1,6 @@ 'use client'; import './globals.css'; import React from 'react'; -import { useSSE } from '@/hooks'; import { METADATA } from '@/utils'; import { ApolloWrapper } from '@/lib'; import { ThemeProviderWrapper } from '@/styles'; @@ -15,12 +14,9 @@ const LAYOUT_STYLE: React.CSSProperties = { }; export default function RootLayout({ children }: { children: React.ReactNode }) { - useSSE(); - return ( - {METADATA.title} @@ -28,6 +24,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) + {METADATA.title} diff --git a/frontend/webapp/app/page.tsx b/frontend/webapp/app/page.tsx index de97cf7be..9df649d3c 100644 --- a/frontend/webapp/app/page.tsx +++ b/frontend/webapp/app/page.tsx @@ -1,20 +1,22 @@ 'use client'; 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 { useNotify, useConfig } from '@/hooks'; +import { useNotificationStore } from '@/store'; import { FadeLoader } from '@/reuseable-components'; -import { ROUTES, CONFIG, NOTIFICATION } from '@/utils'; -import { CenterThis } from '@/styles'; export default function App() { const router = useRouter(); - const notify = useNotify(); const { data, error } = useConfig(); + const { addNotification } = useNotificationStore(); useEffect(() => { if (error) { - notify({ - type: NOTIFICATION.ERROR, + addNotification({ + type: NOTIFICATION_TYPE.ERROR, title: error.name, message: error.message, }); diff --git a/frontend/webapp/components/describe-odigos/index.tsx b/frontend/webapp/components/describe-odigos/index.tsx new file mode 100644 index 000000000..28c78de36 --- /dev/null +++ b/frontend/webapp/components/describe-odigos/index.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import Image from 'next/image'; +import theme from '@/styles/theme'; +import { IconButton } from '@/reuseable-components'; +import { DRAWER_OTHER_TYPES, useDrawerStore } from '@/store'; + +export const DescribeOdigos = () => { + const { setSelectedItem } = useDrawerStore(); + const handleClick = () => setSelectedItem({ type: DRAWER_OTHER_TYPES.DESCRIBE_ODIGOS, id: DRAWER_OTHER_TYPES.DESCRIBE_ODIGOS }); + + return ( + + logo + + ); +}; diff --git a/frontend/webapp/components/index.ts b/frontend/webapp/components/index.ts index 73363b759..c23fdeebd 100644 --- a/frontend/webapp/components/index.ts +++ b/frontend/webapp/components/index.ts @@ -1,6 +1,7 @@ -export * from './setup'; -export * from './overview'; export * from './common'; +export * from './describe-odigos'; export * from './main'; export * from './modals'; export * from './notification'; +export * from './overview'; +export * from './setup'; diff --git a/frontend/webapp/components/main/header/index.tsx b/frontend/webapp/components/main/header/index.tsx index fbfb1be26..b4ddeb2c2 100644 --- a/frontend/webapp/components/main/header/index.tsx +++ b/frontend/webapp/components/main/header/index.tsx @@ -6,14 +6,14 @@ import { PlatformTypes } from '@/types'; import { PlatformTitle } from './cp-title'; import { useConnectionStore } from '@/store'; import { ConnectionStatus } from '@/reuseable-components'; -import { NotificationManager } from '@/components/notification'; +import { DescribeOdigos, NotificationManager } from '@/components'; interface MainHeaderProps {} const HeaderContainer = styled(FlexRow)` width: 100%; padding: 12px 0; - background-color: ${({ theme }) => theme.colors.darker_grey}; + background-color: ${({ theme }) => theme.colors.dark_grey}; border-bottom: 1px solid rgba(249, 249, 249, 0.16); `; @@ -42,6 +42,7 @@ export const MainHeader: React.FC = () => { + ); diff --git a/frontend/webapp/components/modals/cancel-warning/index.tsx b/frontend/webapp/components/modals/cancel-warning/index.tsx index 82221bc9f..5e4b760d1 100644 --- a/frontend/webapp/components/modals/cancel-warning/index.tsx +++ b/frontend/webapp/components/modals/cancel-warning/index.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { NOTIFICATION_TYPE } from '@/types'; import { WarningModal } from '@/reuseable-components'; interface Props { @@ -18,7 +19,7 @@ const CancelWarning: React.FC = ({ isOpen, noOverlay, name, onApprove, on description='Are you sure you want to cancel?' approveButton={{ text: 'Confirm', - variant: 'warning', + variant: NOTIFICATION_TYPE.WARNING, onClick: onApprove, }} denyButton={{ diff --git a/frontend/webapp/components/modals/delete-warning/index.tsx b/frontend/webapp/components/modals/delete-warning/index.tsx index 01b7978c3..e59fa881a 100644 --- a/frontend/webapp/components/modals/delete-warning/index.tsx +++ b/frontend/webapp/components/modals/delete-warning/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { OVERVIEW_ENTITY_TYPES } from '@/types'; import { WarningModal } from '@/reuseable-components'; +import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES } from '@/types'; interface Props { isOpen: boolean; @@ -24,7 +24,7 @@ const DeleteWarning: React.FC = ({ isOpen, noOverlay, name, type, isLastI note={ isLastItem ? { - type: 'warning', + type: NOTIFICATION_TYPE.WARNING, title: `You're about to ${actionText} the last ${type}`, message: 'This will break your pipeline!', } diff --git a/frontend/webapp/components/notification/notification-manager.tsx b/frontend/webapp/components/notification/notification-manager.tsx index 20cb5c66f..c0c752bfe 100644 --- a/frontend/webapp/components/notification/notification-manager.tsx +++ b/frontend/webapp/components/notification/notification-manager.tsx @@ -6,32 +6,8 @@ import { useNotificationStore } from '@/store'; import { ACTION, getStatusIcon } from '@/utils'; import { useOnClickOutside, useTimeAgo } from '@/hooks'; import theme, { hexPercentValues } from '@/styles/theme'; -import { NoDataFound, Text } from '@/reuseable-components'; -import type { Notification, NotificationType } from '@/types'; - -const BellIcon = styled.div` - position: relative; - width: 36px; - height: 36px; - border-radius: 100%; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - &:hover { - background-color: ${({ theme }) => theme.colors.white_opacity['008']}; - } -`; - -const LiveBadge = styled.div` - position: absolute; - top: 8px; - right: 8px; - width: 6px; - height: 6px; - border-radius: 100%; - background-color: ${({ theme }) => theme.colors.orange_og}; -`; +import { NOTIFICATION_TYPE, type Notification } from '@/types'; +import { IconButton, NoDataFound, Text } from '@/reuseable-components'; const RelativeContainer = styled.div` position: relative; @@ -106,15 +82,14 @@ export const NotificationManager = () => { return ( - - {!!unseenCount && } + logo - + {isOpen && ( - + - Notifications{' '} + Notifications {!!unseenCount && ( {unseenCount} new @@ -151,7 +126,7 @@ const NotifCard = styled.div` } `; -const StatusIcon = styled.div<{ $type: NotificationType }>` +const StatusIcon = styled.div<{ $type: NOTIFICATION_TYPE }>` background-color: ${({ $type, theme }) => theme.text[$type] + hexPercentValues['012']}; border-radius: 8px; width: 36px; @@ -200,7 +175,7 @@ const NotificationListItem: React.FC void }> = ( } }} > - + status diff --git a/frontend/webapp/components/overview/add-entity/index.tsx b/frontend/webapp/components/overview/add-entity/index.tsx index b9c449995..5323720ee 100644 --- a/frontend/webapp/components/overview/add-entity/index.tsx +++ b/frontend/webapp/components/overview/add-entity/index.tsx @@ -1,11 +1,11 @@ +import React, { useState, useRef } from 'react'; import Image from 'next/image'; import theme from '@/styles/theme'; -import { useOnClickOutside } from '@/hooks'; -import React, { useState, useRef } from 'react'; +import { useModalStore } from '@/store'; import styled, { css } from 'styled-components'; -import { useBooleanStore, useModalStore } from '@/store'; -import { DropdownOption, OVERVIEW_ENTITY_TYPES } from '@/types'; +import { useComputePlatform, useOnClickOutside } from '@/hooks'; import { Button, FadeLoader, Text } from '@/reuseable-components'; +import { type DropdownOption, OVERVIEW_ENTITY_TYPES } from '@/types'; // Styled components for the dropdown UI const Container = styled.div` @@ -65,13 +65,13 @@ const DEFAULT_OPTIONS: DropdownOption[] = [ { id: OVERVIEW_ENTITY_TYPES.DESTINATION, value: 'Destination' }, ]; -interface AddEntityButtonDropdownProps { +interface Props { options?: DropdownOption[]; placeholder?: string; } -const AddEntity: React.FC = ({ options = DEFAULT_OPTIONS, placeholder = 'ADD...' }) => { - const { isPolling } = useBooleanStore(); +const AddEntity: React.FC = ({ options = DEFAULT_OPTIONS, placeholder = 'ADD...' }) => { + const { loading } = useComputePlatform(); const { currentModal, setCurrentModal } = useModalStore(); const [isDropdownOpen, setIsDropdownOpen] = useState(false); @@ -91,7 +91,7 @@ const AddEntity: React.FC = ({ options = DEFAULT_O return ( - {isPolling ? : Add} + {loading ? : Add} {placeholder} diff --git a/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx b/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx new file mode 100644 index 000000000..4b5d3b87f --- /dev/null +++ b/frontend/webapp/components/overview/all-drawers/describe-drawer.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import styled from 'styled-components'; +import { useDescribeOdigos } from '@/hooks'; +import { DATA_CARDS, safeJsonStringify } from '@/utils'; +import { DataCard, DataCardFieldTypes } from '@/reuseable-components'; +import OverviewDrawer from '@/containers/main/overview/overview-drawer'; + +interface Props {} + +const DataContainer = styled.div` + display: flex; + flex-direction: column; + gap: 12px; +`; + +export const DescribeDrawer: React.FC = () => { + const { data: describe } = useDescribeOdigos(); + + return ( + + + + + + ); +}; diff --git a/frontend/webapp/components/overview/all-drawers/index.tsx b/frontend/webapp/components/overview/all-drawers/index.tsx index 65cbb3b59..8f9329437 100644 --- a/frontend/webapp/components/overview/all-drawers/index.tsx +++ b/frontend/webapp/components/overview/all-drawers/index.tsx @@ -1,12 +1,13 @@ import React from 'react'; -import { useDrawerStore } from '@/store'; 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'; const AllDrawers = () => { const selected = useDrawerStore(({ selectedItem }) => selectedItem); - if (!selected?.item) return null; + if (!selected?.type) return null; switch (selected.type) { case OVERVIEW_ENTITY_TYPES.RULE: @@ -21,6 +22,9 @@ const AllDrawers = () => { case OVERVIEW_ENTITY_TYPES.DESTINATION: return ; + case DRAWER_OTHER_TYPES.DESCRIBE_ODIGOS: + return ; + default: return <>; } diff --git a/frontend/webapp/components/setup/header/index.tsx b/frontend/webapp/components/setup/header/index.tsx index eaf1b3bc4..0c22d70ba 100644 --- a/frontend/webapp/components/setup/header/index.tsx +++ b/frontend/webapp/components/setup/header/index.tsx @@ -18,7 +18,7 @@ const HeaderContainer = styled.div` justify-content: space-between; padding: 0 24px 0 32px; align-items: center; - background-color: ${({ theme }) => theme.colors.darker_grey}; + background-color: ${({ theme }) => theme.colors.dark_grey}; border-bottom: 1px solid rgba(249, 249, 249, 0.16); height: 80px; `; diff --git a/frontend/webapp/containers/main/actions/action-drawer/index.tsx b/frontend/webapp/containers/main/actions/action-drawer/index.tsx index d0b0936e7..fe0d9c893 100644 --- a/frontend/webapp/containers/main/actions/action-drawer/index.tsx +++ b/frontend/webapp/containers/main/actions/action-drawer/index.tsx @@ -3,10 +3,10 @@ import buildCard from './build-card'; import { ActionFormBody } from '../'; import styled from 'styled-components'; import { useDrawerStore } from '@/store'; -import { ACTION, DATA_CARDS, getActionIcon } from '@/utils'; import buildDrawerItem from './build-drawer-item'; import { DataCard } from '@/reuseable-components'; import { useActionCRUD, useActionFormData } from '@/hooks'; +import { ACTION, DATA_CARDS, getActionIcon } from '@/utils'; import OverviewDrawer from '../../overview/overview-drawer'; import { ACTION_OPTIONS } from '../action-modal/action-options'; import { OVERVIEW_ENTITY_TYPES, type ActionDataParsed } from '@/types'; 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 89360955a..06b2086c6 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 @@ -1,10 +1,11 @@ import React, { useState } from 'react'; import Image from 'next/image'; import styled from 'styled-components'; +import { extractMonitors } from '@/utils'; import { DeleteWarning } from '@/components'; import { IAppState, useAppStore } from '@/store'; import { OVERVIEW_ENTITY_TYPES, type ConfiguredDestination } from '@/types'; -import { Button, DataCardFields, Divider, ExtendIcon, Text } from '@/reuseable-components'; +import { DataCardFields, DataTab, IconButton } from '@/reuseable-components'; const Container = styled.div` display: flex; @@ -18,128 +19,25 @@ const Container = styled.div` overflow-y: scroll; `; -const ListItem = styled.div` - width: 100%; - border-radius: 16px; - background: ${({ theme }) => theme.colors.translucent_bg}; -`; - -const ListItemBody = styled.div` - width: 100%; - padding: 16px; -`; - -const ListItemHeader = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - padding: 16px 0px; -`; - -const ListItemContent = styled.div` - display: flex; - gap: 12px; - margin-left: 16px; -`; - -const DestinationIconWrapper = styled.div` - display: flex; - width: 36px; - height: 36px; - justify-content: center; - align-items: center; - gap: 8px; - border-radius: 8px; - background: linear-gradient(180deg, rgba(249, 249, 249, 0.06) 0%, rgba(249, 249, 249, 0.02) 100%); -`; - -const SignalsWrapper = styled.div` - display: flex; - align-items: center; - gap: 4px; -`; - -const SignalText = styled(Text)` - color: rgba(249, 249, 249, 0.8); - font-size: 10px; - text-transform: capitalize; -`; - -const TextWrapper = styled.div` - display: flex; - flex-direction: column; - height: 36px; - justify-content: space-between; -`; - -const IconsContainer = styled.div` - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; -`; - -const IconButton = styled(Button)<{ $expand?: boolean }>` - transition: background 0.3s ease 0s, transform 0.3s ease 0s; - transform: ${({ $expand }) => ($expand ? 'rotate(-180deg)' : 'rotate(0deg)')}; -`; - -const ConfiguredDestinationsListItem: React.FC<{ item: ConfiguredDestination; isLastItem: boolean }> = ({ item, isLastItem }) => { - const [expand, setExpand] = useState(false); - const [deleteWarning, setDeleteWarning] = useState(false); +const ListItem: React.FC<{ item: ConfiguredDestination; isLastItem: boolean }> = ({ item, isLastItem }) => { const { removeConfiguredDestination } = useAppStore((state) => state); - - function renderSupportedSignals(item: ConfiguredDestination) { - const supportedSignals = item.exportedSignals; - const signals = Object.keys(supportedSignals); - const supportedSignalsList = signals.filter((signal) => supportedSignals[signal].supported); - - return Object.keys(supportedSignals).map( - (signal, index) => - supportedSignals[signal] && ( - - monitor - - {signal} - {index < supportedSignalsList.length - 1 && ·} - - ), - ); - } + const [deleteWarning, setDeleteWarning] = useState(false); return ( <> - - - - - destination - - - {item.displayName} - {renderSupportedSignals(item)} - - - - - setDeleteWarning(true)}> - delete - - - setExpand(!expand)}> - - - - - - {expand && ( - - - - + } + renderActions={() => ( + setDeleteWarning(true)}> + delete + )} - + /> {data.map(({ stored }) => ( - + ))} ); diff --git a/frontend/webapp/containers/main/destinations/add-destination/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/index.tsx index b4bc07f54..0e293f753 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/index.tsx @@ -2,15 +2,16 @@ import React, { useState } from 'react'; import Image from 'next/image'; import { ROUTES } from '@/utils'; import theme from '@/styles/theme'; +import { CenterThis } from '@/styles'; import { useAppStore } from '@/store'; import styled from 'styled-components'; import { SetupHeader } from '@/components'; import { useRouter } from 'next/navigation'; -import { useDestinationCRUD, useSourceCRUD } from '@/hooks'; +import { NOTIFICATION_TYPE } from '@/types'; import { DestinationModal } from '../destination-modal'; +import { useDestinationCRUD, useSourceCRUD } from '@/hooks'; import { ConfiguredDestinationsList } from './configured-destinations-list'; import { Button, FadeLoader, NotificationNote, SectionTitle, Text } from '@/reuseable-components'; -import { CenterThis } from '@/styles'; const ContentWrapper = styled.div` width: 640px; @@ -93,7 +94,7 @@ export function AddDestinationContainer() { {!isLoading && isSourcesListEmpty() && ( (); + const [connectionStatus, setConnectionStatus] = useState(); const dirtyForm = () => { setIsFormDirty(true); @@ -87,11 +87,11 @@ export function DestinationFormBody({ isUpdate, destination, formData, formError status={connectionStatus} onError={() => { setIsFormDirty(false); - setConnectionStatus('error'); + setConnectionStatus(NOTIFICATION_TYPE.ERROR); }} onSuccess={() => { setIsFormDirty(false); - setConnectionStatus('success'); + setConnectionStatus(NOTIFICATION_TYPE.SUCCESS); }} validateForm={validateForm} /> @@ -101,9 +101,9 @@ export function DestinationFormBody({ isUpdate, destination, formData, formError {testConnectionSupported && ( - {connectionStatus === 'error' && } - {connectionStatus === 'success' && } - {!connectionStatus && } + {connectionStatus === NOTIFICATION_TYPE.ERROR && } + {connectionStatus === NOTIFICATION_TYPE.SUCCESS && } + {!connectionStatus && } )} diff --git a/frontend/webapp/containers/main/destinations/destination-form-body/test-connection/index.tsx b/frontend/webapp/containers/main/destinations/destination-form-body/test-connection/index.tsx index 15af5b783..a08ad2ac4 100644 --- a/frontend/webapp/containers/main/destinations/destination-form-body/test-connection/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-form-body/test-connection/index.tsx @@ -3,22 +3,22 @@ import Image from 'next/image'; import theme from '@/styles/theme'; import { getStatusIcon } from '@/utils'; import { useTestConnection } from '@/hooks'; -import type { DestinationInput } from '@/types'; import styled, { css } from 'styled-components'; import { Button, FadeLoader, Text } from '@/reuseable-components'; +import { type DestinationInput, NOTIFICATION_TYPE } from '@/types'; -type Status = 'success' | 'error'; +export type ConnectionStatus = NOTIFICATION_TYPE.SUCCESS | NOTIFICATION_TYPE.ERROR; interface Props { destination: DestinationInput; disabled: boolean; - status?: Status; + status?: ConnectionStatus; onError: () => void; onSuccess: () => void; validateForm: () => boolean; } -const ActionButton = styled(Button)<{ $status?: Status }>` +const ActionButton = styled(Button)<{ $status?: ConnectionStatus }>` display: flex; align-items: center; gap: 8px; diff --git a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/destination-list-item/index.tsx b/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/destination-list-item/index.tsx deleted file mode 100644 index 8101829b0..000000000 --- a/frontend/webapp/containers/main/destinations/destination-modal/choose-destination-body/destinations-list/destination-list-item/index.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React from 'react'; -import Image from 'next/image'; -import styled from 'styled-components'; -import { DestinationTypeItem } from '@/types'; -import { Text } from '@/reuseable-components'; - -const HoverTextWrapper = styled.div` - visibility: hidden; -`; - -const ListItem = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - padding: 16px 0px; - transition: background 0.3s; - border-radius: 16px; - cursor: pointer; - background: rgba(249, 249, 249, 0.04); - - &:hover { - background: rgba(249, 249, 249, 0.08); - } - &:last-child { - margin-bottom: 24px; - } - - &:hover { - ${HoverTextWrapper} { - visibility: visible; - } - } -`; - -const ListItemContent = styled.div` - margin-left: 16px; - display: flex; - gap: 12px; -`; - -const DestinationIconWrapper = styled.div` - display: flex; - width: 36px; - height: 36px; - justify-content: center; - align-items: center; - gap: 8px; - border-radius: 8px; - background: linear-gradient(180deg, rgba(249, 249, 249, 0.06) 0%, rgba(249, 249, 249, 0.02) 100%); -`; - -const SignalsWrapper = styled.div` - display: flex; - gap: 4px; -`; - -const SignalText = styled(Text)` - color: rgba(249, 249, 249, 0.8); - font-size: 10px; - text-transform: capitalize; -`; - -const TextWrapper = styled.div` - display: flex; - flex-direction: column; - height: 36px; - justify-content: space-between; -`; - -const HoverText = styled(Text)` - font-family: ${({ theme }) => theme.font_family.secondary}; - text-transform: uppercase; - margin-right: 16px; -`; - -interface DestinationListItemProps { - item: DestinationTypeItem; - onSelect: (item: DestinationTypeItem) => void; -} - -export const DestinationListItem: React.FC = ({ item, onSelect }) => { - const renderSupportedSignals = () => { - const signals = Object.keys(item.supportedSignals).filter((signal) => item.supportedSignals[signal].supported); - - return signals.map((signal, index) => ( - - {signal} - {index < signals.length - 1 && ·} - - )); - }; - - return ( - onSelect(item)}> - - - destination - - - {item.displayName} - {renderSupportedSignals()} - - - - {'Select'} - - - ); -}; 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 41bb135ec..c3ce504b4 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 @@ -3,14 +3,14 @@ import styled from 'styled-components'; import { DestinationTypeItem } from '@/types'; import { IDestinationListItem } from '@/hooks'; import { capitalizeFirstLetter } from '@/utils'; -import { DestinationListItem } from './destination-list-item'; -import { NoDataFound, SectionTitle } from '@/reuseable-components'; +import { DataTab, NoDataFound, SectionTitle } from '@/reuseable-components'; import { PotentialDestinationsList } from './potential-destinations-list'; const Container = styled.div` display: flex; flex-direction: column; align-self: stretch; + gap: 24px; max-height: calc(100vh - 450px); overflow-y: auto; @@ -44,12 +44,21 @@ const DestinationsList: React.FC = ({ items, setSelectedI ); } - return items.map((item) => { + return items.map((categoryItem) => { return ( - - - {item.items.map((categoryItem) => ( - + + + {categoryItem.items.map((destinationItem) => ( + destinationItem.supportedSignals[signal].supported)} + monitorsWithLabels + onClick={() => setSelectedItems(destinationItem)} + /> ))} ); 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 d52c75038..3fec4a5cc 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 @@ -2,8 +2,11 @@ import React from 'react'; import styled from 'styled-components'; import { DestinationTypeItem } from '@/types'; import { usePotentialDestinations } from '@/hooks'; -import { DestinationListItem } from '../destination-list-item'; -import { SectionTitle, SkeletonLoader } from '@/reuseable-components'; +import { DataTab, SectionTitle, SkeletonLoader } from '@/reuseable-components'; + +interface Props { + setSelectedItems: (item: DestinationTypeItem) => void; +} const ListsWrapper = styled.div` display: flex; @@ -11,14 +14,10 @@ const ListsWrapper = styled.div` gap: 12px; `; -interface PotentialDestinationsListProps { - setSelectedItems: (item: DestinationTypeItem) => void; -} - -export const PotentialDestinationsList: React.FC = ({ setSelectedItems }) => { - const { loading, data } = usePotentialDestinations(); +export const PotentialDestinationsList: React.FC = ({ setSelectedItems }) => { + const { data, loading } = usePotentialDestinations(); - if (!data.length) return null; + if (!data.length && !loading) return null; return ( @@ -28,7 +27,22 @@ export const PotentialDestinationsList: React.FC title='Detected by Odigos' description='Odigos detects destinations for which automatic connection is available. All data will be filled out automatically.' /> - {loading ? : data.map((item) => )} + {loading ? ( + + ) : ( + data.map((item) => ( + item.supportedSignals[signal].supported)} + monitorsWithLabels + onClick={() => setSelectedItems(item)} + /> + )) + )} ); }; 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 21ca7e8c8..04b6e5f4e 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx @@ -2,14 +2,14 @@ import React, { useMemo, useState } from 'react'; import buildCard from './build-card'; import { RuleFormBody } from '../'; import styled from 'styled-components'; -import { useDrawerStore } from '@/store'; import { DataCard } from '@/reuseable-components'; import buildDrawerItem from './build-drawer-item'; import { RULE_OPTIONS } from '../rule-modal/rule-options'; import OverviewDrawer from '../../overview/overview-drawer'; -import { ACTION, DATA_CARDS, FORM_ALERTS, getRuleIcon, NOTIFICATION } from '@/utils'; -import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData, useNotify } from '@/hooks'; -import { InstrumentationRuleType, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleSpec } from '@/types'; +import { useDrawerStore, useNotificationStore } from '@/store'; +import { ACTION, DATA_CARDS, FORM_ALERTS, getRuleIcon } from '@/utils'; +import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData } from '@/hooks'; +import { InstrumentationRuleType, NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type InstrumentationRuleSpec } from '@/types'; interface Props {} @@ -22,7 +22,7 @@ const FormContainer = styled.div` `; export const RuleDrawer: React.FC = () => { - const notify = useNotify(); + const { addNotification } = useNotificationStore(); const { selectedItem, setSelectedItem } = useDrawerStore(); const { formData, formErrors, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem } = useInstrumentationRuleFormData(); @@ -74,7 +74,7 @@ export const RuleDrawer: React.FC = () => { const handleEdit = (bool?: boolean) => { if (item.type === InstrumentationRuleType.UNKNOWN_TYPE) { - notify({ type: NOTIFICATION.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 }); } else { setIsEditing(typeof bool === 'boolean' ? bool : true); } diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx index f1b6f52cf..554fda070 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { ACTION } from '@/utils'; import { RuleFormBody } from '../'; +import { NOTIFICATION_TYPE } from '@/types'; import { CenterThis, ModalBody } from '@/styles'; import { RULE_OPTIONS, RuleOption } from './rule-options'; import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData, useKeyDown } from '@/hooks'; @@ -58,7 +59,7 @@ export const RuleModal: React.FC = ({ isOpen, onClose }) => { > - + {!!selectedItem?.type ? ( 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 5cffcba92..373c532f0 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 @@ -3,9 +3,10 @@ import nodeConfig from './node-config.json'; import { type EntityCounts } from './get-entity-counts'; import { type NodePositions } from './get-node-positions'; import { getActionIcon, getEntityIcon, getEntityLabel } from '@/utils'; -import { OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; +import { NODE_TYPES, OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; interface Params { + loading: boolean; entities: ComputePlatformMapped['computePlatform']['actions']; positions: NodePositions; unfilteredCounts: EntityCounts; @@ -28,14 +29,14 @@ const mapToNodeData = (entity: Params['entities'][0]) => { }; }; -export const buildActionNodes = ({ entities, positions, unfilteredCounts }: Params) => { +export const buildActionNodes = ({ loading, entities, positions, unfilteredCounts }: Params) => { const nodes: Node[] = []; const position = positions[OVERVIEW_ENTITY_TYPES.ACTION]; const unfilteredCount = unfilteredCounts[OVERVIEW_ENTITY_TYPES.ACTION]; nodes.push({ id: 'action-header', - type: 'header', + type: NODE_TYPES.HEADER, position: { x: positions[OVERVIEW_ENTITY_TYPES.ACTION]['x'], y: 0, @@ -48,10 +49,23 @@ export const buildActionNodes = ({ entities, positions, unfilteredCounts }: Para }, }); - if (!entities.length) { + if (loading) { + nodes.push({ + id: 'action-skeleton', + type: NODE_TYPES.SKELETON, + position: { + x: position['x'], + y: position['y'](), + }, + data: { + nodeWidth, + size: 3, + }, + }); + } else if (!entities.length) { nodes.push({ id: 'action-add', - type: 'add', + type: NODE_TYPES.ADD, position: { x: position['x'], y: position['y'](), @@ -67,7 +81,7 @@ export const buildActionNodes = ({ entities, positions, unfilteredCounts }: Para } else { nodes.push({ id: 'action-frame', - type: 'frame', + type: NODE_TYPES.FRAME, position: { x: position['x'] - framePadding, y: position['y']() - framePadding, @@ -81,7 +95,7 @@ export const buildActionNodes = ({ entities, positions, unfilteredCounts }: Para entities.forEach((action, idx) => { nodes.push({ id: `action-${idx}`, - type: 'base', + type: NODE_TYPES.BASE, extent: 'parent', parentId: 'action-frame', position: { 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 357c5bd2a..9f4ceaeec 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 @@ -3,9 +3,10 @@ import nodeConfig from './node-config.json'; import { type EntityCounts } from './get-entity-counts'; import { type NodePositions } from './get-node-positions'; import { extractMonitors, getEntityIcon, getEntityLabel, getHealthStatus } from '@/utils'; -import { OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; +import { NODE_TYPES, OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; interface Params { + loading: boolean; entities: ComputePlatformMapped['computePlatform']['destinations']; positions: NodePositions; unfilteredCounts: EntityCounts; @@ -27,14 +28,14 @@ const mapToNodeData = (entity: Params['entities'][0]) => { }; }; -export const buildDestinationNodes = ({ entities, positions, unfilteredCounts }: Params) => { +export const buildDestinationNodes = ({ loading, entities, positions, unfilteredCounts }: Params) => { const nodes: Node[] = []; const position = positions[OVERVIEW_ENTITY_TYPES.DESTINATION]; const unfilteredCount = unfilteredCounts[OVERVIEW_ENTITY_TYPES.DESTINATION]; nodes.push({ id: 'destination-header', - type: 'header', + type: NODE_TYPES.HEADER, position: { x: positions[OVERVIEW_ENTITY_TYPES.DESTINATION]['x'], y: 0, @@ -47,10 +48,23 @@ export const buildDestinationNodes = ({ entities, positions, unfilteredCounts }: }, }); - if (!entities.length) { + if (loading) { + nodes.push({ + id: 'destination-skeleton', + type: NODE_TYPES.SKELETON, + position: { + x: position['x'], + y: position['y'](), + }, + data: { + nodeWidth, + size: 3, + }, + }); + } else if (!entities.length) { nodes.push({ id: 'destination-add', - type: 'add', + type: NODE_TYPES.ADD, position: { x: position['x'], y: position['y'](), @@ -67,7 +81,7 @@ export const buildDestinationNodes = ({ entities, positions, unfilteredCounts }: entities.forEach((destination, idx) => { nodes.push({ id: `destination-${idx}`, - type: 'base', + type: NODE_TYPES.BASE, position: { x: position['x'], y: position['y'](idx), diff --git a/frontend/webapp/containers/main/overview/overview-data-flow/build-edges.ts b/frontend/webapp/containers/main/overview/overview-data-flow/build-edges.ts index 5b7d893ec..1632484d8 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/build-edges.ts +++ b/frontend/webapp/containers/main/overview/overview-data-flow/build-edges.ts @@ -1,7 +1,7 @@ import theme from '@/styles/theme'; import { formatBytes } from '@/utils'; import { type Edge, type Node } from '@xyflow/react'; -import { OVERVIEW_ENTITY_TYPES, STATUSES, WorkloadId, type OverviewMetricsResponse } from '@/types'; +import { EDGE_TYPES, NODE_TYPES, OVERVIEW_ENTITY_TYPES, STATUSES, WorkloadId, type OverviewMetricsResponse } from '@/types'; import nodeConfig from './node-config.json'; interface Params { @@ -18,7 +18,7 @@ const createEdge = (edgeId: string, params?: { label?: string; isMultiTarget?: b return { id: edgeId, - type: !!label ? 'labeled' : 'default', + type: !!label ? EDGE_TYPES.LABELED : 'default', source: sourceNodeId, target: targetNodeId, animated, @@ -32,47 +32,36 @@ export const buildEdges = ({ nodes, metrics, containerHeight }: Params) => { const actionNodeId = nodes.find(({ id: nodeId }) => ['action-frame', 'action-add'].includes(nodeId))?.id; nodes.forEach(({ type: nodeType, id: nodeId, data: { type: entityType, id: entityId, status }, position }) => { - if (nodeType === 'base') { - switch (entityType) { - case OVERVIEW_ENTITY_TYPES.SOURCE: { - const { namespace, name, kind } = entityId as WorkloadId; - const metric = metrics?.getOverviewMetrics.sources.find((m) => m.kind === kind && m.name === name && m.namespace === namespace); + if (nodeType === NODE_TYPES.EDGED && entityType === OVERVIEW_ENTITY_TYPES.SOURCE) { + const { namespace, name, kind } = entityId as WorkloadId; + const metric = metrics?.getOverviewMetrics.sources.find((m) => m.kind === kind && m.name === name && m.namespace === namespace); - const topLimit = -nodeHeight / 2 + framePadding; - const bottomLimit = containerHeight - nodeHeight + framePadding * 2 + topLimit; + const topLimit = -nodeHeight / 2 + framePadding; + const bottomLimit = Math.floor(containerHeight / nodeHeight) * nodeHeight - (nodeHeight / 2 + framePadding); - if (position.y >= topLimit && position.y <= bottomLimit) { - edges.push( - createEdge(`${nodeId}-to-${actionNodeId}`, { - animated: false, - isMultiTarget: false, - label: formatBytes(metric?.throughput), - isError: status === STATUSES.UNHEALTHY, - }), - ); - } - - break; - } - - case OVERVIEW_ENTITY_TYPES.DESTINATION: { - const metric = metrics?.getOverviewMetrics.destinations.find((m) => m.id === entityId); - - edges.push( - createEdge(`${actionNodeId}-to-${nodeId}`, { - animated: false, - isMultiTarget: true, - label: formatBytes(metric?.throughput), - isError: status === STATUSES.UNHEALTHY, - }), - ); + if (position.y >= topLimit && position.y <= bottomLimit) { + edges.push( + createEdge(`${nodeId}-to-${actionNodeId}`, { + animated: false, + isMultiTarget: false, + label: formatBytes(metric?.throughput), + isError: status === STATUSES.UNHEALTHY, + }), + ); + } + } - break; - } + if (nodeType === NODE_TYPES.BASE && entityType === OVERVIEW_ENTITY_TYPES.DESTINATION) { + const metric = metrics?.getOverviewMetrics.destinations.find((m) => m.id === entityId); - default: - break; - } + edges.push( + createEdge(`${actionNodeId}-to-${nodeId}`, { + animated: false, + isMultiTarget: true, + label: formatBytes(metric?.throughput), + isError: status === STATUSES.UNHEALTHY, + }), + ); } }); 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 08bbda592..5caa9e7c5 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 @@ -3,9 +3,10 @@ import nodeConfig from './node-config.json'; import { type EntityCounts } from './get-entity-counts'; import { type NodePositions } from './get-node-positions'; import { getEntityIcon, getEntityLabel, getRuleIcon } from '@/utils'; -import { OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; +import { NODE_TYPES, OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; interface Params { + loading: boolean; entities: ComputePlatformMapped['computePlatform']['instrumentationRules']; positions: NodePositions; unfilteredCounts: EntityCounts; @@ -27,14 +28,14 @@ const mapToNodeData = (entity: Params['entities'][0]) => { }; }; -export const buildRuleNodes = ({ entities, positions, unfilteredCounts }: Params) => { +export const buildRuleNodes = ({ loading, entities, positions, unfilteredCounts }: Params) => { const nodes: Node[] = []; const position = positions[OVERVIEW_ENTITY_TYPES.RULE]; const unfilteredCount = unfilteredCounts[OVERVIEW_ENTITY_TYPES.RULE]; nodes.push({ id: 'rule-header', - type: 'header', + type: NODE_TYPES.HEADER, position: { x: positions[OVERVIEW_ENTITY_TYPES.RULE]['x'], y: 0, @@ -47,10 +48,23 @@ export const buildRuleNodes = ({ entities, positions, unfilteredCounts }: Params }, }); - if (!entities.length) { + if (loading) { + nodes.push({ + id: 'rule-skeleton', + type: NODE_TYPES.SKELETON, + position: { + x: position['x'], + y: position['y'](), + }, + data: { + nodeWidth, + size: 3, + }, + }); + } else if (!entities.length) { nodes.push({ id: 'rule-add', - type: 'add', + type: NODE_TYPES.ADD, position: { x: position['x'], y: position['y'](), @@ -67,7 +81,7 @@ export const buildRuleNodes = ({ entities, positions, unfilteredCounts }: Params entities.forEach((rule, idx) => { nodes.push({ id: `rule-${idx}`, - type: 'base', + type: NODE_TYPES.BASE, position: { x: position['x'], y: position['y'](idx), 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 70166f1f6..26301a422 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,13 +4,13 @@ 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 { OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; +import { NODE_TYPES, OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, STATUSES, type ComputePlatformMapped } from '@/types'; interface Params { + loading: boolean; entities: ComputePlatformMapped['computePlatform']['k8sActualSources']; positions: NodePositions; unfilteredCounts: EntityCounts; - containerHeight: number; onScroll: (params: { clientHeight: number; scrollHeight: number; scrollTop: number }) => void; } @@ -20,6 +20,8 @@ const { nodeWidth, nodeHeight, framePadding } = nodeConfig; const mapToNodeData = (entity: Params['entities'][0]) => { return { nodeWidth, + nodeHeight, + framePadding, id: { namespace: entity.namespace, name: entity.name, @@ -34,14 +36,14 @@ const mapToNodeData = (entity: Params['entities'][0]) => { }; }; -export const buildSourceNodes = ({ entities, positions, unfilteredCounts, containerHeight, onScroll }: Params) => { +export const buildSourceNodes = ({ loading, entities, positions, unfilteredCounts, containerHeight, onScroll }: Params) => { const nodes: Node[] = []; const position = positions[OVERVIEW_ENTITY_TYPES.SOURCE]; const unfilteredCount = unfilteredCounts[OVERVIEW_ENTITY_TYPES.SOURCE]; nodes.push({ id: 'source-header', - type: 'header', + type: NODE_TYPES.HEADER, position: { x: positions[OVERVIEW_ENTITY_TYPES.SOURCE]['x'], y: 0, @@ -54,10 +56,23 @@ export const buildSourceNodes = ({ entities, positions, unfilteredCounts, contai }, }); - if (!entities.length) { + if (loading) { + nodes.push({ + id: 'source-skeleton', + type: NODE_TYPES.SKELETON, + position: { + x: position['x'], + y: position['y'](), + }, + data: { + nodeWidth, + size: 3, + }, + }); + } else if (!entities.length) { nodes.push({ id: 'source-add', - type: 'add', + type: NODE_TYPES.ADD, position: { x: position['x'], y: position['y'](), @@ -73,7 +88,7 @@ export const buildSourceNodes = ({ entities, positions, unfilteredCounts, contai } else { nodes.push({ id: 'source-scroll', - type: 'scroll', + type: NODE_TYPES.SCROLL, position: { x: position['x'], y: position['y']() - framePadding, @@ -83,10 +98,7 @@ export const buildSourceNodes = ({ entities, positions, unfilteredCounts, contai nodeHeight: containerHeight - nodeHeight + framePadding * 2, items: entities.map((source, idx) => ({ id: `source-${idx}`, - data: { - framePadding, - ...mapToNodeData(source), - }, + data: mapToNodeData(source), })), onScroll, }, @@ -95,18 +107,17 @@ export const buildSourceNodes = ({ entities, positions, unfilteredCounts, contai entities.forEach((source, idx) => { nodes.push({ id: `source-${idx}-hidden`, - type: 'base', + type: NODE_TYPES.EDGED, extent: 'parent', parentId: 'source-scroll', position: { x: framePadding, y: position['y'](idx) - (nodeHeight - framePadding), }, - data: mapToNodeData(source), style: { - opacity: 0, zIndex: -1, }, + data: mapToNodeData(source), }); }); } 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 be27b5f30..77acdda75 100644 --- a/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx +++ b/frontend/webapp/containers/main/overview/overview-data-flow/index.tsx @@ -35,40 +35,50 @@ export default function OverviewDataFlowContainer() { const positions = useMemo(() => getNodePositions({ containerWidth }), [containerWidth]); const { metrics } = useMetrics(); - const { data, filteredData, startPolling } = useComputePlatform(); + const { data, filteredData, loading } = useComputePlatform(); const unfilteredCounts = useMemo(() => getEntityCounts({ computePlatform: data?.computePlatform }), [data]); - useEffect(() => { - // this is to start polling on component mount in an attempt to fix any initial errors with sources/destinations - if (!!data?.computePlatform.k8sActualSources.length || !!data?.computePlatform.destinations.length) startPolling(); - // only on-mount, if we include "data" this will trigger on every refetch, causing an infinite loop - }, []); - const ruleNodes = useMemo( - () => buildRuleNodes({ entities: filteredData?.computePlatform.instrumentationRules || [], positions, unfilteredCounts }), - [filteredData?.computePlatform.instrumentationRules, positions, unfilteredCounts], + () => + buildRuleNodes({ + loading, + entities: filteredData?.computePlatform.instrumentationRules || [], + positions, + unfilteredCounts, + }), + [loading, filteredData?.computePlatform.instrumentationRules, positions, unfilteredCounts], ); const actionNodes = useMemo( - () => buildActionNodes({ entities: filteredData?.computePlatform.actions || [], positions, unfilteredCounts }), - [filteredData?.computePlatform.actions, positions, unfilteredCounts], + () => + buildActionNodes({ + loading, + entities: filteredData?.computePlatform.actions || [], + positions, + unfilteredCounts, + }), + [loading, filteredData?.computePlatform.actions, positions, unfilteredCounts], ); const destinationNodes = useMemo( - () => buildDestinationNodes({ entities: filteredData?.computePlatform.destinations || [], positions, unfilteredCounts }), - [filteredData?.computePlatform.destinations, positions, unfilteredCounts], + () => + buildDestinationNodes({ + loading, + entities: filteredData?.computePlatform.destinations || [], + positions, + unfilteredCounts, + }), + [loading, filteredData?.computePlatform.destinations, positions, unfilteredCounts], ); const sourceNodes = useMemo( () => buildSourceNodes({ + loading, entities: filteredData?.computePlatform.k8sActualSources || [], positions, unfilteredCounts, containerHeight, - onScroll: ({ clientHeight, scrollHeight, scrollTop }) => { - console.log('Node scrolled', { clientHeight, scrollHeight, scrollTop }); - setScrollYOffset(scrollTop); - }, + onScroll: ({ scrollTop }) => setScrollYOffset(scrollTop), }), - [filteredData?.computePlatform.k8sActualSources, positions, unfilteredCounts, containerHeight], + [loading, filteredData?.computePlatform.k8sActualSources, positions, unfilteredCounts, containerHeight], ); const [nodes, setNodes, onNodesChange] = useNodesState(([] as Node[]).concat(actionNodes, ruleNodes, sourceNodes, destinationNodes)); diff --git a/frontend/webapp/containers/main/overview/overview-drawer/drawer-header/index.tsx b/frontend/webapp/containers/main/overview/overview-drawer/drawer-header/index.tsx index ea94656d2..ed75e91c8 100644 --- a/frontend/webapp/containers/main/overview/overview-drawer/drawer-header/index.tsx +++ b/frontend/webapp/containers/main/overview/overview-drawer/drawer-header/index.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState, forwardRef, useImperativeHandle } from 'react'; import Image from 'next/image'; import styled from 'styled-components'; -import { Button, Input, Text, Tooltip } from '@/reuseable-components'; +import { Button, IconWrapped, Input, Text, Tooltip } from '@/reuseable-components'; const HeaderContainer = styled.section` display: flex; @@ -32,17 +32,6 @@ const Title = styled(Text)` text-overflow: ellipsis; `; -const DrawerItemImageWrapper = styled.div` - display: flex; - width: 36px; - height: 36px; - justify-content: center; - align-items: center; - gap: 8px; - border-radius: 8px; - background: linear-gradient(180deg, rgba(249, 249, 249, 0.06) 0%, rgba(249, 249, 249, 0.02) 100%); -`; - const EditButton = styled(Button)` gap: 8px; `; @@ -66,9 +55,9 @@ export interface DrawerHeaderRef { interface DrawerHeaderProps { title: string; titleTooltip?: string; - imageUri: string; - isEdit: boolean; - onEdit: () => void; + imageUri?: string; + isEdit?: boolean; + onEdit?: () => void; onClose: () => void; } @@ -87,9 +76,8 @@ const DrawerHeader = forwardRef(({ title, ti return ( - - Drawer Item - + {!!imageUri && } + {!isEdit && ( {title} @@ -105,12 +93,13 @@ const DrawerHeader = forwardRef(({ title, ti )} - {!isEdit && ( + {!isEdit && !!onEdit && ( Edit Edit )} + Close diff --git a/frontend/webapp/containers/main/overview/overview-drawer/index.tsx b/frontend/webapp/containers/main/overview/overview-drawer/index.tsx index bf722f7ad..35c8210f2 100644 --- a/frontend/webapp/containers/main/overview/overview-drawer/index.tsx +++ b/frontend/webapp/containers/main/overview/overview-drawer/index.tsx @@ -13,13 +13,13 @@ const DRAWER_WIDTH = `${640 + 64}px`; // +64 because of "ContentArea" padding interface Props { title: string; titleTooltip?: string; - imageUri: string; - isEdit: boolean; - isFormDirty: boolean; - onEdit: (bool?: boolean) => void; - onSave: (newTitle: string) => void; - onDelete: () => void; - onCancel: () => void; + imageUri?: string; + isEdit?: boolean; + isFormDirty?: boolean; + onEdit?: (bool?: boolean) => void; + onSave?: (newTitle: string) => void; + onDelete?: () => void; + onCancel?: () => void; } const DrawerContent = styled.div` @@ -34,7 +34,7 @@ const ContentArea = styled.div` overflow-y: auto; `; -const OverviewDrawer: React.FC = ({ children, title, titleTooltip, imageUri, isEdit, isFormDirty, onEdit, onSave, onDelete, onCancel }) => { +const OverviewDrawer: React.FC = ({ children, title, titleTooltip, imageUri, isEdit = false, isFormDirty = false, onEdit, onSave, onDelete, onCancel }) => { const { selectedItem, setSelectedItem } = useDrawerStore(); useKeyDown({ key: 'Enter', active: !!selectedItem }, () => (isEdit ? clickSave() : closeDrawer())); @@ -52,7 +52,7 @@ const OverviewDrawer: React.FC = ({ children, title, const closeDrawer = () => { setSelectedItem(null); - onEdit(false); + if (onEdit) onEdit(false); setIsDeleteModalOpen(false); setIsCancelModalOpen(false); }; @@ -64,7 +64,7 @@ const OverviewDrawer: React.FC = ({ children, title, const handleCancel = () => { titleRef.current?.clearTitle(); - onCancel(); + if (onCancel) onCancel(); closeWarningModals(); }; @@ -78,7 +78,7 @@ const OverviewDrawer: React.FC = ({ children, title, }; const handleDelete = () => { - onDelete(); + if (onDelete) onDelete(); closeWarningModals(); }; @@ -87,7 +87,7 @@ const OverviewDrawer: React.FC = ({ children, title, }; const clickSave = () => { - onSave(titleRef.current?.getTitle() || ''); + if (onSave) onSave(titleRef.current?.getTitle() || ''); }; const isLastItem = () => { @@ -103,7 +103,15 @@ const OverviewDrawer: React.FC = ({ children, title, <> - onEdit(true)} onClose={isEdit ? clickCancel : closeDrawer} /> + onEdit(true) : undefined} + onClose={isEdit ? clickCancel : closeDrawer} + /> {children} @@ -113,7 +121,7 @@ const OverviewDrawer: React.FC = ({ children, title, isOpen={isDeleteModalOpen} noOverlay name={`${selectedItem?.type}${title ? ` (${title})` : ''}`} - type={selectedItem?.type} + type={selectedItem?.type as OVERVIEW_ENTITY_TYPES} isLastItem={isLastItem()} onApprove={handleDelete} onDeny={closeWarningModals} 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 6f9381e93..0c5a0fd82 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -1,13 +1,13 @@ import React, { useEffect, useMemo, useState } from 'react'; import buildCard from './build-card'; import styled from 'styled-components'; -import { useSourceCRUD } from '@/hooks'; import { useDrawerStore } from '@/store'; import buildDrawerItem from './build-drawer-item'; import { UpdateSourceBody } from '../update-source-body'; +import { useDescribeSource, useSourceCRUD } from '@/hooks'; import OverviewDrawer from '../../overview/overview-drawer'; import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type K8sActualSource } from '@/types'; -import { ACTION, DATA_CARDS, getMainContainerLanguage, getProgrammingLanguageIcon } from '@/utils'; +import { ACTION, BACKEND_BOOLEAN, DATA_CARDS, getEntityIcon, safeJsonStringify } from '@/utils'; import { ConditionDetails, DataCard, DataCardRow, DataCardFieldTypes } from '@/reuseable-components'; interface Props {} @@ -79,13 +79,17 @@ 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'), + ); + return ( item.instrumentedApplicationDetails.containers.map( (container) => ({ type: DataCardFieldTypes.SOURCE_CONTAINER, width: '100%', - value: JSON.stringify(container), + value: JSON.stringify({ ...container, hasPresenceOfOtherAgent }), } as DataCardRow), ) || [] ); @@ -93,6 +97,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 handleEdit = (bool?: boolean) => { setIsEditing(typeof bool === 'boolean' ? bool : true); @@ -118,7 +123,7 @@ export const SourceDrawer: React.FC = () => { = () => { + )} diff --git a/frontend/webapp/cypress/constants/index.ts b/frontend/webapp/cypress/constants/index.ts index 8afbf4d6e..fcc22c8b9 100644 --- a/frontend/webapp/cypress/constants/index.ts +++ b/frontend/webapp/cypress/constants/index.ts @@ -70,6 +70,9 @@ export const DATA_IDS = { TITLE: '[data-id=title]', SOURCE_TITLE: '[data-id=sourceName]', CHECKBOX: '[data-id=checkbox]', + + NOTIF_MANAGER_BUTTON: '[data-id=notif-manager-button]', + NOTIF_MANAGER_CONTENR: '[data-id=notif-manager-content]', }; export const BUTTONS = { @@ -88,6 +91,8 @@ const CYPRESS_TEST = 'Cypress Test'; export const TEXTS = { UPDATED_NAME: CYPRESS_TEST, + NO_RESOURCES: (namespace: string) => `No resources found in ${namespace} namespace.`, + SOURCE_WARN_MODAL_TITLE: 'Uninstrument 5 sources', SOURCE_WARN_MODAL_NOTE: "You're about to uninstrument the last source", DESTINATION_WARN_MODAL_TITLE: `Delete destination (${CYPRESS_TEST})`, @@ -95,5 +100,6 @@ export const TEXTS = { ACTION_WARN_MODAL_TITLE: `Delete action (${CYPRESS_TEST})`, INSTRUMENTATION_RULE_WARN_MODAL_TITLE: `Delete rule (${CYPRESS_TEST})`, - NO_RESOURCES: (namespace: string) => `No resources found in ${namespace} namespace.`, + NOTIF_SOURCES_CREATED: 'successfully added 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 59f69e44b..84d3709e9 100644 --- a/frontend/webapp/cypress/e2e/03-sources.cy.ts +++ b/frontend/webapp/cypress/e2e/03-sources.cy.ts @@ -11,7 +11,7 @@ const crdName = CRD_NAMES.SOURCE; describe('Sources CRUD', () => { beforeEach(() => cy.intercept('/graphql').as('gql')); - it('Should create a CRD in the cluster', () => { + it('Should create a CRD in the cluster, and notify with SSE', () => { cy.visit(ROUTES.OVERVIEW); getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }, () => { @@ -25,7 +25,10 @@ describe('Sources CRUD', () => { cy.contains('button', BUTTONS.DONE).click(); cy.wait('@gql').then(() => { - getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }); + 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'); + }); }); }); }); @@ -55,7 +58,7 @@ describe('Sources CRUD', () => { }); }); - it('Should delete the CRD from the cluster', () => { + it('Should delete the CRD from the cluster, and notify with SSE', () => { cy.visit(ROUTES.OVERVIEW); getCrdIds({ namespace, crdName, expectedError: '', expectedLength: 5 }, () => { @@ -66,7 +69,10 @@ describe('Sources CRUD', () => { cy.get(DATA_IDS.APPROVE).click(); cy.wait('@gql').then(() => { - getCrdIds({ namespace, crdName, expectedError: TEXTS.NO_RESOURCES(namespace), expectedLength: 0 }); + 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'); + }); }); }); }); diff --git a/frontend/webapp/cypress/e2e/04-destinations.cy.ts b/frontend/webapp/cypress/e2e/04-destinations.cy.ts index bc4ec28f3..a510d03c7 100644 --- a/frontend/webapp/cypress/e2e/04-destinations.cy.ts +++ b/frontend/webapp/cypress/e2e/04-destinations.cy.ts @@ -71,4 +71,3 @@ describe('Destinations CRUD', () => { }); }); }); -// destination-odigos.io.dest.jaeger-chc52 diff --git a/frontend/webapp/graphql/queries/describe.ts b/frontend/webapp/graphql/queries/describe.ts new file mode 100644 index 000000000..1280a312e --- /dev/null +++ b/frontend/webapp/graphql/queries/describe.ts @@ -0,0 +1,365 @@ +import { gql } from '@apollo/client'; + +export const DESCRIBE_ODIGOS = gql` + query DescribeOdigos { + describeOdigos { + odigosVersion { + name + value + status + explain + } + numberOfDestinations + numberOfSources + clusterCollector { + enabled { + name + value + status + explain + } + collectorGroup { + name + value + status + explain + } + deployed { + name + value + status + explain + } + deployedError { + name + value + status + explain + } + collectorReady { + name + value + status + explain + } + deploymentCreated { + name + value + status + explain + } + expectedReplicas { + name + value + status + explain + } + healthyReplicas { + name + value + status + explain + } + failedReplicas { + name + value + status + explain + } + failedReplicasReason { + name + value + status + explain + } + } + nodeCollector { + enabled { + name + value + status + explain + } + collectorGroup { + name + value + status + explain + } + deployed { + name + value + status + explain + } + deployedError { + name + value + status + explain + } + collectorReady { + name + value + status + explain + } + daemonSet { + name + value + status + explain + } + desiredNodes { + name + value + status + explain + } + currentNodes { + name + value + status + explain + } + updatedNodes { + name + value + status + explain + } + availableNodes { + name + value + status + explain + } + } + isSettled + hasErrors + } + } +`; + +export const DESCRIBE_SOURCE = gql` + query DescribeSource($namespace: String!, $kind: String!, $name: String!) { + describeSource(namespace: $namespace, kind: $kind, name: $name) { + name { + name + value + status + explain + } + kind { + name + value + status + explain + } + namespace { + name + value + status + explain + } + labels { + instrumented { + name + value + status + explain + } + workload { + name + value + status + explain + } + namespace { + name + value + status + explain + } + instrumentedText { + name + value + status + explain + } + } + instrumentationConfig { + created { + name + value + status + explain + } + createTime { + name + value + status + explain + } + } + runtimeInfo { + generation { + name + value + status + explain + } + containers { + containerName { + name + value + status + explain + } + language { + name + value + status + explain + } + runtimeVersion { + name + value + status + explain + } + envVars { + name + value + status + explain + } + } + } + instrumentedApplication { + created { + name + value + status + explain + } + createTime { + name + value + status + explain + } + containers { + containerName { + name + value + status + explain + } + language { + name + value + status + explain + } + runtimeVersion { + name + value + status + explain + } + envVars { + name + value + status + explain + } + } + } + instrumentationDevice { + statusText { + name + value + status + explain + } + containers { + containerName { + name + value + status + explain + } + devices { + name + value + status + explain + } + originalEnv { + name + value + status + explain + } + } + } + totalPods + podsPhasesCount + pods { + podName { + name + value + status + explain + } + nodeName { + name + value + status + explain + } + phase { + name + value + status + explain + } + containers { + containerName { + name + value + status + explain + } + actualDevices { + name + value + status + explain + } + instrumentationInstances { + healthy { + name + value + status + explain + } + message { + name + value + status + explain + } + identifyingAttributes { + name + value + status + explain + } + } + } + } + } + } +`; diff --git a/frontend/webapp/graphql/queries/index.ts b/frontend/webapp/graphql/queries/index.ts index 498bd90f5..a7c69d689 100644 --- a/frontend/webapp/graphql/queries/index.ts +++ b/frontend/webapp/graphql/queries/index.ts @@ -1,3 +1,4 @@ export * from './config'; export * from './compute-platform'; +export * from './describe'; export * from './destination'; diff --git a/frontend/webapp/hooks/actions/useActionCRUD.ts b/frontend/webapp/hooks/actions/useActionCRUD.ts index f75ba691a..f261ee47e 100644 --- a/frontend/webapp/hooks/actions/useActionCRUD.ts +++ b/frontend/webapp/hooks/actions/useActionCRUD.ts @@ -1,10 +1,9 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; -import { useNotify } from '../notification/useNotify'; +import { ACTION, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; -import { ACTION, getSseTargetFromId, NOTIFICATION } from '@/utils'; import { CREATE_ACTION, DELETE_ACTION, UPDATE_ACTION } from '@/graphql/mutations'; -import { OVERVIEW_ENTITY_TYPES, type ActionInput, type ActionsType, type NotificationType } from '@/types'; +import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type ActionInput, type ActionsType } from '@/types'; interface UseActionCrudParams { onSuccess?: (type: string) => void; @@ -14,10 +13,10 @@ interface UseActionCrudParams { export const useActionCRUD = (params?: UseActionCrudParams) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { data, refetch } = useComputePlatform(); - const notify = useNotify(); + const { addNotification } = useNotificationStore(); - const notifyUser = (type: NotificationType, title: string, message: string, id?: string) => { - notify({ + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + addNotification({ type, title, message, @@ -27,12 +26,12 @@ export const useActionCRUD = (params?: UseActionCrudParams) => { }; const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION.ERROR, title, message, id); + notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); params?.onError?.(title); }; const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION.SUCCESS, title, message, id); + notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); refetch(); params?.onSuccess?.(title); }; diff --git a/frontend/webapp/hooks/actions/useActionFormData.ts b/frontend/webapp/hooks/actions/useActionFormData.ts index e6f721462..61872fdb8 100644 --- a/frontend/webapp/hooks/actions/useActionFormData.ts +++ b/frontend/webapp/hooks/actions/useActionFormData.ts @@ -1,7 +1,7 @@ -import { DrawerBaseItem } from '@/store'; -import { useGenericForm, useNotify } from '@/hooks'; -import { FORM_ALERTS, NOTIFICATION } from '@/utils'; -import type { ActionDataParsed, ActionInput } from '@/types'; +import { DrawerItem, useNotificationStore } from '@/store'; +import { FORM_ALERTS } from '@/utils'; +import { useGenericForm } from '@/hooks'; +import { NOTIFICATION_TYPE, type ActionDataParsed, type ActionInput } from '@/types'; const INITIAL: ActionInput = { // @ts-ignore (TS complains about empty string because we expect an "ActionsType", but it's fine) @@ -14,7 +14,7 @@ const INITIAL: ActionInput = { }; export function useActionFormData() { - const notify = useNotify(); + const { addNotification } = useNotificationStore(); const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL); const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { @@ -38,8 +38,8 @@ export function useActionFormData() { }); if (!ok && params?.withAlert) { - notify({ - type: NOTIFICATION.WARNING, + addNotification({ + type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, }); @@ -50,7 +50,7 @@ export function useActionFormData() { return ok; }; - const loadFormWithDrawerItem = (drawerItem: DrawerBaseItem) => { + const loadFormWithDrawerItem = (drawerItem: DrawerItem) => { const { type, spec } = drawerItem.item as ActionDataParsed; const updatedData: ActionInput = { diff --git a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts index f3835e368..919c032aa 100644 --- a/frontend/webapp/hooks/compute-platform/useComputePlatform.ts +++ b/frontend/webapp/hooks/compute-platform/useComputePlatform.ts @@ -1,6 +1,5 @@ -import { useCallback, useMemo } from 'react'; +import { useMemo } from 'react'; import { useQuery } from '@apollo/client'; -import { useBooleanStore } from '@/store'; import { GET_COMPUTE_PLATFORM } from '@/graphql'; import { useFilterStore } from '@/store/useFilterStore'; import { BACKEND_BOOLEAN, deriveTypeFromRule, safeJsonParse } from '@/utils'; @@ -12,30 +11,12 @@ type UseComputePlatformHook = { loading: boolean; error?: Error; refetch: () => void; - startPolling: () => Promise; }; export const useComputePlatform = (): UseComputePlatformHook => { const { data, loading, error, refetch } = useQuery(GET_COMPUTE_PLATFORM); - const { togglePolling } = useBooleanStore(); const filters = useFilterStore(); - const startPolling = useCallback(async () => { - togglePolling(true); - - let retries = 0; - const maxRetries = 5; - const retryInterval = 3 * 1000; // time in milliseconds - - while (retries < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, retryInterval)); - refetch(); - retries++; - } - - togglePolling(false); - }, [refetch, togglePolling]); - const mappedData = useMemo(() => { if (!data) return undefined; @@ -95,5 +76,5 @@ export const useComputePlatform = (): UseComputePlatformHook => { }; }, [mappedData, filters]); - return { data: mappedData, filteredData, loading, error, refetch, startPolling }; + return { data: mappedData, filteredData, loading, error, refetch }; }; diff --git a/frontend/webapp/hooks/compute-platform/useNamespace.ts b/frontend/webapp/hooks/compute-platform/useNamespace.ts index 7bf034592..fe95b9ea5 100644 --- a/frontend/webapp/hooks/compute-platform/useNamespace.ts +++ b/frontend/webapp/hooks/compute-platform/useNamespace.ts @@ -1,20 +1,19 @@ -import { NOTIFICATION } from '@/utils'; -import { useNotify } from '../notification'; +import { useNotificationStore } from '@/store'; import { useMutation, useQuery } from '@apollo/client'; import { useComputePlatform } from './useComputePlatform'; import { GET_NAMESPACES, PERSIST_NAMESPACE } from '@/graphql'; -import { ComputePlatform, PersistNamespaceItemInput } from '@/types'; +import { type ComputePlatform, NOTIFICATION_TYPE, type PersistNamespaceItemInput } from '@/types'; export const useNamespace = (namespaceName?: string, instrumentationLabeled = null as boolean | null) => { - const notify = useNotify(); + const { addNotification } = useNotificationStore(); const cp = useComputePlatform(); const handleError = (title: string, message: string) => { - notify({ type: NOTIFICATION.ERROR, title, message }); + addNotification({ type: NOTIFICATION_TYPE.ERROR, title, message }); }; const handleComplete = (title: string, message: string) => { - notify({ type: NOTIFICATION.SUCCESS, title, message }); + addNotification({ type: NOTIFICATION_TYPE.SUCCESS, title, message }); }; const { data, loading, error } = useQuery(GET_NAMESPACES, { diff --git a/frontend/webapp/hooks/describe/index.ts b/frontend/webapp/hooks/describe/index.ts new file mode 100644 index 000000000..16e5b9b9d --- /dev/null +++ b/frontend/webapp/hooks/describe/index.ts @@ -0,0 +1,2 @@ +export * from './useDescribeOdigos'; +export * from './useDescribeSource'; diff --git a/frontend/webapp/hooks/describe/useDescribeOdigos.ts b/frontend/webapp/hooks/describe/useDescribeOdigos.ts new file mode 100644 index 000000000..74b90199e --- /dev/null +++ b/frontend/webapp/hooks/describe/useDescribeOdigos.ts @@ -0,0 +1,15 @@ +import { useQuery } from '@apollo/client'; +import { DESCRIBE_ODIGOS } from '@/graphql'; +import type { DescribeOdigos } from '@/types'; + +export const useDescribeOdigos = () => { + const { data, loading, error } = useQuery(DESCRIBE_ODIGOS, { + pollInterval: 5000, + }); + + return { + data: data?.describeOdigos, + loading, + error, + }; +}; diff --git a/frontend/webapp/hooks/describe/useDescribeSource.ts b/frontend/webapp/hooks/describe/useDescribeSource.ts new file mode 100644 index 000000000..209e92794 --- /dev/null +++ b/frontend/webapp/hooks/describe/useDescribeSource.ts @@ -0,0 +1,16 @@ +import { useQuery } from '@apollo/client'; +import { DESCRIBE_SOURCE } from '@/graphql'; +import type { DescribeSource, WorkloadId } from '@/types'; + +export const useDescribeSource = ({ namespace, name, kind }: WorkloadId) => { + const { data, loading, error } = useQuery(DESCRIBE_SOURCE, { + variables: { namespace, name, kind }, + pollInterval: 5000, + }); + + return { + data: data?.describeSource, + loading, + error, + }; +}; diff --git a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts index 2088c18cc..f30bf80cb 100644 --- a/frontend/webapp/hooks/destinations/useDestinationCRUD.ts +++ b/frontend/webapp/hooks/destinations/useDestinationCRUD.ts @@ -1,9 +1,8 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; -import { useNotify } from '../notification/useNotify'; +import { ACTION, getSseTargetFromId } from '@/utils'; import { useComputePlatform } from '../compute-platform'; -import { ACTION, getSseTargetFromId, NOTIFICATION } from '@/utils'; -import { OVERVIEW_ENTITY_TYPES, type DestinationInput, type NotificationType } from '@/types'; +import { NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, type DestinationInput } from '@/types'; import { CREATE_DESTINATION, DELETE_DESTINATION, UPDATE_DESTINATION } from '@/graphql/mutations'; interface Params { @@ -14,10 +13,10 @@ interface Params { export const useDestinationCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { data, refetch } = useComputePlatform(); - const notify = useNotify(); + const { addNotification } = useNotificationStore(); - const notifyUser = (type: NotificationType, title: string, message: string, id?: string) => { - notify({ + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + addNotification({ type, title, message, @@ -27,12 +26,12 @@ export const useDestinationCRUD = (params?: Params) => { }; const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION.ERROR, title, message, id); + notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); params?.onError?.(title); }; const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION.SUCCESS, title, message, id); + notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); refetch(); params?.onSuccess?.(title); }; diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 3f4edefd3..eb50c9ac2 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -1,9 +1,9 @@ import { useState, useEffect } from 'react'; -import { DrawerBaseItem } from '@/store'; import { useQuery } from '@apollo/client'; import { GET_DESTINATION_TYPE_DETAILS } from '@/graphql'; -import { useConnectDestinationForm, useGenericForm, useNotify } from '@/hooks'; -import { ACTION, FORM_ALERTS, NOTIFICATION, safeJsonParse } from '@/utils'; +import { DrawerItem, useNotificationStore } from '@/store'; +import { ACTION, FORM_ALERTS, safeJsonParse } from '@/utils'; +import { useConnectDestinationForm, useGenericForm } from '@/hooks'; import { type DynamicField, type DestinationDetailsResponse, @@ -12,6 +12,7 @@ import { type ActualDestination, type SupportedDestinationSignals, OVERVIEW_ENTITY_TYPES, + NOTIFICATION_TYPE, } from '@/types'; const INITIAL: DestinationInput = { @@ -28,7 +29,7 @@ const INITIAL: DestinationInput = { export function useDestinationFormData(params?: { destinationType?: string; supportedSignals?: SupportedDestinationSignals; preLoadedFields?: string | DestinationTypeItem['fields'] }) { const { destinationType, supportedSignals, preLoadedFields } = params || {}; - const notify = useNotify(); + const { addNotification } = useNotificationStore(); const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL); const { buildFormDynamicFields } = useConnectDestinationForm(); @@ -38,7 +39,13 @@ export function useDestinationFormData(params?: { destinationType?: string; supp const { data: { destinationTypeDetails } = {} } = useQuery(GET_DESTINATION_TYPE_DETAILS, { variables: { type: t }, skip: !t, - onError: (error) => notify({ type: NOTIFICATION.ERROR, title: ACTION.FETCH, message: error.message, crdType: OVERVIEW_ENTITY_TYPES.DESTINATION }), + onError: (error) => + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: ACTION.FETCH, + message: error.message, + crdType: OVERVIEW_ENTITY_TYPES.DESTINATION, + }), }); useEffect(() => { @@ -98,8 +105,8 @@ export function useDestinationFormData(params?: { destinationType?: string; supp }); if (!ok && params?.withAlert) { - notify({ - type: NOTIFICATION.WARNING, + addNotification({ + type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, }); @@ -110,7 +117,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp return ok; }; - const loadFormWithDrawerItem = (drawerItem: DrawerBaseItem) => { + const loadFormWithDrawerItem = (drawerItem: DrawerItem) => { const { destinationType: { type }, name, diff --git a/frontend/webapp/hooks/index.tsx b/frontend/webapp/hooks/index.ts similarity index 90% rename from frontend/webapp/hooks/index.tsx rename to frontend/webapp/hooks/index.ts index 6198e2ae9..0996541ea 100644 --- a/frontend/webapp/hooks/index.tsx +++ b/frontend/webapp/hooks/index.ts @@ -1,9 +1,10 @@ +export * from './actions'; export * from './common'; +export * from './compute-platform'; export * from './config'; -export * from './sources'; -export * from './actions'; -export * from './overview'; -export * from './notification'; +export * from './describe'; export * from './destinations'; -export * from './compute-platform'; export * from './instrumentation-rules'; +export * from './notification'; +export * from './overview'; +export * from './sources'; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts index 3181d3cbc..407c8961b 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleCRUD.ts @@ -1,9 +1,8 @@ import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; -import { useNotify } from '../notification/useNotify'; import { useComputePlatform } from '../compute-platform'; -import { ACTION, deriveTypeFromRule, getSseTargetFromId, NOTIFICATION } from '@/utils'; -import { OVERVIEW_ENTITY_TYPES, type InstrumentationRuleInput, type NotificationType } from '@/types'; +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'; interface Params { @@ -14,10 +13,10 @@ interface Params { export const useInstrumentationRuleCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); const { data, refetch } = useComputePlatform(); - const notify = useNotify(); + const { addNotification } = useNotificationStore(); - const notifyUser = (type: NotificationType, title: string, message: string, id?: string) => { - notify({ + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: string) => { + addNotification({ type, title, message, @@ -27,12 +26,12 @@ export const useInstrumentationRuleCRUD = (params?: Params) => { }; const handleError = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION.ERROR, title, message, id); + notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); params?.onError?.(title); }; const handleComplete = (title: string, message: string, id?: string) => { - notifyUser(NOTIFICATION.SUCCESS, title, message, id); + notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); refetch(); params?.onSuccess?.(title); }; diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts index c1718e65f..20d14e7df 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts @@ -1,7 +1,7 @@ -import type { DrawerBaseItem } from '@/store'; -import { useGenericForm, useNotify } from '@/hooks'; -import { FORM_ALERTS, NOTIFICATION } from '@/utils'; -import { PayloadCollectionType, type InstrumentationRuleInput, type InstrumentationRuleSpec } from '@/types'; +import { FORM_ALERTS } from '@/utils'; +import { useGenericForm } from '@/hooks'; +import { useNotificationStore, type DrawerItem } from '@/store'; +import { NOTIFICATION_TYPE, PayloadCollectionType, type InstrumentationRuleInput, type InstrumentationRuleSpec } from '@/types'; const INITIAL: InstrumentationRuleInput = { ruleName: '', @@ -18,7 +18,7 @@ const INITIAL: InstrumentationRuleInput = { }; export function useInstrumentationRuleFormData() { - const notify = useNotify(); + const { addNotification } = useNotificationStore(); const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL); const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { @@ -41,8 +41,8 @@ export function useInstrumentationRuleFormData() { }); if (!ok && params?.withAlert) { - notify({ - type: NOTIFICATION.WARNING, + addNotification({ + type: NOTIFICATION_TYPE.WARNING, title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, }); @@ -53,7 +53,7 @@ export function useInstrumentationRuleFormData() { return ok; }; - const loadFormWithDrawerItem = (drawerItem: DrawerBaseItem) => { + const loadFormWithDrawerItem = (drawerItem: DrawerItem) => { const { ruleName, notes, disabled, payloadCollection } = drawerItem.item as InstrumentationRuleSpec; const updatedData: InstrumentationRuleInput = { diff --git a/frontend/webapp/hooks/notification/index.ts b/frontend/webapp/hooks/notification/index.ts index 44d4bfd4e..20874d0ed 100644 --- a/frontend/webapp/hooks/notification/index.ts +++ b/frontend/webapp/hooks/notification/index.ts @@ -1,3 +1,2 @@ export * from './useClickNotif'; -export * from './useNotify'; export * from './useSSE'; diff --git a/frontend/webapp/hooks/notification/useClickNotif.ts b/frontend/webapp/hooks/notification/useClickNotif.ts index 02d05a450..a9ba5a970 100644 --- a/frontend/webapp/hooks/notification/useClickNotif.ts +++ b/frontend/webapp/hooks/notification/useClickNotif.ts @@ -4,7 +4,7 @@ import { getIdFromSseTarget } from '@/utils'; import { useDestinationCRUD } from '../destinations'; import { type Notification, OVERVIEW_ENTITY_TYPES } from '@/types'; import { useInstrumentationRuleCRUD } from '../instrumentation-rules'; -import { DrawerBaseItem, useDrawerStore, useNotificationStore } from '@/store'; +import { DrawerItem, useDrawerStore, useNotificationStore } from '@/store'; export const useClickNotif = () => { const { sources } = useSourceCRUD(); @@ -19,7 +19,7 @@ export const useClickNotif = () => { const { dismissToast } = options || {}; if (crdType && target) { - const drawerItem: Partial = {}; + const drawerItem: Partial = {}; switch (crdType) { case OVERVIEW_ENTITY_TYPES.RULE: @@ -55,7 +55,7 @@ export const useClickNotif = () => { } if (!!drawerItem.item) { - setSelectedItem(drawerItem as DrawerBaseItem); + setSelectedItem(drawerItem as DrawerItem); } else { console.warn('notif item not found for:', { crdType, target }); } diff --git a/frontend/webapp/hooks/notification/useNotify.ts b/frontend/webapp/hooks/notification/useNotify.ts deleted file mode 100644 index 910ffe07a..000000000 --- a/frontend/webapp/hooks/notification/useNotify.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useNotificationStore } from '@/store'; -import { Notification } from '@/types'; - -export const useNotify = () => { - const { addNotification } = useNotificationStore(); - - const notify = ({ - type, - title, - message, - crdType, - target, - }: { - type: Notification['type']; - title?: Notification['title']; - message?: Notification['message']; - crdType?: Notification['crdType']; - target?: Notification['target']; - }) => { - addNotification({ type, title, message, crdType, target }); - }; - - return notify; -}; diff --git a/frontend/webapp/hooks/notification/useSSE.ts b/frontend/webapp/hooks/notification/useSSE.ts index 98f67a4b5..44629d415 100644 --- a/frontend/webapp/hooks/notification/useSSE.ts +++ b/frontend/webapp/hooks/notification/useSSE.ts @@ -1,13 +1,27 @@ -import { useEffect, useRef, useState } from 'react'; -import { useNotify } from './useNotify'; -import { API, NOTIFICATION } from '@/utils'; -import { useConnectionStore } from '@/store'; - -export function useSSE() { - const notify = useNotify(); +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'; + +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(); + const { refetch: refetchComputePlatform } = useComputePlatform(); - const [retryCount, setRetryCount] = useState(0); + const retryCount = useRef(0); const eventBuffer = useRef({}); const maxRetries = 10; @@ -15,72 +29,61 @@ export function useSSE() { const connect = () => { const eventSource = new EventSource(API.EVENTS); - eventSource.onmessage = function (event) { - const data = JSON.parse(event.data); + eventSource.onmessage = (event) => { const key = event.data; + const data = JSON.parse(key); - const notification = { - id: Date.now(), - message: data.data, - title: data.event, + const notification: NotifyPayload = { type: data.type, - target: data.target, + title: data.event, + message: data.data, crdType: data.crdType, + target: data.target, }; + notification.type = modifyType(notification); + // Check if the event is already in the buffer if (eventBuffer.current[key] && eventBuffer.current[key].id > Date.now() - 2000) { eventBuffer.current[key] = notification; - return; } else { // Add a new event to the buffer eventBuffer.current[key] = notification; - } - // Dispatch the notification to the store - notify({ - type: eventBuffer.current[key].type, - title: eventBuffer.current[key].title, - message: eventBuffer.current[key].message, - crdType: eventBuffer.current[key].crdType, - target: eventBuffer.current[key].target, - }); + // Dispatch the notification to the store + addNotification(notification); + refetchComputePlatform(); + } // Reset retry count on successful connection - setRetryCount(0); + retryCount.current = 0; }; - eventSource.onerror = function (event) { + eventSource.onerror = (event) => { console.error('EventSource failed:', event); eventSource.close(); // Retry connection with exponential backoff if below max retries - setRetryCount((prevRetryCount) => { - if (prevRetryCount < maxRetries) { - const newRetryCount = prevRetryCount + 1; - const retryTimeout = Math.min(10000, 1000 * Math.pow(2, newRetryCount)); - - setTimeout(() => connect(), retryTimeout); - - return newRetryCount; - } else { - console.error('Max retries reached. Could not reconnect to EventSource.'); - - setConnectionStore({ - connecting: false, - active: false, - title: `Connection lost on ${new Date().toLocaleString()}`, - message: 'Please reboot the application', - }); - notify({ - type: NOTIFICATION.ERROR, - title: 'Connection Error', - message: 'Connection to the server failed. Please reboot the application.', - }); - - return prevRetryCount; - } - }); + if (retryCount.current < maxRetries) { + retryCount.current += 1; + const retryTimeout = Math.min(10000, 1000 * Math.pow(2, retryCount.current)); + + setTimeout(() => connect(), retryTimeout); + } else { + console.error('Max retries reached. Could not reconnect to EventSource.'); + + setConnectionStore({ + connecting: false, + active: false, + title: `Connection lost on ${new Date().toLocaleString()}`, + message: 'Please reboot the application', + }); + addNotification({ + type: NOTIFICATION_TYPE.ERROR, + title: 'Connection Error', + message: 'Connection to the server failed. Please reboot the application.', + }); + } }; setConnectionStore({ @@ -100,4 +103,4 @@ export function useSSE() { eventSource.close(); }; }, []); -} +}; diff --git a/frontend/webapp/hooks/overview/useMetrics.ts b/frontend/webapp/hooks/overview/useMetrics.ts index 5fcc8f868..2f6be9934 100644 --- a/frontend/webapp/hooks/overview/useMetrics.ts +++ b/frontend/webapp/hooks/overview/useMetrics.ts @@ -1,15 +1,11 @@ -import { useEffect } from 'react'; import { useQuery } from '@apollo/client'; import { GET_METRICS } from '@/graphql/mutations/metrics'; import type { OverviewMetricsResponse } from '@/types'; -export function useMetrics() { - const { data, refetch } = useQuery(GET_METRICS); - - useEffect(() => { - const interval = setInterval(async () => await refetch(), 3000); - return () => clearInterval(interval); - }, [refetch]); +export const useMetrics = () => { + const { data } = useQuery(GET_METRICS, { + pollInterval: 3000, + }); return { metrics: data }; -} +}; diff --git a/frontend/webapp/hooks/sources/useSourceCRUD.ts b/frontend/webapp/hooks/sources/useSourceCRUD.ts index fc679efa7..82f30bdf7 100644 --- a/frontend/webapp/hooks/sources/useSourceCRUD.ts +++ b/frontend/webapp/hooks/sources/useSourceCRUD.ts @@ -1,10 +1,9 @@ -import { useNotify } from '../notification'; import { useMutation } from '@apollo/client'; import { useNotificationStore } from '@/store'; -import { ACTION, getSseTargetFromId, NOTIFICATION } from '@/utils'; +import { ACTION, getSseTargetFromId } from '@/utils'; import { PERSIST_SOURCE, UPDATE_K8S_ACTUAL_SOURCE } from '@/graphql'; import { useComputePlatform, useNamespace } from '../compute-platform'; -import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type NotificationType, type PatchSourceRequestInput, type K8sActualSource } from '@/types'; +import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type PatchSourceRequestInput, type K8sActualSource, NOTIFICATION_TYPE } from '@/types'; interface Params { onSuccess?: (type: string) => void; @@ -13,12 +12,12 @@ interface Params { export const useSourceCRUD = (params?: Params) => { const removeNotifications = useNotificationStore((store) => store.removeNotifications); - const { data, startPolling } = useComputePlatform(); const { persistNamespace } = useNamespace(); - const notify = useNotify(); + const { data, refetch } = useComputePlatform(); + const { addNotification } = useNotificationStore(); - const notifyUser = (type: NotificationType, title: string, message: string, id?: WorkloadId) => { - notify({ + const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId) => { + addNotification({ type, title, message, @@ -28,13 +27,13 @@ export const useSourceCRUD = (params?: Params) => { }; const handleError = (title: string, message: string, id?: WorkloadId) => { - notifyUser(NOTIFICATION.ERROR, title, message, id); + notifyUser(NOTIFICATION_TYPE.ERROR, title, message, id); params?.onError?.(title); }; const handleComplete = (title: string, message: string, id?: WorkloadId) => { - notifyUser(NOTIFICATION.SUCCESS, title, message, id); - startPolling(); + notifyUser(NOTIFICATION_TYPE.SUCCESS, title, message, id); + refetch(); params?.onSuccess?.(title); }; diff --git a/frontend/webapp/package.json b/frontend/webapp/package.json index 9fdd896f0..39b8b08dd 100644 --- a/frontend/webapp/package.json +++ b/frontend/webapp/package.json @@ -18,6 +18,7 @@ "graphql": "^16.9.0", "javascript-time-ago": "^2.5.11", "next": "15.0.3", + "prism-react-renderer": "^2.4.1", "react": "18.3.1", "react-dom": "18.3.1", "react-flow-renderer": "^10.3.17", diff --git a/frontend/webapp/reuseable-components/code/index.tsx b/frontend/webapp/reuseable-components/code/index.tsx new file mode 100644 index 000000000..21d281f22 --- /dev/null +++ b/frontend/webapp/reuseable-components/code/index.tsx @@ -0,0 +1,37 @@ +import styled from 'styled-components'; +import { Highlight, themes as prismThemes } from 'prism-react-renderer'; +import { flattenObjectKeys, safeJsonParse, safeJsonStringify } from '@/utils'; + +interface Props { + language: string; + code: string; + flatten?: boolean; +} + +const Token = styled.span` + white-space: pre-wrap; + overflow-wrap: break-word; + opacity: 0.75; + font-size: 12px; + font-family: ${({ theme }) => theme.font_family.code}; +`; + +export const Code: React.FC = ({ language, code, flatten }) => { + const str = flatten && language === 'json' ? safeJsonStringify(flattenObjectKeys(safeJsonParse(code, {}))) : code; + + return ( + + {({ getLineProps, getTokenProps, tokens }) => ( +
+          {tokens.map((line, i) => (
+            
+ {line.map((token, ii) => ( + + ))} +
+ ))} +
+ )} +
+ ); +}; diff --git a/frontend/webapp/reuseable-components/condition-details/index.tsx b/frontend/webapp/reuseable-components/condition-details/index.tsx index 5d3baabec..3c91cb800 100644 --- a/frontend/webapp/reuseable-components/condition-details/index.tsx +++ b/frontend/webapp/reuseable-components/condition-details/index.tsx @@ -2,8 +2,8 @@ import React, { useMemo, useState } from 'react'; import Image from 'next/image'; import theme from '@/styles/theme'; import styled from 'styled-components'; -import type { Condition } from '@/types'; import { BACKEND_BOOLEAN, getStatusIcon } from '@/utils'; +import { NOTIFICATION_TYPE, type Condition } from '@/types'; import { ExtendIcon, FadeLoader, Text } from '@/reuseable-components'; interface Props { @@ -51,7 +51,7 @@ export const ConditionDetails: React.FC = ({ conditions }) => { return ( setExtend((prev) => !prev)} $hasErrors={hasErrors}>
- {loading ? : } + {loading ? : } {headerText} @@ -60,14 +60,14 @@ export const ConditionDetails: React.FC = ({ conditions }) => { ({hasErrors ? errors.length : conditions.length}/{conditions.length}) - +
{extend && ( {conditions.map(({ status, message }, idx) => ( - + {message} diff --git a/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx b/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx index dc449ea79..764960676 100644 --- a/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx +++ b/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx @@ -1,6 +1,7 @@ import React, { useId } from 'react'; import styled from 'styled-components'; -import { ActiveStatus, DataTab, Divider, InstrumentStatus, MonitorsIcons, Text, Tooltip } from '@/reuseable-components'; +import { NOTIFICATION_TYPE } from '@/types'; +import { ActiveStatus, Code, DataTab, Divider, InstrumentStatus, MonitorsIcons, NotificationNote, Text, Tooltip } from '@/reuseable-components'; import { capitalizeFirstLetter, getProgrammingLanguageIcon, parseJsonStringToPrettyString, safeJsonParse, WORKLOAD_PROGRAMMING_LANGUAGES } from '@/utils'; export enum DataCardFieldTypes { @@ -8,6 +9,7 @@ export enum DataCardFieldTypes { MONITORS = 'monitors', ACTIVE_STATUS = 'active-status', SOURCE_CONTAINER = 'source-container', + CODE = 'code', } export interface DataCardRow { @@ -80,23 +82,46 @@ const renderValue = (type: DataCardRow['type'], value: DataCardRow['value']) => return ; case DataCardFieldTypes.SOURCE_CONTAINER: { - const { containerName, language, runtimeVersion } = safeJsonParse(value, { + const { containerName, language, runtimeVersion, otherAgent, hasPresenceOfOtherAgent } = safeJsonParse(value, { containerName: '-', language: WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN, runtimeVersion: '-', + otherAgent: null, + hasPresenceOfOtherAgent: false, }); + // Determine if running concurrently is possible based on language and other_agent + const canRunInParallel = !hasPresenceOfOtherAgent && (language === WORKLOAD_PROGRAMMING_LANGUAGES.PYTHON || language === WORKLOAD_PROGRAMMING_LANGUAGES.JAVA); + return ( - - + isExtended={!!otherAgent} + renderExtended={() => ( + + )} + renderActions={() => } + /> ); } + case DataCardFieldTypes.CODE: { + const params = safeJsonParse(value, { language: '', code: '' }); + + return ; + } + default: { return {parseJsonStringToPrettyString(value || '-')}; } diff --git a/frontend/webapp/reuseable-components/data-card/index.tsx b/frontend/webapp/reuseable-components/data-card/index.tsx index 7105834e4..6c7788944 100644 --- a/frontend/webapp/reuseable-components/data-card/index.tsx +++ b/frontend/webapp/reuseable-components/data-card/index.tsx @@ -43,14 +43,19 @@ const Description = styled(Text)` export const DataCard: React.FC = ({ title = 'Details', titleBadge, description, data }) => { return ( -
- - {title} - {/* NOT undefined, because we should allow zero (0) values */} - {titleBadge !== undefined && <Badge label={titleBadge} />} - - {!!description && {description}} -
+ {!!title || !!description ? ( +
+ {!!title && ( + + {title} + {/* NOT undefined, because we should allow zero (0) values */} + {titleBadge !== undefined && <Badge label={titleBadge} />} + + )} + + {!!description && {description}} +
+ ) : null}
diff --git a/frontend/webapp/reuseable-components/data-tab/index.tsx b/frontend/webapp/reuseable-components/data-tab/index.tsx index 71575cc7e..c817e9693 100644 --- a/frontend/webapp/reuseable-components/data-tab/index.tsx +++ b/frontend/webapp/reuseable-components/data-tab/index.tsx @@ -1,24 +1,32 @@ -import React, { PropsWithChildren, useCallback } from 'react'; -import Image from 'next/image'; -import { FlexColumn } from '@/styles'; +import React, { Fragment, useCallback, useState } from 'react'; +import { FlexColumn, FlexRow } from '@/styles'; import styled, { css } from 'styled-components'; -import { ActiveStatus, MonitorsIcons, Text } from '@/reuseable-components'; +import { ActiveStatus, Divider, ExtendIcon, IconButton, IconWrapped, MonitorsIcons, Text } from '@/reuseable-components'; -interface Props extends PropsWithChildren { +interface Props { title: string; - subTitle: string; + subTitle?: string; logo: string; + hoverText?: string; monitors?: string[]; + monitorsWithLabels?: boolean; isActive?: boolean; isError?: boolean; + withExtend?: boolean; + isExtended?: boolean; + renderExtended?: () => JSX.Element; + renderActions?: () => JSX.Element; onClick?: () => void; } +const ControlledVisibility = styled.div` + visibility: hidden; +`; + const Container = styled.div<{ $withClick: boolean; $isError: Props['isError'] }>` display: flex; - align-items: center; + flex-direction: column; align-self: stretch; - gap: 8px; padding: 16px; width: calc(100% - 32px); border-radius: 16px; @@ -30,20 +38,17 @@ const Container = styled.div<{ $withClick: boolean; $isError: Props['isError'] } &:hover { cursor: pointer; background-color: ${$isError ? '#351515' : theme.colors.white_opacity['008']}; + ${ControlledVisibility} { + visibility: visible; + } } `} -`; -const IconWrapper = styled.div<{ $isError: Props['isError'] }>` - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - width: 36px; - height: 36px; - border-radius: 8px; - background: ${({ $isError }) => - `linear-gradient(180deg, ${$isError ? 'rgba(237, 124, 124, 0.08)' : 'rgba(249, 249, 249, 0.06)'} 0%, ${$isError ? 'rgba(237, 124, 124, 0.02)' : 'rgba(249, 249, 249, 0.02)'} 100%)`}; + &:hover { + ${ControlledVisibility} { + visibility: visible; + } + } `; const Title = styled(Text)` @@ -51,6 +56,7 @@ const Title = styled(Text)` overflow: hidden; white-space: nowrap; text-overflow: ellipsis; + font-size: 14px; `; const SubTitleWrapper = styled.div` @@ -71,45 +77,96 @@ const ActionsWrapper = styled.div` margin-left: auto; `; -export const DataTab: React.FC = ({ title, subTitle, logo, monitors, isActive, isError, onClick, children }) => { - const renderMonitors = useCallback(() => { - if (!monitors) return null; - - return ( - <> - {'•'} - - - ); - }, [monitors]); - - const renderActiveStatus = useCallback(() => { - if (typeof isActive !== 'boolean') return null; - - return ( - <> - {'•'} - - - ); - }, [isActive]); +const HoverText = styled(Text)` + margin-right: 16px; +`; + +export const DataTab: React.FC = ({ + title, + subTitle, + logo, + hoverText, + monitors, + monitorsWithLabels, + isActive, + isError, + withExtend, + isExtended, + renderExtended, + renderActions, + onClick, + ...props +}) => { + const [extend, setExtend] = useState(isExtended || false); + + const renderMonitors = useCallback( + (withSeperator: boolean) => { + if (!monitors || !monitors.length) return null; + + return ( + <> + {withSeperator && {'•'}} + + + ); + }, + [monitors], + ); + + const renderActiveStatus = useCallback( + (withSeperator: boolean) => { + if (typeof isActive !== 'boolean') return null; + + return ( + <> + {withSeperator && {'•'}} + + + ); + }, + [isActive], + ); return ( - - - - - - - {title} - - {subTitle} - {renderMonitors()} - {renderActiveStatus()} - - - - {children} + + + + + + {title} + + {subTitle && {subTitle}} + {renderMonitors(!!subTitle)} + {renderActiveStatus(!!monitors?.length)} + + + + + {!!hoverText && ( + + + {hoverText} + + + )} + {renderActions && renderActions()} + {withExtend && ( + + + setExtend((prev) => !prev)}> + + + + )} + + + + {extend && renderExtended && ( + + + {renderExtended()} + + )} ); }; diff --git a/frontend/webapp/reuseable-components/divider/index.tsx b/frontend/webapp/reuseable-components/divider/index.tsx index 35294176b..392fbca30 100644 --- a/frontend/webapp/reuseable-components/divider/index.tsx +++ b/frontend/webapp/reuseable-components/divider/index.tsx @@ -1,11 +1,11 @@ import React from 'react'; import styled from 'styled-components'; import { hexPercentValues } from '@/styles'; -import type { NotificationType } from '@/types'; +import { NOTIFICATION_TYPE } from '@/types'; interface Props { orientation?: 'horizontal' | 'vertical'; - type?: NotificationType; // this is to apply coloring to the divider + type?: NOTIFICATION_TYPE; // this is to apply coloring to the divider thickness?: number; length?: string; margin?: string; diff --git a/frontend/webapp/reuseable-components/extend-icon/index.tsx b/frontend/webapp/reuseable-components/extend-icon/index.tsx index c36caae5e..62b93d8e4 100644 --- a/frontend/webapp/reuseable-components/extend-icon/index.tsx +++ b/frontend/webapp/reuseable-components/extend-icon/index.tsx @@ -5,10 +5,11 @@ import styled from 'styled-components'; interface Props { extend: boolean; size?: number; - align?: 'left' | 'right'; + align?: 'left' | 'right' | 'center'; } const Icon = styled(Image)<{ $align?: Props['align'] }>` + margin: ${({ $align }) => ($align === 'right' ? 'auto 0 auto auto' : $align === 'left' ? 'auto auto auto 0' : 'auto')}; &.open { transform: rotate(180deg); } @@ -16,9 +17,8 @@ const Icon = styled(Image)<{ $align?: Props['align'] }>` transform: rotate(0deg); } transition: transform 0.3s; - margin-${({ $align }) => ($align === 'right' ? 'left' : 'right')}: auto; `; -export const ExtendIcon: React.FC = ({ extend, size = 14, align = 'right' }) => { +export const ExtendIcon: React.FC = ({ extend, size = 14, align = 'center' }) => { return ; }; diff --git a/frontend/webapp/reuseable-components/icon-button/index.tsx b/frontend/webapp/reuseable-components/icon-button/index.tsx new file mode 100644 index 000000000..149d976c5 --- /dev/null +++ b/frontend/webapp/reuseable-components/icon-button/index.tsx @@ -0,0 +1,63 @@ +import React, { CSSProperties, PropsWithChildren } from 'react'; +import styled, { keyframes } from 'styled-components'; + +interface Props extends PropsWithChildren { + onClick?: () => void; + withPing?: boolean; + pingColor?: CSSProperties['backgroundColor']; +} + +const Button = styled.button` + position: relative; + width: 36px; + height: 36px; + border: none; + border-radius: 100%; + background-color: transparent; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + &:hover { + background-color: ${({ theme }) => theme.colors.white_opacity['008']}; + } +`; + +const pingAnimation = keyframes` + 0% { + transform: scale(1); + opacity: 1; + } + 75%, 100% { + transform: scale(2); + opacity: 0; + } +`; + +const Ping = styled.div<{ $color: Props['pingColor'] }>` + position: absolute; + top: 8px; + right: 8px; + width: 6px; + height: 6px; + border-radius: 100%; + background-color: ${({ theme, $color }) => $color || theme.colors.secondary}; + + &::after { + content: ''; + position: absolute; + inset: 0; + border-radius: 100%; + background-color: ${({ theme, $color }) => $color || theme.colors.secondary}; + animation: ${pingAnimation} 1.5s cubic-bezier(0, 0, 0.2, 1) infinite; + } +`; + +export const IconButton: React.FC = ({ children, onClick, withPing, pingColor, ...props }) => { + return ( + + ); +}; diff --git a/frontend/webapp/reuseable-components/icon-wrapped/index.tsx b/frontend/webapp/reuseable-components/icon-wrapped/index.tsx new file mode 100644 index 000000000..53bfe2031 --- /dev/null +++ b/frontend/webapp/reuseable-components/icon-wrapped/index.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import Image from 'next/image'; +import styled from 'styled-components'; + +interface Props { + src: string; + alt?: string; + isError?: boolean; +} + +const Container = styled.div<{ $isError: Props['isError'] }>` + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + width: 36px; + height: 36px; + border-radius: 8px; + background: ${({ $isError }) => + $isError ? 'linear-gradient(180deg, rgba(237, 124, 124, 0.2) 0%, rgba(237, 124, 124, 0.05) 100%);' : 'linear-gradient(180deg, rgba(249, 249, 249, 0.2) 0%, rgba(249, 249, 249, 0.05) 100%);'}; +`; + +export const IconWrapped: React.FC = ({ src, alt = '', isError }) => { + return ( + + {alt} + + ); +}; diff --git a/frontend/webapp/reuseable-components/index.ts b/frontend/webapp/reuseable-components/index.ts index 6735fe776..e0a2e3346 100644 --- a/frontend/webapp/reuseable-components/index.ts +++ b/frontend/webapp/reuseable-components/index.ts @@ -36,3 +36,6 @@ export * from './extend-icon'; export * from './condition-details'; export * from './data-card'; export * from './data-tab'; +export * from './code'; +export * from './icon-button'; +export * from './icon-wrapped'; diff --git a/frontend/webapp/reuseable-components/modal/warning-modal/index.tsx b/frontend/webapp/reuseable-components/modal/warning-modal/index.tsx index bd8e76109..e3349a478 100644 --- a/frontend/webapp/reuseable-components/modal/warning-modal/index.tsx +++ b/frontend/webapp/reuseable-components/modal/warning-modal/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useKeyDown } from '@/hooks'; import styled from 'styled-components'; -import type { NotificationType } from '@/types'; +import { NOTIFICATION_TYPE } from '@/types'; import { Button, ButtonProps, Modal, NotificationNote, Text } from '@/reuseable-components'; interface ButtonParams { @@ -16,7 +16,7 @@ interface Props { title: string; description: string; note?: { - type: NotificationType; + type: NOTIFICATION_TYPE; title: string; message: string; }; diff --git a/frontend/webapp/reuseable-components/monitors-icons/index.tsx b/frontend/webapp/reuseable-components/monitors-icons/index.tsx index 9c580d451..8616259a1 100644 --- a/frontend/webapp/reuseable-components/monitors-icons/index.tsx +++ b/frontend/webapp/reuseable-components/monitors-icons/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import Image from 'next/image'; +import theme from '@/styles/theme'; import { FlexRow } from '@/styles'; import { capitalizeFirstLetter } from '@/utils'; import { Text, Tooltip } from '@/reuseable-components'; @@ -15,14 +16,18 @@ export const MonitorsIcons: React.FC = ({ monitors, withTooltips, withLab return ( {monitors.map((str) => { - const signal = str.toLocaleLowerCase(); + const signal = str.toLowerCase(); const signalDisplayName = capitalizeFirstLetter(signal); return ( - + {signal} - {withLabels && {signalDisplayName}} + {withLabels && ( + + {signalDisplayName} + + )} ); diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/edges/labeled-edge.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/edges/labeled-edge.tsx index c6f2452f8..687064b90 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/edges/labeled-edge.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/edges/labeled-edge.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { EDGE_TYPES } from '@/types'; import styled from 'styled-components'; import { EdgeLabelRenderer, BaseEdge, type EdgeProps, type Edge, getSmoothStepPath } from '@xyflow/react'; @@ -10,7 +11,7 @@ interface Props isMultiTarget?: boolean; isError?: boolean; }, - 'labeled' + EDGE_TYPES.LABELED > > {} diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/index.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/index.tsx index bae0649db..b1fef9d64 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/index.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/index.tsx @@ -3,10 +3,13 @@ import '@xyflow/react/dist/style.css'; import styled from 'styled-components'; import AddNode from './nodes/add-node'; import BaseNode from './nodes/base-node'; +import EdgedNode from './nodes/edged-node'; import FrameNode from './nodes/frame-node'; import ScrollNode from './nodes/scroll-node'; import HeaderNode from './nodes/header-node'; import LabeledEdge from './edges/labeled-edge'; +import SkeletonNode from './nodes/skeleton-node'; +import { EDGE_TYPES, NODE_TYPES } from '@/types'; import { Controls, type Edge, type Node, type OnEdgesChange, type OnNodesChange, ReactFlow } from '@xyflow/react'; interface Props { @@ -41,15 +44,17 @@ const ControllerWrapper = styled.div` `; const nodeTypes = { - header: HeaderNode, - add: AddNode, - base: BaseNode, - frame: FrameNode, - scroll: ScrollNode, + [NODE_TYPES.HEADER]: HeaderNode, + [NODE_TYPES.ADD]: AddNode, + [NODE_TYPES.BASE]: BaseNode, + [NODE_TYPES.EDGED]: EdgedNode, + [NODE_TYPES.FRAME]: FrameNode, + [NODE_TYPES.SCROLL]: ScrollNode, + [NODE_TYPES.SKELETON]: SkeletonNode, }; const edgeTypes = { - labeled: LabeledEdge, + [EDGE_TYPES.LABELED]: LabeledEdge, }; export const NodeDataFlow: React.FC = ({ nodes, edges, onNodeClick, onNodesChange, onEdgesChange }) => { 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 10389bd8d..c9cd52278 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 @@ -2,7 +2,7 @@ import React from 'react'; import Image from 'next/image'; import styled from 'styled-components'; import { Text } from '@/reuseable-components'; -import { OVERVIEW_NODE_TYPES, STATUSES } from '@/types'; +import { NODE_TYPES, OVERVIEW_NODE_TYPES, STATUSES } from '@/types'; import { Handle, type Node, type NodeProps, Position } from '@xyflow/react'; interface Props @@ -16,7 +16,7 @@ interface Props title: string; subTitle: string; }, - 'add' + NODE_TYPES.ADD > > {} 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 fb3cb44b1..e7ea1a084 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 @@ -5,7 +5,7 @@ import styled from 'styled-components'; import { getStatusIcon } from '@/utils'; import { Checkbox, DataTab } from '@/reuseable-components'; import { Handle, type Node, type NodeProps, Position } from '@xyflow/react'; -import { type ActionDataParsed, type ActualDestination, type InstrumentationRuleSpec, type K8sActualSource, OVERVIEW_ENTITY_TYPES, STATUSES, WorkloadId } from '@/types'; +import { type ActionDataParsed, type ActualDestination, type InstrumentationRuleSpec, type K8sActualSource, NODE_TYPES, NOTIFICATION_TYPE, OVERVIEW_ENTITY_TYPES, STATUSES, WorkloadId } from '@/types'; interface Props extends NodeProps< @@ -23,7 +23,7 @@ interface Props isActive?: boolean; raw: InstrumentationRuleSpec | K8sActualSource | ActionDataParsed | ActualDestination; }, - 'base' + NODE_TYPES.BASE > > {} @@ -63,7 +63,7 @@ const BaseNode: React.FC = ({ id: nodeId, data }) => { <> {/* TODO: handle instrumentation rules for sources */} {isError ? ( - + ) : // : type === 'source' && SOME_INDICATOR_THAT_THIS_IS_INSTRUMENTED ? ( ) null} @@ -72,23 +72,11 @@ const BaseNode: React.FC = ({ id: nodeId, data }) => { ); }; - const renderHandles = () => { - switch (type) { - case 'source': - return ; - case 'destination': - return ; - default: - return null; - } - }; - return ( - {}}> - {renderActions()} - {renderHandles()} - + {}} renderActions={renderActions} /> + + ); }; diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/edged-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/edged-node.tsx new file mode 100644 index 000000000..c90cfd27a --- /dev/null +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/edged-node.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { NODE_TYPES } from '@/types'; +import styled from 'styled-components'; +import { Handle, type Node, type NodeProps, Position } from '@xyflow/react'; + +interface Props + extends NodeProps< + Node< + { + nodeWidth: number; + nodeHeight: number; + }, + NODE_TYPES.EDGED + > + > {} + +const Container = styled.div<{ $nodeWidth: Props['data']['nodeWidth']; $nodeHeight: Props['data']['nodeHeight'] }>` + width: ${({ $nodeWidth }) => `${$nodeWidth}px`}; + height: ${({ $nodeHeight }) => `${$nodeHeight}px`}; + opacity: 0; +`; + +const EdgedNode: React.FC = ({ data }) => { + const { nodeWidth, nodeHeight } = data; + + return ( + + + + + ); +}; + +export default EdgedNode; diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/frame-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/frame-node.tsx index c93d20a18..6c06e6369 100644 --- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/frame-node.tsx +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/frame-node.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { NODE_TYPES } from '@/types'; import styled from 'styled-components'; import { Handle, type Node, type NodeProps, Position } from '@xyflow/react'; @@ -9,7 +10,7 @@ interface Props nodeWidth: number; nodeHeight: number; }, - 'frame' + NODE_TYPES.FRAME > > {} 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 3261de9bc..9a2e9522b 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,5 +1,6 @@ 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'; @@ -16,7 +17,7 @@ interface Props title: string; tagValue: number; }, - 'header' + NODE_TYPES.HEADER > > {} 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 3479fe43e..14a4f9695 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,9 +1,9 @@ import React, { useEffect, useRef } from 'react'; import BaseNode from './base-node'; import styled from 'styled-components'; -import { type Node, type NodeProps } from '@xyflow/react'; -import { type K8sActualSource, OVERVIEW_ENTITY_TYPES, STATUSES, type WorkloadId } from '@/types'; import { useDrawerStore } from '@/store'; +import { type Node, type NodeProps } from '@xyflow/react'; +import { type K8sActualSource, NODE_TYPES, OVERVIEW_ENTITY_TYPES, STATUSES, type WorkloadId } from '@/types'; interface Props extends NodeProps< @@ -24,12 +24,12 @@ interface Props imageUri: string; raw: K8sActualSource; }, - 'scroll-item' + NODE_TYPES.BASE > >[]; onScroll: (params: { clientHeight: number; scrollHeight: number; scrollTop: number }) => void; }, - 'scroll' + NODE_TYPES.SCROLL > > {} @@ -57,16 +57,17 @@ const ScrollNode: React.FC = ({ data, ...rest }) => { // @ts-ignore - these properties are available on the EventTarget, TS is not aware of it const { clientHeight, scrollHeight, scrollTop } = e.target || { clientHeight: 0, scrollHeight: 0, scrollTop: 0 }; - const isTop = scrollTop === 0; - const isBottom = scrollHeight - scrollTop <= clientHeight; - - if (isTop) { - console.log('Reached top of scroll-node'); - } else if (isBottom) { - console.log('Reached bottom of scroll-node'); - } if (!!onScroll) onScroll({ clientHeight, scrollHeight, scrollTop }); + + // TODO: Use the following once we have to handle paginated API requests: + // const isTop = scrollTop === 0; + // const isBottom = scrollHeight - scrollTop <= clientHeight; + // if (isTop) { + // console.log('Reached top of scroll-node'); + // } else if (isBottom) { + // console.log('Reached bottom of scroll-node'); + // } }; const { current } = containerRef; @@ -86,7 +87,7 @@ const ScrollNode: React.FC = ({ data, ...rest }) => { setSelectedItem({ id: item.data.id, type: item.data.type, item: item.data.raw }); }} > - + ))} diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/skeleton-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/skeleton-node.tsx new file mode 100644 index 000000000..49a484329 --- /dev/null +++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/skeleton-node.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { NODE_TYPES } from '@/types'; +import styled from 'styled-components'; +import { type Node, type NodeProps } from '@xyflow/react'; +import { SkeletonLoader } from '@/reuseable-components/skeleton-loader'; + +interface Props + extends NodeProps< + Node< + { + nodeWidth: number; + size: number; + }, + NODE_TYPES.SKELETON + > + > {} + +const Container = styled.div<{ $nodeWidth: Props['data']['nodeWidth'] }>` + width: ${({ $nodeWidth }) => `${$nodeWidth}px`}; +`; + +const SkeletonNode: React.FC = ({ id: nodeId, data }) => { + const { nodeWidth, size } = data; + + return ( + + + + ); +}; + +export default SkeletonNode; diff --git a/frontend/webapp/reuseable-components/notification-note/index.tsx b/frontend/webapp/reuseable-components/notification-note/index.tsx index 8223c695a..dae9046f7 100644 --- a/frontend/webapp/reuseable-components/notification-note/index.tsx +++ b/frontend/webapp/reuseable-components/notification-note/index.tsx @@ -5,15 +5,15 @@ import { Divider } from '../divider'; import styled from 'styled-components'; import { getStatusIcon } from '@/utils'; import { progress, slide } from '@/styles'; -import type { Notification, NotificationType } from '@/types'; +import { type Notification, NOTIFICATION_TYPE } from '@/types'; interface OnCloseParams { asSeen: boolean; } -interface NotificationProps { +interface Props { id?: string; - type: NotificationType; + type: NOTIFICATION_TYPE; title?: Notification['title']; message?: Notification['message']; action?: { @@ -37,7 +37,7 @@ const Container = styled.div<{ $isLeaving?: boolean }>` } `; -const DurationAnimation = styled.div<{ $type: NotificationType }>` +const DurationAnimation = styled.div<{ $type: Props['type'] }>` position: absolute; bottom: -1px; left: 0; @@ -49,7 +49,7 @@ const DurationAnimation = styled.div<{ $type: NotificationType }>` animation: ${progress.out} ${TOAST_DURATION - TRANSITION_DURATION}ms forwards; `; -const Content = styled.div<{ $type: NotificationType }>` +const Content = styled.div<{ $type: Props['type'] }>` display: flex; align-items: center; flex: 1; @@ -65,12 +65,12 @@ const TextWrapper = styled.div` height: 12px; `; -const Title = styled(Text)<{ $type: NotificationType }>` +const Title = styled(Text)<{ $type: Props['type'] }>` font-size: 14px; color: ${({ $type, theme }) => theme.text[$type]}; `; -const Message = styled(Text)<{ $type: NotificationType }>` +const Message = styled(Text)<{ $type: Props['type'] }>` font-size: 12px; color: ${({ $type, theme }) => theme.text[$type]}; `; @@ -105,7 +105,7 @@ const CloseButton = styled(Image)` } `; -export const NotificationNote: React.FC = ({ type, title, message, action, onClose, style }) => { +export const NotificationNote: React.FC = ({ type, title, message, action, onClose, style }) => { // These are for handling transitions: // isEntering - to stop the progress bar from rendering before the toast is fully slide-in // isLeaving - to trigger the slide-out animation diff --git a/frontend/webapp/reuseable-components/skeleton-loader/index.tsx b/frontend/webapp/reuseable-components/skeleton-loader/index.tsx index be6b1fb56..fac3fd26a 100644 --- a/frontend/webapp/reuseable-components/skeleton-loader/index.tsx +++ b/frontend/webapp/reuseable-components/skeleton-loader/index.tsx @@ -1,64 +1,63 @@ import React from 'react'; +import { FlexColumn } from '@/styles'; import styled, { keyframes } from 'styled-components'; -const shimmer = keyframes` +const shimmer = keyframes<{ $width: string }>` 0% { - background-position: -1000px 0; + background-position: -500px 0; } 100% { - background-position: 1000px 0; + background-position: 500px 0; } `; -const SkeletonLoaderWrapper = styled.div` +const Container = styled.div` display: flex; flex-direction: column; - gap: 1rem; + gap: 16px; `; const SkeletonItem = styled.div` display: flex; align-items: center; - gap: 1rem; + gap: 16px; `; -const SkeletonThumbnail = styled.div` +const Thumbnail = styled.div` width: 50px; height: 50px; border-radius: 8px; - background: ${({ theme }) => `linear-gradient(90deg, ${theme.colors.primary} 25%, ${theme.colors.primary} 50%, ${theme.colors.darker_grey} 75%)`}; + background: ${({ theme }) => `linear-gradient(90deg, ${theme.colors.dropdown_bg_2} 25%, ${theme.colors.dropdown_bg_2} 50%, ${theme.colors.border} 75%)`}; background-size: 200% 100%; animation: ${shimmer} 10s infinite linear; `; -const SkeletonText = styled.div` +const LineWrapper = styled(FlexColumn)` flex: 1; + gap: 12px; `; -const SkeletonLine = styled.div<{ $width: string }>` +const Line = styled.div<{ $width: string }>` + width: ${({ $width }) => $width}; height: 16px; - margin-bottom: 0.5rem; - background: ${({ theme }) => `linear-gradient(90deg, ${theme.colors.primary} 25%, ${theme.colors.primary} 50%, ${theme.colors.darker_grey} 75%)`}; + background: ${({ theme }) => `linear-gradient(90deg, ${theme.colors.dropdown_bg_2} 25%, ${theme.colors.dropdown_bg_2} 50%, ${theme.colors.border} 75%)`}; background-size: 200% 100%; animation: ${shimmer} 1.5s infinite linear; - width: ${({ $width }) => $width}; border-radius: 4px; `; -const SkeletonLoader: React.FC<{ size: number }> = ({ size = 5 }) => { +export const SkeletonLoader: React.FC<{ size?: number }> = ({ size = 5 }) => { return ( - + {[...Array(size)].map((_, index) => ( - - - - - + + + + + ))} - +
); }; - -export { SkeletonLoader }; diff --git a/frontend/webapp/reuseable-components/status/index.tsx b/frontend/webapp/reuseable-components/status/index.tsx index c9c891987..0563a70dc 100644 --- a/frontend/webapp/reuseable-components/status/index.tsx +++ b/frontend/webapp/reuseable-components/status/index.tsx @@ -1,7 +1,8 @@ import React from 'react'; import Image from 'next/image'; -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import { getStatusIcon } from '@/utils'; +import { NOTIFICATION_TYPE } from '@/types'; import { Divider, Text } from '@/reuseable-components'; import theme, { hexPercentValues } from '@/styles/theme'; @@ -76,7 +77,12 @@ export const Status: React.FC = ({ title, subtitle, size = 12, fami {withIcon && ( {/* TODO: SVG to JSX */} - status + status )} @@ -90,7 +96,7 @@ export const Status: React.FC = ({ title, subtitle, size = 12, fami {!!subtitle && ( - + {subtitle} diff --git a/frontend/webapp/store/index.ts b/frontend/webapp/store/index.ts index b35649a7d..017c41823 100644 --- a/frontend/webapp/store/index.ts +++ b/frontend/webapp/store/index.ts @@ -1,5 +1,4 @@ export * from './useAppStore'; -export * from './useBooleanStore'; export * from './useConnectionStore'; export * from './useDrawerStore'; export * from './useModalStore'; diff --git a/frontend/webapp/store/useBooleanStore.ts b/frontend/webapp/store/useBooleanStore.ts deleted file mode 100644 index 0a83796f9..000000000 --- a/frontend/webapp/store/useBooleanStore.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { create } from 'zustand'; - -interface StoreState { - isPolling: boolean; - togglePolling: (bool?: boolean) => void; -} - -export const useBooleanStore = create((set) => ({ - isPolling: false, - togglePolling: (bool) => set(({ isPolling }) => ({ isPolling: bool ?? !isPolling })), -})); diff --git a/frontend/webapp/store/useDrawerStore.ts b/frontend/webapp/store/useDrawerStore.ts index a4efa3a3e..bef2ef993 100644 --- a/frontend/webapp/store/useDrawerStore.ts +++ b/frontend/webapp/store/useDrawerStore.ts @@ -1,19 +1,19 @@ -// drawerStore.ts import { create } from 'zustand'; import type { ActionDataParsed, ActualDestination, InstrumentationRuleSpec, K8sActualSource, OVERVIEW_ENTITY_TYPES, WorkloadId } from '@/types'; -type ItemType = OVERVIEW_ENTITY_TYPES; +export enum DRAWER_OTHER_TYPES { + DESCRIBE_ODIGOS = 'describe-odigos', +} -export interface DrawerBaseItem { +export interface DrawerItem { + type: OVERVIEW_ENTITY_TYPES | DRAWER_OTHER_TYPES; id: string | WorkloadId; item?: InstrumentationRuleSpec | K8sActualSource | ActionDataParsed | ActualDestination; - type: ItemType; - // Add common properties here } interface DrawerStoreState { - selectedItem: DrawerBaseItem | null; - setSelectedItem: (item: DrawerBaseItem | null) => void; + selectedItem: DrawerItem | null; + setSelectedItem: (item: DrawerItem | null) => void; isDrawerOpen: boolean; openDrawer: () => void; closeDrawer: () => void; diff --git a/frontend/webapp/store/useNotificationStore.ts b/frontend/webapp/store/useNotificationStore.ts index 273371e9b..d16485b3a 100644 --- a/frontend/webapp/store/useNotificationStore.ts +++ b/frontend/webapp/store/useNotificationStore.ts @@ -1,9 +1,11 @@ import { create } from 'zustand'; import type { Notification } from '@/types'; +export type NotifyPayload = Omit; + interface StoreState { notifications: Notification[]; - addNotification: (notif: { type: Notification['type']; title: Notification['title']; message: Notification['message']; crdType: Notification['crdType']; target: Notification['target'] }) => void; + addNotification: (notif: NotifyPayload) => void; markAsDismissed: (id: string) => void; markAsSeen: (id: string) => void; removeNotification: (id: string) => void; diff --git a/frontend/webapp/styles/theme.ts b/frontend/webapp/styles/theme.ts index 48fa07254..6e1e71cee 100644 --- a/frontend/webapp/styles/theme.ts +++ b/frontend/webapp/styles/theme.ts @@ -245,6 +245,7 @@ const text = { const font_family = { primary: 'Inter, sans-serif', secondary: 'Kode Mono, sans-serif', + code: 'IBM Plex Mono, monospace', }; // Define the theme interface diff --git a/frontend/webapp/types/common.ts b/frontend/webapp/types/common.ts index 78bdec9a5..0cd5db4e9 100644 --- a/frontend/webapp/types/common.ts +++ b/frontend/webapp/types/common.ts @@ -11,11 +11,17 @@ export interface Condition { lastTransitionTime: string; } -export type NotificationType = 'warning' | 'error' | 'success' | 'info' | 'default'; +export enum NOTIFICATION_TYPE { + WARNING = 'warning', + ERROR = 'error', + SUCCESS = 'success', + INFO = 'info', + DEFAULT = 'default', +} export interface Notification { id: string; - type: NotificationType; + type: NOTIFICATION_TYPE; title?: string; message?: string; crdType?: string; diff --git a/frontend/webapp/types/data-flow.ts b/frontend/webapp/types/data-flow.ts new file mode 100644 index 000000000..dcd65b74e --- /dev/null +++ b/frontend/webapp/types/data-flow.ts @@ -0,0 +1,13 @@ +export enum NODE_TYPES { + HEADER = 'header', + ADD = 'add', + BASE = 'base', + EDGED = 'edged', + FRAME = 'frame', + SCROLL = 'scroll', + SKELETON = 'skeleton', +} + +export enum EDGE_TYPES { + LABELED = 'labeled', +} diff --git a/frontend/webapp/types/describe.ts b/frontend/webapp/types/describe.ts new file mode 100644 index 000000000..ead5c51e0 --- /dev/null +++ b/frontend/webapp/types/describe.ts @@ -0,0 +1,126 @@ +interface EntityProperty { + name: string; + value: string; + status?: string; + explain?: string; +} + +interface InstrumentationLabelsAnalyze { + instrumented: EntityProperty; + workload?: EntityProperty; + namespace?: EntityProperty; + instrumentedText?: EntityProperty; +} + +interface InstrumentationConfigAnalyze { + created: EntityProperty; + createTime?: EntityProperty; +} + +interface ContainerRuntimeInfoAnalyze { + containerName: EntityProperty; + language: EntityProperty; + runtimeVersion: EntityProperty; + envVars: EntityProperty[]; +} + +interface RuntimeInfoAnalyze { + generation: EntityProperty; + containers: ContainerRuntimeInfoAnalyze[]; +} + +interface InstrumentedApplicationAnalyze { + created: EntityProperty; + createTime?: EntityProperty; + containers: ContainerRuntimeInfoAnalyze[]; +} + +interface ContainerWorkloadManifestAnalyze { + containerName: EntityProperty; + devices: EntityProperty; + originalEnv: EntityProperty[]; +} + +interface InstrumentationDeviceAnalyze { + statusText: EntityProperty; + containers: ContainerWorkloadManifestAnalyze[]; +} + +interface InstrumentationInstanceAnalyze { + healthy: EntityProperty; + message?: EntityProperty; + identifyingAttributes: EntityProperty[]; +} + +interface PodContainerAnalyze { + containerName: EntityProperty; + actualDevices: EntityProperty; + instrumentationInstances: InstrumentationInstanceAnalyze[]; +} + +interface PodAnalyze { + podName: EntityProperty; + nodeName: EntityProperty; + phase: EntityProperty; + containers: PodContainerAnalyze[]; +} + +interface SourceAnalyze { + name: EntityProperty; + kind: EntityProperty; + namespace: EntityProperty; + labels: InstrumentationLabelsAnalyze; + + instrumentationConfig: InstrumentationConfigAnalyze; + runtimeInfo?: RuntimeInfoAnalyze; + instrumentedApplication: InstrumentedApplicationAnalyze; + instrumentationDevice: InstrumentationDeviceAnalyze; + + totalPods: number; + podsPhasesCount: string; + pods: PodAnalyze[]; +} + +interface ClusterCollectorAnalyze { + enabled: EntityProperty; + collectorGroup: EntityProperty; + deployed?: EntityProperty; + deployedError?: EntityProperty; + collectorReady?: EntityProperty; + deploymentCreated: EntityProperty; + expectedReplicas?: EntityProperty; + healthyReplicas?: EntityProperty; + failedReplicas?: EntityProperty; + failedReplicasReason?: EntityProperty; +} + +interface NodeCollectorAnalyze { + enabled: EntityProperty; + collectorGroup: EntityProperty; + deployed?: EntityProperty; + deployedError?: EntityProperty; + collectorReady?: EntityProperty; + daemonSet: EntityProperty; + desiredNodes?: EntityProperty; + currentNodes?: EntityProperty; + updatedNodes?: EntityProperty; + availableNodes?: EntityProperty; +} + +interface OdigosAnalyze { + odigosVersion: EntityProperty; + numberOfDestinations: number; + numberOfSources: number; + clusterCollector: ClusterCollectorAnalyze; + nodeCollector: NodeCollectorAnalyze; + isSettled: boolean; + hasErrors: boolean; +} + +export interface DescribeSource { + describeSource: SourceAnalyze; +} + +export interface DescribeOdigos { + describeOdigos: OdigosAnalyze; +} diff --git a/frontend/webapp/types/index.ts b/frontend/webapp/types/index.ts index 514841421..4dee70aa1 100644 --- a/frontend/webapp/types/index.ts +++ b/frontend/webapp/types/index.ts @@ -1,9 +1,10 @@ -export * from './sources'; -export * from './destinations'; export * from './actions'; -export * from './sources'; export * from './common'; export * from './compute-platform'; -export * from './namespace'; +export * from './data-flow'; +export * from './describe'; +export * from './destinations'; export * from './instrumentation-rules'; export * from './metrics'; +export * from './namespace'; +export * from './sources'; diff --git a/frontend/webapp/utils/constants/string.tsx b/frontend/webapp/utils/constants/string.tsx index 56a4ab275..8ebdd825f 100644 --- a/frontend/webapp/utils/constants/string.tsx +++ b/frontend/webapp/utils/constants/string.tsx @@ -1,5 +1,3 @@ -import type { NotificationType } from '@/types'; - export const SETUP = { MONITORS: { LOGS: 'Logs', @@ -39,16 +37,6 @@ export const FORM_ALERTS = { CANNOT_EDIT_RULE: 'Cannot edit instrumentation rule of this type', }; -export const NOTIFICATION: { - [key: string]: NotificationType; -} = { - ERROR: 'error', - SUCCESS: 'success', - WARNING: 'warning', - INFO: 'info', - DEFAULT: 'default', -}; - export const BACKEND_BOOLEAN = { FALSE: 'False', TRUE: 'True', @@ -64,9 +52,10 @@ export const DATA_CARDS = { RULE_DETAILS: 'Instrumentation Rule Details', DESTINATION_DETAILS: 'Destination Details', SOURCE_DETAILS: 'Source Details', - DETECTED_CONTAINERS: 'Detected Containers', DETECTED_CONTAINERS_DESCRIPTION: 'The system automatically instruments the containers it detects with a supported programming language.', + DESCRIBE_SOURCE: 'Describe Source', + DESCRIBE_ODIGOS: 'Describe Odigos', }; export const DISPLAY_TITLES = { diff --git a/frontend/webapp/utils/functions/formatters/flatten-object-keys/index.ts b/frontend/webapp/utils/functions/formatters/flatten-object-keys/index.ts new file mode 100644 index 000000000..b89355214 --- /dev/null +++ b/frontend/webapp/utils/functions/formatters/flatten-object-keys/index.ts @@ -0,0 +1,62 @@ +/** + * Recursively flattens a nested object into a single-level object where each key + * represents the path to its corresponding value in the original object. Keys for nested + * properties are concatenated using a dot (`.`) as a separator, while array elements + * include their index in square brackets (`[]`). + * + * @param {Record} obj - The input object to be flattened. + * @param {string} [prefix=''] - The current prefix for the keys, used for recursion. + * @param {Record} [result={}] - The accumulator object that stores the flattened result. + * @returns {Record} A new object where all nested properties are flattened into + * a single level with their paths as keys. + * + * @example + * const input = { + * name: { + * name: 'Name', + * value: 'load-generator', + * status: null, + * explain: '...', + * }, + * }; + * + * const output = flattenObjectKeys(input); + * Output: + * { + * 'name.name': 'Name', + * 'name.value': 'load-generator', + * 'name.status': null, + * 'name.explain': '...', + * } + */ + +export const flattenObjectKeys = (obj: Record, prefix: string = '', result: Record = {}) => { + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + const value = obj[key]; + const newKey = prefix ? `${prefix}.${key}` : key; + + if (value !== null && typeof value === 'object' && !Array.isArray(value)) { + // Recurse for nested objects + flattenObjectKeys(value, newKey, result); + } else if (Array.isArray(value)) { + value.forEach((item, index) => { + const arrayKey = `${newKey}[${index}]`; + + if (item !== null && typeof item === 'object') { + // Recurse for objects in arrays + flattenObjectKeys(item, arrayKey, result); + } else { + // Assign primitive array values + result[arrayKey] = item; + } + }); + } else { + // Assign non-object, non-array values + result[newKey] = value; + } + } + } + + return result; +}; diff --git a/frontend/webapp/utils/functions/formatters/index.ts b/frontend/webapp/utils/functions/formatters/index.ts index 60092f157..64249733c 100644 --- a/frontend/webapp/utils/functions/formatters/index.ts +++ b/frontend/webapp/utils/functions/formatters/index.ts @@ -1,8 +1,10 @@ export * from './clean-object-empty-strings-values'; export * from './extract-monitors'; +export * from './flatten-object-keys'; export * from './format-bytes'; export * from './get-id-from-sse-target'; export * from './get-sse-target-from-id'; export * from './parse-json-string-to-pretty-string'; export * from './safe-json-parse'; +export * from './safe-json-stringify'; export * from './stringify-non-string-values'; diff --git a/frontend/webapp/utils/functions/formatters/safe-json-stringify/index.ts b/frontend/webapp/utils/functions/formatters/safe-json-stringify/index.ts new file mode 100644 index 000000000..0351a873d --- /dev/null +++ b/frontend/webapp/utils/functions/formatters/safe-json-stringify/index.ts @@ -0,0 +1,3 @@ +export const safeJsonStringify = (obj?: Record, indent = 2) => { + return JSON.stringify(obj || {}, null, indent); +}; diff --git a/frontend/webapp/utils/functions/icons/get-status-icon/index.ts b/frontend/webapp/utils/functions/icons/get-status-icon/index.ts index 8f7c8d96d..50f15c457 100644 --- a/frontend/webapp/utils/functions/icons/get-status-icon/index.ts +++ b/frontend/webapp/utils/functions/icons/get-status-icon/index.ts @@ -1,18 +1,18 @@ -import { type NotificationType } from '@/types'; +import { NOTIFICATION_TYPE } from '@/types'; const BRAND_ICON = '/brand/odigos-icon.svg'; -export const getStatusIcon = (status?: NotificationType) => { +export const getStatusIcon = (status?: NOTIFICATION_TYPE) => { if (!status) return BRAND_ICON; switch (status) { - case 'success': + case NOTIFICATION_TYPE.SUCCESS: return '/icons/notification/success-icon.svg'; - case 'error': + case NOTIFICATION_TYPE.ERROR: return '/icons/notification/error-icon2.svg'; - case 'warning': + case NOTIFICATION_TYPE.WARNING: return '/icons/notification/warning-icon2.svg'; - case 'info': + case NOTIFICATION_TYPE.INFO: return '/icons/common/info.svg'; default: return BRAND_ICON; diff --git a/frontend/webapp/yarn.lock b/frontend/webapp/yarn.lock index 200d61fe5..40efdcef6 100644 --- a/frontend/webapp/yarn.lock +++ b/frontend/webapp/yarn.lock @@ -2265,6 +2265,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== +"@types/prismjs@^1.26.0": + version "1.26.5" + resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.5.tgz#72499abbb4c4ec9982446509d2f14fb8483869d6" + integrity sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ== + "@types/prop-types@*": version "15.7.13" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" @@ -2953,6 +2958,11 @@ client-only@0.0.1: resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== +clsx@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -5142,6 +5152,14 @@ pretty-bytes@^5.6.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== +prism-react-renderer@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz#ac63b7f78e56c8f2b5e76e823a976d5ede77e35f" + integrity sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig== + dependencies: + "@types/prismjs" "^1.26.0" + clsx "^2.0.0" + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" diff --git a/helm/odigos/templates/odigos-config-cm.yaml b/helm/odigos/templates/odigos-config-cm.yaml index 0ec3577fd..84d7d608e 100644 --- a/helm/odigos/templates/odigos-config-cm.yaml +++ b/helm/odigos/templates/odigos-config-cm.yaml @@ -56,6 +56,12 @@ data: {{- with .Values.collectorNode.limitMemoryMiB }} limitMemoryMiB: {{ . }} {{- end }} + {{- with .Values.collectorNode.requestCPUm }} + requestCPUm: {{ . }} + {{- end }} + {{- with .Values.collectorNode.limitCPUm }} + limitCPUm: {{ . }} + {{- end }} {{- with .Values.collectorNode.memoryLimiterLimitMiB }} memoryLimiterLimitMiB: {{ . }} {{- end }} diff --git a/helm/odigos/templates/ui/clusterrole.yaml b/helm/odigos/templates/ui/clusterrole.yaml index 80a07c803..cb7992b83 100644 --- a/helm/odigos/templates/ui/clusterrole.yaml +++ b/helm/odigos/templates/ui/clusterrole.yaml @@ -43,6 +43,7 @@ rules: - deployments - statefulsets - daemonsets + - replicasets verbs: - get - list diff --git a/helm/odigos/templates/ui/service.yaml b/helm/odigos/templates/ui/service.yaml index c3eb92989..c381c2c3b 100644 --- a/helm/odigos/templates/ui/service.yaml +++ b/helm/odigos/templates/ui/service.yaml @@ -9,7 +9,7 @@ spec: ports: - name: ui port: 3000 - - name: 'beta-ui' + - name: 'legacy-ui' port: 3001 - name: otlp port: 4317 diff --git a/helm/odigos/values.yaml b/helm/odigos/values.yaml index fbcd4501e..8955e7f94 100644 --- a/helm/odigos/values.yaml +++ b/helm/odigos/values.yaml @@ -77,6 +77,17 @@ collectorNode: # default value is 2x the memory request. limitMemoryMiB: 500 + # the CPU request for the node collector daemonset. + # it will be embedded in the daemonset as a resource request + # of the form "cpu: m". + # default value is 250m + requestCPUm: 250 + # the CPU limit for the node collector daemonset. + # it will be embedded in the daemonset as a resource limit + # of the form "cpu: m". + # default value is 500m + limitCPUm: 500 + # this parameter sets the "limit_mib" parameter in the memory limiter configuration for the node collector. # it is the hard limit after which a force garbage collection will be performed. # if not set, it will be 50Mi below the memory limit. diff --git a/instrumentation/detector/detector.go b/instrumentation/detector/detector.go new file mode 100644 index 000000000..9d8060544 --- /dev/null +++ b/instrumentation/detector/detector.go @@ -0,0 +1,29 @@ +package detector + +import ( + "context" + + detector "github.com/odigos-io/runtime-detector" +) + +type ProcessEvent = detector.ProcessEvent + +type DetectorOption = detector.DetectorOption + +const ( + ProcessExecEvent = detector.ProcessExecEvent + ProcessExitEvent = detector.ProcessExitEvent +) + +// Detector is used to report process events. +type Detector interface { + Run(ctx context.Context) error +} + +// NewDetector creates a new Detector. +// +// The process events are sent to the events channel. +// The channel is closed when the detector is stopped (just before returning from Run). +func NewDetector(events chan<- ProcessEvent, opts ...DetectorOption) (Detector, error) { + return detector.NewDetector(events, opts...) +} diff --git a/odiglet/pkg/ebpf/factory.go b/instrumentation/factory.go similarity index 72% rename from odiglet/pkg/ebpf/factory.go rename to instrumentation/factory.go index 4d0098b60..9c0706faa 100644 --- a/odiglet/pkg/ebpf/factory.go +++ b/instrumentation/factory.go @@ -1,14 +1,13 @@ -package ebpf +package instrumentation import ( "context" - odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" - "github.com/odigos-io/odigos/common" - "go.opentelemetry.io/otel/attribute" ) +type Config any + // Settings is used to pass initial configuration to the instrumentation type Settings struct { // ServiceName is the name of the service that is being instrumented @@ -19,7 +18,7 @@ type Settings struct { ResourceAttributes []attribute.KeyValue // InitialConfig is the initial configuration that should be applied to the instrumentation, // it can be used to enable/disable specific instrumentation libraries, configure sampling, etc. - InitialConfig *odigosv1.SdkConfig + InitialConfig Config } // Factory is used to create an Instrumentation @@ -29,15 +28,6 @@ type Factory interface { CreateInstrumentation(ctx context.Context, pid int, settings Settings) (Instrumentation, error) } -// OtelDistribution is a customized version of an OpenTelemetry component. -// see https://opentelemetry.io/docs/concepts/distributions and https://github.com/odigos-io/odigos/pull/1776#discussion_r1853367917 for more information. -// TODO: This should be moved to a common package, since it will require a bigger refactor across multiple components, -// we use this local definition for now. -type OtelDistribution struct { - Language common.ProgrammingLanguage - OtelSdk common.OtelSdk -} - // Instrumentation is used to instrument a running process type Instrumentation interface { // Loads the relevant probes, and will perform any initialization required @@ -46,7 +36,7 @@ type Instrumentation interface { // In case of a failure, an error will be returned and all the resources will be cleaned up. Load(ctx context.Context) error - // Run will attach the probes to the relevant process, and will start the instrumentation. + // Run will start reading events from the probes and export them. // It is a blocking call, and will return only when the instrumentation is stopped. // During the run, telemetry will be collected from the probes and sent with the configured exporter. // Run will return when either a fatal error occurs, the context is canceled, or Close is called. @@ -57,5 +47,5 @@ type Instrumentation interface { Close(ctx context.Context) error // ApplyConfig will send a configuration update to the instrumentation. - ApplyConfig(ctx context.Context, config *odigosv1.SdkConfig) error + ApplyConfig(ctx context.Context, config Config) error } diff --git a/instrumentation/go.mod b/instrumentation/go.mod new file mode 100644 index 000000000..9c085327f --- /dev/null +++ b/instrumentation/go.mod @@ -0,0 +1,21 @@ +module github.com/odigos-io/odigos/instrumentation + +go 1.23.0 + +require ( + github.com/go-logr/logr v1.4.2 + github.com/odigos-io/odigos/common v0.0.0 + github.com/odigos-io/runtime-detector v0.0.4 + go.opentelemetry.io/otel v1.33.0 + golang.org/x/sync v0.10.0 +) + +require ( + github.com/cilium/ebpf v0.16.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect + golang.org/x/sys v0.20.0 // indirect +) + +replace github.com/odigos-io/odigos/common => ../common diff --git a/instrumentation/go.sum b/instrumentation/go.sum new file mode 100644 index 000000000..e0cf9882a --- /dev/null +++ b/instrumentation/go.sum @@ -0,0 +1,46 @@ +github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= +github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= +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.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +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/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= +github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= +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/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/odigos-io/runtime-detector v0.0.4 h1:q4yap5yTApkFFSRdwnU6NZeV+IbzEzWJZv6IEVCVKyE= +github.com/odigos-io/runtime-detector v0.0.4/go.mod h1:m8GfdG6pET0K/8aoZwnX+MkYz8oVroKs8HzRnkuJWt4= +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/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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/instrumentation/manager.go b/instrumentation/manager.go new file mode 100644 index 000000000..94f037603 --- /dev/null +++ b/instrumentation/manager.go @@ -0,0 +1,345 @@ +package instrumentation + +import ( + "context" + "errors" + "fmt" + + "golang.org/x/sync/errgroup" + + "github.com/go-logr/logr" + + "github.com/odigos-io/odigos/instrumentation/detector" +) + +var ( + errNoInstrumentationFactory = errors.New("no ebpf factory found") + errFailedToGetDetails = errors.New("failed to get details for process event") + errFailedToGetDistribution = errors.New("failed to get otel distribution for details") + errFailedToGetConfigGroup = errors.New("failed to get config group") +) + +// ConfigUpdate is used to send a configuration update request to the manager. +// The manager will apply the configuration to all instrumentations that match the config group. +type ConfigUpdate[configGroup ConfigGroup] map[configGroup]Config + +type instrumentationDetails[processGroup ProcessGroup, configGroup ConfigGroup] struct { + inst Instrumentation + pg processGroup + cg configGroup +} + +type ManagerOptions[processGroup ProcessGroup, configGroup ConfigGroup] struct { + Logger logr.Logger + + // Factories is a map of OTel distributions to their corresponding instrumentation factories. + // + // The manager will use this map to create new instrumentations based on the process event. + // If a process event is received and the OTel distribution is not found in this map, + // the manager will ignore the event. + Factories map[OtelDistribution]Factory + + // Handler is used to resolve details, config group, OTel distribution and settings for the instrumentation + // based on the process event. + // + // The handler is also used to report the instrumentation lifecycle events. + Handler *Handler[processGroup, configGroup] + + // DetectorOptions is a list of options to configure the process detector. + // + // The process detector is used to trigger new instrumentation for new relevant processes, + // and un-instrumenting processes once they exit. + DetectorOptions []detector.DetectorOption + + // ConfigUpdates is a channel for receiving configuration updates. + // The manager will apply the configuration to all instrumentations that match the config group. + // + // The caller is responsible for closing the channel once no more updates are expected. + ConfigUpdates <-chan ConfigUpdate[configGroup] +} + +// Manager is used to orchestrate the ebpf instrumentations lifecycle. +type Manager interface { + // Run launches the manger. + // It will block until the context is canceled. + // It is an error to not cancel the context before the program exits, and may result in leaked resources. + Run(ctx context.Context) error +} + +type manager[processGroup ProcessGroup, configGroup ConfigGroup] struct { + // channel for receiving process events, + // used to detect new processes and process exits, and handle their instrumentation accordingly. + procEvents <-chan detector.ProcessEvent + detector detector.Detector + handler *Handler[processGroup, configGroup] + factories map[OtelDistribution]Factory + logger logr.Logger + + // all the active instrumentations by pid, + // this map is not concurrent safe, so it should be accessed only from the main event loop + detailsByPid map[int]*instrumentationDetails[processGroup, configGroup] + + // active instrumentations by workload, and aggregated by pid + // this map is not concurrent safe, so it should be accessed only from the main event loop + detailsByWorkload map[configGroup]map[int]*instrumentationDetails[processGroup, configGroup] + + configUpdates <-chan ConfigUpdate[configGroup] +} + +func NewManager[processGroup ProcessGroup, configGroup ConfigGroup](options ManagerOptions[processGroup, configGroup]) (Manager, error) { + handler := options.Handler + if handler == nil { + return nil, errors.New("handler is required for ebpf instrumentation manager") + } + + if handler.Reporter == nil { + return nil, errors.New("reporter is required for ebpf instrumentation manager") + } + + if handler.ProcessGroupResolver == nil { + return nil, errors.New("details resolver is required for ebpf instrumentation manager") + } + + if handler.ConfigGroupResolver == nil { + return nil, errors.New("config group resolver is required for ebpf instrumentation manager") + } + + if handler.DistributionMatcher == nil { + return nil, errors.New("distribution matcher is required for ebpf instrumentation manager") + } + + if handler.SettingsGetter == nil { + return nil, errors.New("settings getter is required for ebpf instrumentation manager") + } + + if options.ConfigUpdates == nil { + return nil, errors.New("config updates channel is required for ebpf instrumentation manager") + } + + logger := options.Logger + procEvents := make(chan detector.ProcessEvent) + detector, err := detector.NewDetector(procEvents, options.DetectorOptions...) + if err != nil { + return nil, fmt.Errorf("failed to create process detector: %w", err) + } + + return &manager[processGroup, configGroup]{ + procEvents: procEvents, + detector: detector, + handler: handler, + factories: options.Factories, + logger: logger.WithName("ebpf-instrumentation-manager"), + detailsByPid: make(map[int]*instrumentationDetails[processGroup, configGroup]), + detailsByWorkload: map[configGroup]map[int]*instrumentationDetails[processGroup, configGroup]{}, + configUpdates: options.ConfigUpdates, + }, nil +} + +func (m *manager[ProcessGroup, ConfigGroup]) runEventLoop(ctx context.Context) { + // main event loop for handling instrumentations + for { + select { + case <-ctx.Done(): + m.logger.Info("stopping eBPF instrumentation manager") + for pid, details := range m.detailsByPid { + 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 + return + case e := <-m.procEvents: + switch e.EventType { + case detector.ProcessExecEvent: + m.logger.V(1).Info("detected new process", "pid", e.PID, "cmd", e.ExecDetails.CmdLine) + err := m.handleProcessExecEvent(ctx, e) + // ignore the error if no instrumentation factory is found, + // as this is expected for some language and sdk combinations + if err != nil && !errors.Is(err, errNoInstrumentationFactory) { + m.logger.Error(err, "failed to handle process exec event") + } + case detector.ProcessExitEvent: + m.cleanInstrumentation(ctx, e.PID) + } + case configUpdate := <-m.configUpdates: + for configGroup, config := range configUpdate { + err := m.applyInstrumentationConfigurationForSDK(ctx, configGroup, config) + if err != nil { + m.logger.Error(err, "failed to apply instrumentation configuration") + } + } + } + } +} + +func (m *manager[ProcessGroup, ConfigGroup]) Run(ctx context.Context) error { + g, errCtx := errgroup.WithContext(ctx) + + g.Go(func() error { + return m.detector.Run(errCtx) + }) + + g.Go(func() error { + m.runEventLoop(errCtx) + return nil + }) + + err := g.Wait() + return err +} + +func (m *manager[ProcessGroup, ConfigGroup]) cleanInstrumentation(ctx context.Context, pid int) { + details, found := m.detailsByPid[pid] + if !found { + m.logger.V(3).Info("no instrumentation found for exiting pid, nothing to clean", "pid", pid) + return + } + + m.logger.Info("cleaning instrumentation resources", "pid", pid, "process group details", details.pg) + + err := details.inst.Close(ctx) + if err != nil { + m.logger.Error(err, "failed to close instrumentation") + } + + err = m.handler.Reporter.OnExit(ctx, pid, details.pg) + if err != nil { + m.logger.Error(err, "failed to report instrumentation exit") + } + + m.stopTrackInstrumentation(pid) +} + +func (m *manager[ProcessGroup, ConfigGroup]) handleProcessExecEvent(ctx context.Context, e detector.ProcessEvent) error { + if _, found := m.detailsByPid[e.PID]; found { + // this can happen if we have multiple exec events for the same pid (chain loading) + // TODO: better handle this? + // this can be done by first closing the existing instrumentation, + // and then creating a new one + m.logger.Info("received exec event for process id which is already instrumented with ebpf, skipping it", "pid", e.PID) + return nil + } + + pg, err := m.handler.ProcessGroupResolver.Resolve(ctx, e) + if err != nil { + return errors.Join(err, errFailedToGetDetails) + } + + otelDisto, err := m.handler.DistributionMatcher.Distribution(ctx, pg) + if err != nil { + return errors.Join(err, errFailedToGetDistribution) + } + + configGroup, err := m.handler.ConfigGroupResolver.Resolve(ctx, pg, otelDisto) + if err != nil { + return errors.Join(err, errFailedToGetConfigGroup) + } + + factory, found := m.factories[otelDisto] + if !found { + return errNoInstrumentationFactory + } + + // Fetch initial settings for the instrumentation + settings, err := m.handler.SettingsGetter.Settings(ctx, pg, otelDisto) + if err != nil { + // for k8s instrumentation config CR will be queried to get the settings + // we should always have config for this event. + // if missing, it means that either: + // - the config will be generated later due to reconciliation timing in instrumentor + // - just got deleted and the pod (and the process) will go down soon + // TODO: sync reconcilers so inst config is guaranteed be created before the webhook is enabled + // + m.logger.Info("failed to get initial settings for instrumentation", "language", otelDisto.Language, "sdk", otelDisto.OtelSdk, "error", err) + // return nil + } + + inst, err := factory.CreateInstrumentation(ctx, e.PID, settings) + if err != nil { + m.logger.Error(err, "failed to initialize instrumentation", "language", otelDisto.Language, "sdk", otelDisto.OtelSdk) + err = m.handler.Reporter.OnInit(ctx, e.PID, err, pg) + // TODO: should we return here the initialize error? or the handler error? or both? + return err + } + + err = inst.Load(ctx) + // call the reporter regardless of the load result - as we want to report the load status + reporterErr := m.handler.Reporter.OnLoad(ctx, e.PID, err, pg) + if err != nil { + m.logger.Error(err, "failed to load instrumentation", "language", otelDisto.Language, "sdk", otelDisto.OtelSdk) + // TODO: should we return here the load error? or the instance write error? or both? + return err + } + + if reporterErr != nil { + m.logger.Error(reporterErr, "failed to report instrumentation load") + } + + m.startTrackInstrumentation(e.PID, inst, pg, configGroup) + + m.logger.Info("instrumentation loaded", "pid", e.PID, "process group details", pg) + + go func() { + err := inst.Run(ctx) + if err != nil && !errors.Is(err, context.Canceled) { + reporterErr := m.handler.Reporter.OnRun(ctx, e.PID, err, pg) + if reporterErr != nil { + m.logger.Error(reporterErr, "failed to report instrumentation run") + } + m.logger.Error(err, "failed to run instrumentation") + } + }() + + return nil +} + +func (m *manager[ProcessGroup, ConfigGroup]) startTrackInstrumentation(pid int, inst Instrumentation, processGroup ProcessGroup, configGroup ConfigGroup) { + instDetails := &instrumentationDetails[ProcessGroup, ConfigGroup]{ + inst: inst, + pg: processGroup, + cg: configGroup, + } + m.detailsByPid[pid] = instDetails + + if _, found := m.detailsByWorkload[configGroup]; !found { + // first instrumentation for this workload + m.detailsByWorkload[configGroup] = map[int]*instrumentationDetails[ProcessGroup, ConfigGroup]{pid: instDetails} + } else { + m.detailsByWorkload[configGroup][pid] = instDetails + } +} + +func (m *manager[ProcessGroup, ConfigGroup]) stopTrackInstrumentation(pid int) { + details, ok := m.detailsByPid[pid] + if !ok { + return + } + workloadConfigID := details.cg + + delete(m.detailsByPid, pid) + delete(m.detailsByWorkload[workloadConfigID], pid) + + if len(m.detailsByWorkload[workloadConfigID]) == 0 { + delete(m.detailsByWorkload, workloadConfigID) + } +} + +func (m *manager[ProcessGroup, ConfigGroup]) applyInstrumentationConfigurationForSDK(ctx context.Context, configGroup ConfigGroup, config Config) error { + var err error + + configGroupInstrumentations, ok := m.detailsByWorkload[configGroup] + if !ok { + return nil + } + + for _, instDetails := range configGroupInstrumentations { + m.logger.Info("applying configuration to instrumentation", "process group details", instDetails.pg, "configGroup", configGroup) + applyErr := instDetails.inst.ApplyConfig(ctx, config) + err = errors.Join(err, applyErr) + } + return err +} diff --git a/instrumentation/types.go b/instrumentation/types.go new file mode 100644 index 000000000..9b734e592 --- /dev/null +++ b/instrumentation/types.go @@ -0,0 +1,93 @@ +package instrumentation + +import ( + "context" + "fmt" + + "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/instrumentation/detector" +) + +// OtelDistribution is a customized version of an OpenTelemetry component. +// see https://opentelemetry.io/docs/concepts/distributions and https://github.com/odigos-io/odigos/pull/1776#discussion_r1853367917 for more information. +// TODO: This should be moved to a common root package, since it will require a bigger refactor across multiple components, +// we use this local definition for now. +type OtelDistribution struct { + Language common.ProgrammingLanguage + OtelSdk common.OtelSdk +} + +// ProcessGroup represents a group of processes that are managed together by the hosting platform. +// It may include different information depending on the platform (Kubernetes, VM, etc). +// +// For example consider an app which is launched by a bash script, the script launches a python process. +// The process may create different child processes, and the bash script may launch multiple python processes. +// In this case, the process group may include the bash script, the python process, and the child processes. +type ProcessGroup interface { + fmt.Stringer +} + +// ConfigGroup is used to represent a group of instrumented processes which can be configured together. +// For example, a Kubernetes deployment with multiple replicas can be considered a ConfigGroup. +// A config group may include multiple process groups, but there is no assumption on a connection between them. +type ConfigGroup interface { + comparable +} + +// ProcessGroupResolver is used to resolve the process group of a process. +type ProcessGroupResolver[processGroup ProcessGroup] interface { + // Resolve will classify the process into a process group. + // Those process group details may be used for future calls when reporting the status of the instrumentation. + // or for resolving the configuration group of the process. + Resolve(context.Context, detector.ProcessEvent) (processGroup, error) +} + +// ConfigGroupResolver is used to resolve the configuration group of a process. +type ConfigGroupResolver[processGroup ProcessGroup, configGroup ConfigGroup] interface { + // Resolve will classify the process into a configuration group. + // The Otel Distribution is resolved in the time of calling this function, and may be used + // to determine the configuration group. + Resolve(context.Context, processGroup, OtelDistribution) (configGroup, error) +} + +// Reporter is used to report the status of the instrumentation. +// It is called at different stages of the instrumentation lifecycle. +type Reporter[processGroup ProcessGroup] interface { + // OnInit is called when the instrumentation is initialized. + // The error parameter will be nil if the instrumentation was initialized successfully. + OnInit(ctx context.Context, pid int, err error, pg processGroup) error + + // OnLoad is called after an instrumentation is loaded successfully or failed to load. + // The error parameter will be nil if the instrumentation was loaded successfully. + OnLoad(ctx context.Context, pid int, err error, pg processGroup) error + + // OnRun is called after the instrumentation stops running. + // An error may report a fatal error during the instrumentation run, or a closing error + // which happened during the closing of the instrumentation. + OnRun(ctx context.Context, pid int, err error, pg processGroup) error + + // OnExit is called when the instrumented process exits, and the instrumentation has already been stopped. + // For a reported which persists the instrumentation state, this is the time to clean up the state. + OnExit(ctx context.Context, pid int, pg processGroup) error +} + +// DistributionMatcher is used to match a process to an Otel Distribution. +type DistributionMatcher[processGroup ProcessGroup] interface { + // Distribution will match a process to an Otel Distribution. + Distribution(context.Context, processGroup) (OtelDistribution, error) +} + +// SettingsGetter is used to fetch the initial settings of an instrumentation. +type SettingsGetter[processGroup ProcessGroup] interface { + // GetSettings will fetch the initial settings of an instrumentation. + Settings(context.Context, processGroup, OtelDistribution) (Settings, error) +} + +// Handler is used to classify, report and configure instrumentations. +type Handler[processGroup ProcessGroup, configGroup comparable] struct { + ProcessGroupResolver ProcessGroupResolver[processGroup] + ConfigGroupResolver ConfigGroupResolver[processGroup, configGroup] + Reporter Reporter[processGroup] + DistributionMatcher DistributionMatcher[processGroup] + SettingsGetter SettingsGetter[processGroup] +} diff --git a/instrumentor/controllers/instrumentationdevice/manager.go b/instrumentor/controllers/instrumentationdevice/manager.go index e06e5cd57..135357e30 100644 --- a/instrumentor/controllers/instrumentationdevice/manager.go +++ b/instrumentor/controllers/instrumentationdevice/manager.go @@ -163,7 +163,9 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. WebhookManagedBy(mgr). For(&corev1.Pod{}). - WithDefaulter(&PodsWebhook{}). + WithDefaulter(&PodsWebhook{ + Client: mgr.GetClient(), + }). Complete() if err != nil { return err diff --git a/instrumentor/controllers/instrumentationdevice/pods_webhook.go b/instrumentor/controllers/instrumentationdevice/pods_webhook.go index af572cc60..83e9ad042 100644 --- a/instrumentor/controllers/instrumentationdevice/pods_webhook.go +++ b/instrumentor/controllers/instrumentationdevice/pods_webhook.go @@ -5,15 +5,31 @@ import ( "fmt" "strings" - common "github.com/odigos-io/odigos/common" - "github.com/odigos-io/odigos/k8sutils/pkg/consts" + "github.com/odigos-io/odigos/common" + k8sconsts "github.com/odigos-io/odigos/k8sutils/pkg/consts" + containerutils "github.com/odigos-io/odigos/k8sutils/pkg/container" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) -type PodsWebhook struct{} +const otelServiceNameEnvVarName = "OTEL_SERVICE_NAME" +const otelResourceAttributesEnvVarName = "OTEL_RESOURCE_ATTRIBUTES" + +type resourceAttribute struct { + Key attribute.Key + Value string +} + +type PodsWebhook struct { + client.Client +} var _ webhook.CustomDefaulter = &PodsWebhook{} @@ -27,18 +43,40 @@ func (p *PodsWebhook) Default(ctx context.Context, obj runtime.Object) error { pod.Annotations = map[string]string{} } + serviceName, podWorkload := p.getServiceNameForEnv(ctx, pod) + // Inject ODIGOS environment variables into all containers - injectOdigosEnvVars(pod) + injectOdigosEnvVars(pod, podWorkload, serviceName) return nil } -func injectOdigosEnvVars(pod *corev1.Pod) { +// checks for the service name on the annotation, or fallback to the workload name +func (p *PodsWebhook) getServiceNameForEnv(ctx context.Context, pod *corev1.Pod) (*string, *workload.PodWorkload) { + + logger := log.FromContext(ctx) + + podWorkload, err := workload.PodWorkloadObject(ctx, pod) + if err != nil { + logger.Error(err, "failed to extract pod workload details from pod. skipping OTEL_SERVICE_NAME injection") + return nil, nil + } + + 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") + return &podWorkload.Name, podWorkload + } + resolvedServiceName := workload.ExtractServiceNameFromAnnotations(workloadObj.GetAnnotations(), podWorkload.Name) + return &resolvedServiceName, podWorkload +} + +func injectOdigosEnvVars(pod *corev1.Pod, podWorkload *workload.PodWorkload, serviceName *string) { // Common environment variables that do not change across containers commonEnvVars := []corev1.EnvVar{ { - Name: consts.OdigosEnvVarNamespace, + Name: k8sconsts.OdigosEnvVarNamespace, ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "metadata.namespace", @@ -46,7 +84,7 @@ func injectOdigosEnvVars(pod *corev1.Pod) { }, }, { - Name: consts.OdigosEnvVarPodName, + Name: k8sconsts.OdigosEnvVarPodName, ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "metadata.name", @@ -55,11 +93,19 @@ func injectOdigosEnvVars(pod *corev1.Pod) { }, } + var serviceNameEnv *corev1.EnvVar + if serviceName != nil { + serviceNameEnv = &corev1.EnvVar{ + Name: otelServiceNameEnvVarName, + Value: *serviceName, + } + } + for i := range pod.Spec.Containers { container := &pod.Spec.Containers[i] - // Check if the container does NOT have device in conatiner limits. If so, skip the environment injection. - if !hasOdigosInstrumentationInLimits(container.Resources) { + pl, otelsdk, found := containerutils.GetLanguageAndOtelSdk(container) + if !found { continue } @@ -68,10 +114,25 @@ func injectOdigosEnvVars(pod *corev1.Pod) { continue } - container.Env = append(container.Env, append(commonEnvVars, corev1.EnvVar{ - Name: consts.OdigosEnvVarContainerName, + containerNameEnv := corev1.EnvVar{ + Name: k8sconsts.OdigosEnvVarContainerName, Value: container.Name, - })...) + } + + resourceAttributes := getResourceAttributes(podWorkload, container.Name) + resourceAttributesEnvValue := getResourceAttributesEnvVarValue(resourceAttributes) + + container.Env = append(container.Env, append(commonEnvVars, containerNameEnv)...) + + if serviceNameEnv != nil && shouldInjectServiceName(pl, otelsdk) { + if !otelNameExists(container.Env) { + container.Env = append(container.Env, *serviceNameEnv) + } + container.Env = append(container.Env, corev1.EnvVar{ + Name: otelResourceAttributesEnvVarName, + Value: resourceAttributesEnvValue, + }) + } } } @@ -89,12 +150,67 @@ func envVarsExist(containerEnv []corev1.EnvVar, commonEnvVars []corev1.EnvVar) b return false } -// Helper function to check if a container's resource limits have a key starting with the specified namespace -func hasOdigosInstrumentationInLimits(resources corev1.ResourceRequirements) bool { - for resourceName := range resources.Limits { - if strings.HasPrefix(string(resourceName), common.OdigosResourceNamespace) { +func getWorkloadKindAttributeKey(podWorkload *workload.PodWorkload) attribute.Key { + switch podWorkload.Kind { + case workload.WorkloadKindDeployment: + return semconv.K8SDeploymentNameKey + case workload.WorkloadKindStatefulSet: + return semconv.K8SStatefulSetNameKey + case workload.WorkloadKindDaemonSet: + return semconv.K8SDaemonSetNameKey + } + return attribute.Key("") +} + +func getResourceAttributes(podWorkload *workload.PodWorkload, containerName string) []resourceAttribute { + if podWorkload == nil { + return []resourceAttribute{} + } + + workloadKindKey := getWorkloadKindAttributeKey(podWorkload) + return []resourceAttribute{ + { + Key: semconv.K8SContainerNameKey, + Value: containerName, + }, + { + Key: semconv.K8SNamespaceNameKey, + Value: podWorkload.Namespace, + }, + { + Key: workloadKindKey, + Value: podWorkload.Name, + }, + } +} + +func getResourceAttributesEnvVarValue(ra []resourceAttribute) string { + var attrs []string + for _, a := range ra { + attrs = append(attrs, fmt.Sprintf("%s=%s", a.Key, a.Value)) + } + return strings.Join(attrs, ",") +} + +func otelNameExists(containerEnv []corev1.EnvVar) bool { + for _, envVar := range containerEnv { + if envVar.Name == otelServiceNameEnvVarName { return true } } return false } + +// this is used to set the OTEL_SERVICE_NAME for programming languages and otel sdks that requires it. +// eBPF instrumentations sets the service name in code, thus it's not needed here. +// OpAMP sends the service name in the protocol, thus it's not needed here. +// We are only left with OSS Java and Dotnet that requires the OTEL_SERVICE_NAME to be set. +func shouldInjectServiceName(pl common.ProgrammingLanguage, otelsdk common.OtelSdk) bool { + if pl == common.DotNetProgrammingLanguage { + return true + } + if pl == common.JavaProgrammingLanguage && otelsdk.SdkTier == common.CommunityOtelSdkTier { + return true + } + return false +} diff --git a/instrumentor/go.mod b/instrumentor/go.mod index c0cdc3de5..36c993199 100644 --- a/instrumentor/go.mod +++ b/instrumentor/go.mod @@ -10,11 +10,12 @@ require ( github.com/odigos-io/odigos/common v0.0.0 github.com/odigos-io/odigos/k8sutils v0.0.0 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.36.0 + github.com/onsi/gomega v1.36.1 github.com/stretchr/testify v1.10.0 - k8s.io/api v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/client-go v0.31.3 + go.opentelemetry.io/otel v1.29.0 + k8s.io/api v0.32.0 + k8s.io/apimachinery v0.32.0 + k8s.io/client-go v0.32.0 sigs.k8s.io/controller-runtime v0.19.3 ) @@ -31,7 +32,6 @@ require ( github.com/nxadm/tail v1.4.8 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/sdk v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect @@ -40,6 +40,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.65.0 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect ) @@ -51,16 +52,14 @@ require ( github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/zapr v1.3.0 - github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -78,22 +77,21 @@ require ( go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.30.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.35.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + 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 ) diff --git a/instrumentor/go.sum b/instrumentor/go.sum index 1a6679ffb..4a41a86c4 100644 --- a/instrumentor/go.sum +++ b/instrumentor/go.sum @@ -32,13 +32,14 @@ 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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= @@ -53,8 +54,6 @@ github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -75,8 +74,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 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/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= @@ -84,8 +83,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -123,12 +120,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= -github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.36.0 h1:Pb12RlruUtj4XUuPUqeEWc6j5DkVVVA49Uf6YLfC95Y= -github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -195,8 +192,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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= @@ -220,15 +217,15 @@ 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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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= @@ -263,32 +260,29 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +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= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/k8sutils/pkg/container/container.go b/k8sutils/pkg/container/container.go index 60cc911a3..73f7ed26a 100644 --- a/k8sutils/pkg/container/container.go +++ b/k8sutils/pkg/container/container.go @@ -16,7 +16,7 @@ var ( func LanguageSdkFromPodContainer(pod *v1.Pod, containerName string) (common.ProgrammingLanguage, common.OtelSdk, error) { for _, container := range pod.Spec.Containers { if container.Name == containerName { - language, sdk, found := GetLanguageAndOtelSdk(container) + language, sdk, found := GetLanguageAndOtelSdk(&container) if !found { return common.UnknownProgrammingLanguage, common.OtelSdk{}, ErrDeviceNotDetected } @@ -28,7 +28,7 @@ func LanguageSdkFromPodContainer(pod *v1.Pod, containerName string) (common.Prog return common.UnknownProgrammingLanguage, common.OtelSdk{}, ErrContainerNotInPodSpec } -func GetLanguageAndOtelSdk(container v1.Container) (common.ProgrammingLanguage, common.OtelSdk, bool) { +func GetLanguageAndOtelSdk(container *v1.Container) (common.ProgrammingLanguage, common.OtelSdk, bool) { deviceName := podContainerDeviceName(container) if deviceName == nil { return common.UnknownProgrammingLanguage, common.OtelSdk{}, false @@ -38,7 +38,7 @@ func GetLanguageAndOtelSdk(container v1.Container) (common.ProgrammingLanguage, return language, sdk, true } -func podContainerDeviceName(container v1.Container) *string { +func podContainerDeviceName(container *v1.Container) *string { if container.Resources.Limits == nil { return nil } diff --git a/odiglet/Dockerfile b/odiglet/Dockerfile index dc1ff85fc..e3dfa774a 100644 --- a/odiglet/Dockerfile +++ b/odiglet/Dockerfile @@ -82,6 +82,7 @@ COPY common/ common/ COPY k8sutils/ k8sutils/ COPY procdiscovery/ procdiscovery/ COPY opampserver/ opampserver/ +COPY instrumentation/ instrumentation/ WORKDIR /go/src/github.com/odigos-io/odigos/odiglet COPY odiglet/ . diff --git a/odiglet/cmd/main.go b/odiglet/cmd/main.go index 719d11feb..3dd0e0903 100644 --- a/odiglet/cmd/main.go +++ b/odiglet/cmd/main.go @@ -12,6 +12,7 @@ import ( "github.com/kubevirt/device-plugin-manager/pkg/dpm" "github.com/odigos-io/odigos/common" + commonInstrumentation "github.com/odigos-io/odigos/instrumentation" k8senv "github.com/odigos-io/odigos/k8sutils/pkg/env" "github.com/odigos-io/odigos/odiglet/pkg/env" "github.com/odigos-io/odigos/odiglet/pkg/instrumentation" @@ -42,10 +43,14 @@ func odigletInitPhase() { type odiglet struct { clientset *kubernetes.Clientset mgr ctrl.Manager - ebpfManager *ebpf.Manager - configUpdates chan<- ebpf.ConfigUpdate + ebpfManager commonInstrumentation.Manager + configUpdates chan<- commonInstrumentation.ConfigUpdate[ebpf.K8sConfigGroup] } +const ( + configUpdatesBufferSize = 10 +) + func newOdiglet() (*odiglet, error) { // Init Kubernetes API client cfg, err := rest.InClusterConfig() @@ -63,21 +68,22 @@ func newOdiglet() (*odiglet, error) { return nil, fmt.Errorf("Failed to create controller-runtime manager %w", err) } + configUpdates := make(chan commonInstrumentation.ConfigUpdate[ebpf.K8sConfigGroup], configUpdatesBufferSize) ebpfManager, err := ebpf.NewManager( mgr.GetClient(), log.Logger, - map[ebpf.OtelDistribution]ebpf.Factory{ - ebpf.OtelDistribution{ + map[commonInstrumentation.OtelDistribution]commonInstrumentation.Factory{ + commonInstrumentation.OtelDistribution{ Language: common.GoProgrammingLanguage, OtelSdk: common.OtelSdkEbpfCommunity, }: sdks.NewGoInstrumentationFactory(), }, + configUpdates, ) if err != nil { return nil, fmt.Errorf("Failed to create ebpf manager %w", err) } - configUpdates := ebpfManager.ConfigUpdates() err = kube.SetupWithManager(mgr, nil, clientset, configUpdates) if err != nil { return nil, fmt.Errorf("Failed to setup controller-runtime manager %w", err) @@ -123,7 +129,7 @@ func (o *odiglet) run(ctx context.Context) { if err != nil { log.Logger.Error(err, "Failed to run ebpf manager") } - log.Logger.V(0).Info("Ebpf manager exited") + log.Logger.V(0).Info("eBPF manager exited") }() // start OpAmp server diff --git a/odiglet/debug.Dockerfile b/odiglet/debug.Dockerfile index 1ce17a41b..369bde3b8 100644 --- a/odiglet/debug.Dockerfile +++ b/odiglet/debug.Dockerfile @@ -71,6 +71,7 @@ COPY common/ common/ COPY k8sutils/ k8sutils/ COPY procdiscovery/ procdiscovery/ COPY opampserver/ opampserver/ +COPY instrumentation/ instrumentation/ WORKDIR /go/src/github.com/odigos-io/odigos/odiglet COPY odiglet/ . diff --git a/odiglet/go.mod b/odiglet/go.mod index 91c2b0b53..386f47384 100644 --- a/odiglet/go.mod +++ b/odiglet/go.mod @@ -9,59 +9,54 @@ require ( github.com/kubevirt/device-plugin-manager v1.19.5 github.com/moby/sys/mountinfo v0.7.2 github.com/odigos-io/odigos/api v0.0.0 - github.com/odigos-io/odigos/common v1.0.70 + github.com/odigos-io/odigos/common v0.0.0 + github.com/odigos-io/odigos/instrumentation v0.0.0 github.com/odigos-io/odigos/k8sutils v0.0.0 github.com/odigos-io/odigos/opampserver v0.0.0 github.com/odigos-io/odigos/procdiscovery v0.0.0 github.com/odigos-io/opentelemetry-zap-bridge v0.0.5 - github.com/odigos-io/runtime-detector v0.0.3 + github.com/odigos-io/runtime-detector v0.0.4 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/auto v0.19.0-alpha - go.opentelemetry.io/otel v1.32.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 + go.opentelemetry.io/otel v1.33.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 go.uber.org/zap v1.27.0 - golang.org/x/sync v0.10.0 - google.golang.org/grpc v1.68.1 - k8s.io/api v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/client-go v0.31.3 - k8s.io/kubelet v0.31.3 + google.golang.org/grpc v1.69.0 + k8s.io/api v0.32.0 + k8s.io/apimachinery v0.32.0 + k8s.io/client-go v0.32.0 + k8s.io/kubelet v0.32.0 sigs.k8s.io/controller-runtime v0.19.3 ) require ( - github.com/agoda-com/opentelemetry-logs-go v0.4.0 // indirect + github.com/agoda-com/opentelemetry-logs-go v0.5.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cilium/ebpf v0.16.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect - github.com/fatih/color v1.15.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect - github.com/goccy/go-yaml v1.11.3 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/goccy/go-yaml v1.15.9 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.2.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/glog v1.2.3 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // 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 @@ -73,6 +68,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/collector/pdata v1.21.0 // indirect go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 // indirect @@ -80,49 +76,49 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.54.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 // indirect go.opentelemetry.io/otel/log v0.8.0 // indirect - go.opentelemetry.io/otel/metric v1.32.0 // indirect - go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect go.opentelemetry.io/otel/sdk/log v0.8.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect - go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.12.0 // indirect - golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect + golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect golang.org/x/net v0.32.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + golang.org/x/time v0.8.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/protobuf v1.35.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.31.0 // indirect + k8s.io/apiextensions-apiserver v0.31.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect + k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) replace ( github.com/odigos-io/odigos/api => ../api github.com/odigos-io/odigos/common => ../common + github.com/odigos-io/odigos/instrumentation => ../instrumentation github.com/odigos-io/odigos/k8sutils => ../k8sutils github.com/odigos-io/odigos/opampserver => ../opampserver github.com/odigos-io/odigos/procdiscovery => ../procdiscovery diff --git a/odiglet/go.sum b/odiglet/go.sum index 4749c9bb6..db5340018 100644 --- a/odiglet/go.sum +++ b/odiglet/go.sum @@ -28,8 +28,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/agoda-com/opentelemetry-logs-go v0.4.0 h1:XLPOlLHLOND2/VrL69TWSy6blCZnypV37t1MjfILksI= -github.com/agoda-com/opentelemetry-logs-go v0.4.0/go.mod h1:CeDuVaK9yCWN+8UjOW8AciYJE0rl7K/mw4ejBntGYkc= +github.com/agoda-com/opentelemetry-logs-go v0.5.1 h1:6iQrLaY4M0glBZb/xVN559qQutK4V+HJ/mB1cbwaX3c= +github.com/agoda-com/opentelemetry-logs-go v0.5.1/go.mod h1:35B5ypjX5pkVCPJR01i6owJSYWe8cnbWLpEyHgAGD/E= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -52,7 +52,6 @@ github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -62,8 +61,8 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -72,12 +71,10 @@ github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQo github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -96,40 +93,31 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= -github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= +github.com/goccy/go-yaml v1.15.9 h1:500CYajdgpK4Smqrf86u7VMZuj/bFt2ghdff9D/nQYc= +github.com/goccy/go-yaml v1.15.9/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= -github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.3 h1:oDTdz9f5VGVVNGu/Q7UXKWYsD0873HXLHdJUNBsSEKM= +github.com/golang/glog v1.2.3/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -148,8 +136,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -166,8 +154,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -185,8 +173,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= @@ -210,7 +196,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -221,16 +206,9 @@ github.com/kubevirt/device-plugin-manager v1.19.5 h1:nA9rPpQyWBNyrpqaZe2aW4PZfTB github.com/kubevirt/device-plugin-manager v1.19.5/go.mod h1:gPIAptDNdxXBVbq4I2eNPlLy8ZHbd24KpjNLWmpfQuU= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= @@ -256,23 +234,23 @@ github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/odigos-io/opentelemetry-zap-bridge v0.0.5 h1:UDEKtgab42nGVSvA/F3lLKUYFPETtpl7kpxM9BKBlWk= github.com/odigos-io/opentelemetry-zap-bridge v0.0.5/go.mod h1:K98wHhktQ6vCTqeFztcpBDtPTe88vxVgZFlbeGobt24= -github.com/odigos-io/runtime-detector v0.0.3 h1:hUsdSmsPJ4ZrXofEzeNsfiids99RN+O6XcKxsgpZirE= -github.com/odigos-io/runtime-detector v0.0.3/go.mod h1:m8GfdG6pET0K/8aoZwnX+MkYz8oVroKs8HzRnkuJWt4= +github.com/odigos-io/runtime-detector v0.0.4 h1:q4yap5yTApkFFSRdwnU6NZeV+IbzEzWJZv6IEVCVKyE= +github.com/odigos-io/runtime-detector v0.0.4/go.mod h1:m8GfdG6pET0K/8aoZwnX+MkYz8oVroKs8HzRnkuJWt4= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -314,14 +292,9 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= @@ -335,14 +308,16 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/auto v0.19.0-alpha h1:TsJVpNtI0g/N/FT/rv7jaEqctY4kJz5yPfFuol24IS8= go.opentelemetry.io/auto v0.19.0-alpha/go.mod h1:Olz9mFDiXanGQxa4qqWYO6CV5cVFFxC5tPZzu2W3lxk= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/collector/pdata v1.21.0 h1:PG+UbiFMJ35X/WcAR7Rf/PWmWtRdW0aHlOidsR6c5MA= go.opentelemetry.io/collector/pdata v1.21.0/go.mod h1:GKb1/zocKJMvxKbS+sl0W85lxhYBTFJ6h6I1tphVyDU= go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk= go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4= go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= @@ -351,10 +326,10 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7Z go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU= @@ -367,16 +342,16 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsu go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s= go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo= go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -397,15 +372,13 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= -golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -484,8 +457,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= @@ -499,8 +470,8 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -530,8 +501,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -555,10 +524,10 @@ google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBr google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200916143405-f6a2fa72f0c4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E= -google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -567,8 +536,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI= +google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -601,9 +570,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= @@ -614,16 +580,16 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= -k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= -k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= +k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= +k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= @@ -631,22 +597,22 @@ k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= k8s.io/kubelet v0.19.2/go.mod h1:FHHoByVWzh6kNaarXaDPAa751Oz6REcOVRyFT84L1Is= -k8s.io/kubelet v0.31.3 h1:DIXRAmvVGp42mV2vpA1GCLU6oO8who0/vp3Oq6kSpbI= -k8s.io/kubelet v0.31.3/go.mod h1:KSdbEfNy5VzqUlAHlytA/fH12s+sE1u8fb/8JY9sL/8= +k8s.io/kubelet v0.32.0 h1:uLyiKlz195Wo4an/K2tyge8o3QHx0ZkhVN3pevvp59A= +k8s.io/kubelet v0.32.0/go.mod h1:lAwuVZT/Hm7EdLn0jW2D+WdrJoorjJL2rVSdhOFnegw= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/odiglet/pkg/detector/detector.go b/odiglet/pkg/detector/detector.go index d1d9a477c..4646b902a 100644 --- a/odiglet/pkg/detector/detector.go +++ b/odiglet/pkg/detector/detector.go @@ -1,7 +1,6 @@ package detector import ( - "context" "log/slog" "github.com/go-logr/logr" @@ -11,16 +10,7 @@ import ( "github.com/odigos-io/runtime-detector" ) -type ProcessEvent = detector.ProcessEvent - -type Detector = detector.Detector - -const ( - ProcessExecEvent = detector.ProcessExecEvent - ProcessExitEvent = detector.ProcessExitEvent -) - -func NewK8SProcDetector(ctx context.Context, logger logr.Logger, events chan<- ProcessEvent) (*detector.Detector, error) { +func K8sDetectorOptions(logger logr.Logger) []detector.DetectorOption { sLogger := slog.New(logr.ToSlogHandler(logger)) opts := []detector.DetectorOption{ @@ -28,13 +18,8 @@ func NewK8SProcDetector(ctx context.Context, logger logr.Logger, events chan<- P detector.WithEnvironments(relevantEnvVars()...), detector.WithEnvPrefixFilter(consts.OdigosEnvVarPodName), } - detector, err := detector.NewDetector(ctx, events, opts...) - - if err != nil { - return nil, err - } - return detector, nil + return opts } func relevantEnvVars() []string { diff --git a/odiglet/pkg/ebpf/common.go b/odiglet/pkg/ebpf/common.go new file mode 100644 index 000000000..b39a390d7 --- /dev/null +++ b/odiglet/pkg/ebpf/common.go @@ -0,0 +1,48 @@ +package ebpf + +import ( + "github.com/go-logr/logr" + "github.com/odigos-io/odigos/instrumentation" + "github.com/odigos-io/odigos/odiglet/pkg/detector" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// NewManager creates a new instrumentation manager for eBPF which is configured to work with Kubernetes. +func NewManager(client client.Client, logger logr.Logger, factories map[instrumentation.OtelDistribution]instrumentation.Factory, configUpdates <-chan instrumentation.ConfigUpdate[K8sConfigGroup]) (instrumentation.Manager, error) { + managerOpts := instrumentation.ManagerOptions[K8sProcessGroup, K8sConfigGroup]{ + Logger: logger, + Factories: factories, + Handler: newHandler(client), + DetectorOptions: detector.K8sDetectorOptions(logger), + ConfigUpdates: configUpdates, + } + + manager, err := instrumentation.NewManager(managerOpts) + if err != nil { + return nil, err + } + + return manager, nil +} + +func newHandler(client client.Client) *instrumentation.Handler[K8sProcessGroup, K8sConfigGroup] { + reporter := &k8sReporter{ + client: client, + } + processGroupResolver := &k8sDetailsResolver{ + client: client, + } + configGroupResolver := &k8sConfigGroupResolver{} + settingsGetter := &k8sSettingsGetter{ + client: client, + } + distributionMatcher := &podDeviceDistributionMatcher{} + return &instrumentation.Handler[K8sProcessGroup, K8sConfigGroup]{ + ProcessGroupResolver: processGroupResolver, + ConfigGroupResolver: configGroupResolver, + Reporter: reporter, + DistributionMatcher: distributionMatcher, + SettingsGetter: settingsGetter, + } +} diff --git a/odiglet/pkg/ebpf/distribution_matcher.go b/odiglet/pkg/ebpf/distribution_matcher.go new file mode 100644 index 000000000..624e31e63 --- /dev/null +++ b/odiglet/pkg/ebpf/distribution_matcher.go @@ -0,0 +1,23 @@ +package ebpf + +import ( + "context" + "fmt" + + "github.com/odigos-io/odigos/instrumentation" + odgiosK8s "github.com/odigos-io/odigos/k8sutils/pkg/container" +) + +type podDeviceDistributionMatcher struct{} + +func (dm *podDeviceDistributionMatcher) Distribution(ctx context.Context, e K8sProcessGroup) (instrumentation.OtelDistribution, error) { + // get the language and sdk for this process event + // based on the pod spec and the container name from the process event + // TODO: We should have all the required information in the process event + // to determine the language - hence in the future we can improve this + lang, sdk, err := odgiosK8s.LanguageSdkFromPodContainer(e.pod, e.containerName) + if err != nil { + return instrumentation.OtelDistribution{}, fmt.Errorf("failed to get language and sdk: %w", err) + } + return instrumentation.OtelDistribution{Language: lang, OtelSdk: sdk}, nil +} diff --git a/odiglet/pkg/ebpf/manager.go b/odiglet/pkg/ebpf/manager.go deleted file mode 100644 index bc31f596c..000000000 --- a/odiglet/pkg/ebpf/manager.go +++ /dev/null @@ -1,434 +0,0 @@ -package ebpf - -import ( - "context" - "errors" - "fmt" - - "golang.org/x/sync/errgroup" - - "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/k8sutils/pkg/consts" - odgiosK8s "github.com/odigos-io/odigos/k8sutils/pkg/container" - instance "github.com/odigos-io/odigos/k8sutils/pkg/instrumentation_instance" - "github.com/odigos-io/odigos/k8sutils/pkg/workload" - workloadUtils "github.com/odigos-io/odigos/k8sutils/pkg/workload" - "github.com/odigos-io/odigos/odiglet/pkg/detector" - "github.com/odigos-io/odigos/odiglet/pkg/kube/utils" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var ( - ErrNoInstrumentationFactory = errors.New("no ebpf factory found") -) - -const ( - configUpdatesBufferSize = 10 -) - -type errRequiredEnvVarNotFound struct { - envVarName string -} - -func (e *errRequiredEnvVarNotFound) Error() string { - return fmt.Sprintf("required environment variable not found: %s", e.envVarName) -} - -var _ error = &errRequiredEnvVarNotFound{} - -var ( - errContainerNameNotReported = &errRequiredEnvVarNotFound{envVarName: consts.OdigosEnvVarContainerName} - errPodNameNotReported = &errRequiredEnvVarNotFound{envVarName: consts.OdigosEnvVarPodName} - errPodNameSpaceNotReported = &errRequiredEnvVarNotFound{envVarName: consts.OdigosEnvVarNamespace} -) - -type InstrumentationStatusReason string - -const ( - FailedToLoad InstrumentationStatusReason = "FailedToLoad" - FailedToInitialize InstrumentationStatusReason = "FailedToInitialize" - LoadedSuccessfully InstrumentationStatusReason = "LoadedSuccessfully" - FailedToRun InstrumentationStatusReason = "FailedToRun" -) - -type InstrumentationHealth bool - -const ( - InstrumentationHealthy InstrumentationHealth = true - InstrumentationUnhealthy InstrumentationHealth = false -) - -type ConfigUpdate struct { - PodWorkload workload.PodWorkload - Config *odigosv1.InstrumentationConfig -} - -type instrumentationDetails struct { - inst Instrumentation - pod types.NamespacedName - configID workloadConfigID -} - -// workloadConfigID is used to identify a workload and its language for configuration updates -type workloadConfigID struct { - podWorkload workload.PodWorkload - lang common.ProgrammingLanguage -} - -type Manager struct { - // channel for receiving process events, - // used to detect new processes and process exits, and handle their instrumentation accordingly. - procEvents <-chan detector.ProcessEvent - detector *detector.Detector - client client.Client - factories map[OtelDistribution]Factory - logger logr.Logger - - // all the active instrumentations by pid, - // this map is not concurrent safe, so it should be accessed only from the main event loop - detailsByPid map[int]*instrumentationDetails - - // active instrumentations by workload, and aggregated by pid - // this map is not concurrent safe, so it should be accessed only from the main event loop - detailsByWorkload map[workloadConfigID]map[int]*instrumentationDetails - - configUpdates chan ConfigUpdate -} - -func NewManager(client client.Client, logger logr.Logger, factories map[OtelDistribution]Factory) (*Manager, error) { - procEvents := make(chan detector.ProcessEvent) - detector, err := detector.NewK8SProcDetector(context.Background(), logger, procEvents) - if err != nil { - return nil, fmt.Errorf("failed to create process detector: %w", err) - } - - return &Manager{ - procEvents: procEvents, - detector: detector, - client: client, - factories: factories, - logger: logger.WithName("ebpf-instrumentation-manager"), - detailsByPid: make(map[int]*instrumentationDetails), - detailsByWorkload: map[workloadConfigID]map[int]*instrumentationDetails{}, - configUpdates: make(chan ConfigUpdate, configUpdatesBufferSize), - }, nil -} - -// ConfigUpdates returns a channel for receiving configuration updates for instrumentations -// sending on the channel will add an event to the main event loop to apply the configuration. -// closing this channel is in the responsibility of the caller. -func (m *Manager) ConfigUpdates() chan<- ConfigUpdate { - return m.configUpdates -} - -func (m *Manager) runEventLoop(ctx context.Context) { - // main event loop for handling instrumentations - for { - select { - case <-ctx.Done(): - m.logger.Info("stopping Odiglet instrumentation manager") - for pid, details := range m.detailsByPid { - 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 - return - case e := <-m.procEvents: - switch e.EventType { - case detector.ProcessExecEvent: - m.logger.V(1).Info("detected new process", "pid", e.PID, "cmd", e.ExecDetails.CmdLine) - err := m.handleProcessExecEvent(ctx, e) - // ignore the error if no instrumentation factory is found, - // as this is expected for some language and sdk combinations - if err != nil && !errors.Is(err, ErrNoInstrumentationFactory) { - m.logger.Error(err, "failed to handle process exec event") - } - case detector.ProcessExitEvent: - m.cleanInstrumentation(ctx, e.PID) - } - case configUpdate := <-m.configUpdates: - if configUpdate.Config == nil { - m.logger.Info("received nil config update, skipping") - break - } - for _, sdkConfig := range configUpdate.Config.Spec.SdkConfigs { - err := m.applyInstrumentationConfigurationForSDK(ctx, configUpdate.PodWorkload, &sdkConfig) - if err != nil { - m.logger.Error(err, "failed to apply instrumentation configuration") - } - } - } - } -} - -func (m *Manager) Run(ctx context.Context) error { - g, errCtx := errgroup.WithContext(ctx) - - g.Go(func() error { - return m.detector.Run(errCtx) - }) - - g.Go(func() error { - m.runEventLoop(errCtx) - return nil - }) - - err := g.Wait() - return err -} - -func (m *Manager) cleanInstrumentation(ctx context.Context, pid int) { - details, found := m.detailsByPid[pid] - if !found { - m.logger.V(3).Info("no instrumentation found for exiting pid, nothing to clean", "pid", pid) - return - } - - m.logger.Info("cleaning instrumentation resources", "pid", pid) - - err := details.inst.Close(ctx) - if err != nil { - m.logger.Error(err, "failed to close instrumentation") - } - - // remove instrumentation instance - if err = m.client.Delete(ctx, &odigosv1.InstrumentationInstance{ - ObjectMeta: metav1.ObjectMeta{ - Name: instance.InstrumentationInstanceName(details.pod.Name, pid), - Namespace: details.pod.Namespace, - }, - }); err != nil && !apierrors.IsNotFound(err) { - m.logger.Error(err, "error deleting instrumentation instance", "pod", details.pod.Name, "pid", pid) - } - - m.stopTrackInstrumentation(pid) -} - -func (m *Manager) handleProcessExecEvent(ctx context.Context, e detector.ProcessEvent) error { - if _, found := m.detailsByPid[e.PID]; found { - // this can happen if we have multiple exec events for the same pid (chain loading) - // TODO: better handle this? - // this can be done by first closing the existing instrumentation, - // and then creating a new one - m.logger.Info("received exec event for process id which is already instrumented with ebpf, skipping it", "pid", e.PID) - return nil - } - - // get the corresponding pod object for this process event - pod, err := m.podFromProcEvent(ctx, e) - if err != nil { - return fmt.Errorf("failed to get pod from process event: %w", err) - } - - containerName, found := containerNameFromProcEvent(e) - if !found { - return errContainerNameNotReported - } - - // get the language and sdk for this process event - // based on the pod spec and the container name from the process event - // TODO: We should have all the required information in the process event - // to determine the language - hence in the future we can improve this - lang, sdk, err := odgiosK8s.LanguageSdkFromPodContainer(pod, containerName) - if err != nil { - return fmt.Errorf("failed to get language and sdk: %w", err) - } - - factory, found := m.factories[OtelDistribution{Language: lang, OtelSdk: sdk}] - if !found { - return ErrNoInstrumentationFactory - } - - podWorkload, err := workloadUtils.PodWorkloadObjectOrError(ctx, pod) - if err != nil { - return fmt.Errorf("failed to find workload object from pod manifest owners references: %w", err) - } - - // Fetch initial config based on the InstrumentationConfig CR - sdkConfig := m.instrumentationSDKConfig(ctx, podWorkload, lang, types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}) - // we should always have config for this event. - // if missing, it means that either: - // - the config will be generated later due to reconciliation timing in instrumentor - // - just got deleted and the pod (and the process) will go down soon - // TODO: sync reconcilers so inst config is guaranteed be created before the webhook is enabled - // - // if sdkConfig == nil { - // m.Logger.Info("no sdk config found for language", "language", lang, "pod", pod.Name) - // return nil - // } - - settings := Settings{ - // TODO: respect reported name annotation (if present) - to override the service name - // refactor from opAmp code - ServiceName: podWorkload.Name, - // TODO: add container name - ResourceAttributes: utils.GetResourceAttributes(podWorkload, pod.Name), - InitialConfig: sdkConfig, - } - - inst, err := factory.CreateInstrumentation(ctx, e.PID, settings) - if err != nil { - m.logger.Error(err, "failed to initialize instrumentation", "language", lang, "sdk", sdk) - - // write instrumentation instance CR with error status - err = m.updateInstrumentationInstanceStatus(ctx, pod, containerName, podWorkload, e.PID, InstrumentationUnhealthy, FailedToInitialize, err.Error()) - // TODO: should we return here the initialize error? or the instance write error? or both? - return err - } - - err = inst.Load(ctx) - if err != nil { - m.logger.Error(err, "failed to load instrumentation", "language", lang, "sdk", sdk) - - // write instrumentation instance CR with error status - err = m.updateInstrumentationInstanceStatus(ctx, pod, containerName, podWorkload, e.PID, InstrumentationUnhealthy, FailedToLoad, err.Error()) - // TODO: should we return here the load error? or the instance write error? or both? - return err - } - - m.startTrackInstrumentation(e.PID, lang, inst, types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}, *podWorkload) - - m.logger.Info("instrumentation loaded", "pid", e.PID, "pod", pod.Name, "container", containerName, "language", lang, "sdk", sdk) - - // write instrumentation instance CR with success status - msg := fmt.Sprintf("Successfully loaded eBPF probes to pod: %s container: %s", pod.Name, containerName) - err = m.updateInstrumentationInstanceStatus(ctx, pod, containerName, podWorkload, e.PID, InstrumentationHealthy, LoadedSuccessfully, msg) - if err != nil { - m.logger.Error(err, "failed to update instrumentation instance for successful load") - } - - go func() { - err := inst.Run(ctx) - if err != nil && !errors.Is(err, context.Canceled) { - m.logger.Error(err, "failed to run instrumentation") - err = m.updateInstrumentationInstanceStatus(ctx, pod, containerName, podWorkload, e.PID, InstrumentationUnhealthy, FailedToRun, err.Error()) - if err != nil { - m.logger.Error(err, "failed to update instrumentation instance for failed instrumentation run") - } - } - }() - - return nil -} - -func (m *Manager) startTrackInstrumentation(pid int, lang common.ProgrammingLanguage, inst Instrumentation, pod types.NamespacedName, podWorkload workload.PodWorkload) { - workloadConfigID := workloadConfigID{ - podWorkload: podWorkload, - lang: lang, - } - - details := &instrumentationDetails{ - inst: inst, - pod: pod, - configID: workloadConfigID, - } - m.detailsByPid[pid] = details - - if _, found := m.detailsByWorkload[workloadConfigID]; !found { - // first instrumentation for this workload - m.detailsByWorkload[workloadConfigID] = map[int]*instrumentationDetails{pid: details} - } else { - m.detailsByWorkload[workloadConfigID][pid] = details - } -} - -func (m *Manager) stopTrackInstrumentation(pid int) { - details, ok := m.detailsByPid[pid] - if !ok { - return - } - workloadConfigID := details.configID - - delete(m.detailsByPid, pid) - delete(m.detailsByWorkload[workloadConfigID], pid) - - if len(m.detailsByWorkload[workloadConfigID]) == 0 { - delete(m.detailsByWorkload, workloadConfigID) - } -} - -func (m *Manager) instrumentationSDKConfig(ctx context.Context, w *workloadUtils.PodWorkload, lang common.ProgrammingLanguage, podKey types.NamespacedName) *odigosv1.SdkConfig { - instrumentationConfig := odigosv1.InstrumentationConfig{} - instrumentationConfigKey := client.ObjectKey{ - Namespace: w.Namespace, - Name: workloadUtils.CalculateWorkloadRuntimeObjectName(w.Name, w.Kind), - } - if err := m.client.Get(ctx, instrumentationConfigKey, &instrumentationConfig); err != nil { - // this can be valid when the instrumentation config is deleted and current pods will go down soon - m.logger.Error(err, "failed to get initial instrumentation config for instrumented pod", "pod", podKey.Name, "namespace", podKey.Namespace) - return nil - } - for _, config := range instrumentationConfig.Spec.SdkConfigs { - if config.Language == lang { - return &config - } - } - return nil -} - -func (m *Manager) applyInstrumentationConfigurationForSDK(ctx context.Context, podWorkload workload.PodWorkload, sdkConfig *odigosv1.SdkConfig) error { - var err error - - configID := workloadConfigID{ - podWorkload: podWorkload, - lang: sdkConfig.Language, - } - - workloadInstrumentations, ok := m.detailsByWorkload[configID] - if !ok { - return nil - } - - for _, instDetails := range workloadInstrumentations { - m.logger.Info("applying configuration to instrumentation", "podWorkload", podWorkload, "pod", instDetails.pod, "language", sdkConfig.Language) - applyErr := instDetails.inst.ApplyConfig(ctx, sdkConfig) - err = errors.Join(err, applyErr) - } - return err -} - -func (m *Manager) updateInstrumentationInstanceStatus(ctx context.Context, pod *corev1.Pod, containerName string, w *workloadUtils.PodWorkload, pid int, health InstrumentationHealth, reason InstrumentationStatusReason, msg string) error { - instrumentedAppName := workloadUtils.CalculateWorkloadRuntimeObjectName(w.Name, w.Kind) - healthy := bool(health) - return instance.UpdateInstrumentationInstanceStatus(ctx, pod, containerName, m.client, instrumentedAppName, pid, m.client.Scheme(), - instance.WithHealthy(&healthy, string(reason), &msg), - ) -} - -func (m *Manager) podFromProcEvent(ctx context.Context, event detector.ProcessEvent) (*corev1.Pod, error) { - eventEnvs := event.ExecDetails.Environments - - podName, ok := eventEnvs[consts.OdigosEnvVarPodName] - if !ok { - return nil, errPodNameNotReported - } - - podNamespace, ok := eventEnvs[consts.OdigosEnvVarNamespace] - if !ok { - return nil, errPodNameSpaceNotReported - } - - pod := corev1.Pod{} - err := m.client.Get(ctx, client.ObjectKey{Namespace: podNamespace, Name: podName}, &pod) - if err != nil { - return nil, fmt.Errorf("error fetching pod object: %w", err) - } - - return &pod, nil -} - -func containerNameFromProcEvent(event detector.ProcessEvent) (string, bool) { - containerName, ok := event.ExecDetails.Environments[consts.OdigosEnvVarContainerName] - return containerName, ok -} diff --git a/odiglet/pkg/ebpf/reporter.go b/odiglet/pkg/ebpf/reporter.go new file mode 100644 index 000000000..ec52647e6 --- /dev/null +++ b/odiglet/pkg/ebpf/reporter.go @@ -0,0 +1,123 @@ +package ebpf + +import ( + "context" + "fmt" + + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/instrumentation" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" + instance "github.com/odigos-io/odigos/k8sutils/pkg/instrumentation_instance" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + 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" +) + +type K8sProcessGroup struct { + pod *corev1.Pod + containerName string + pw *workload.PodWorkload +} + +func (kd K8sProcessGroup) String() string { + return fmt.Sprintf("Pod: %s.%s, Container: %s, Workload: %s", + kd.pod.Name, kd.pod.Namespace, + kd.containerName, + workload.CalculateWorkloadRuntimeObjectName(kd.pw.Name, kd.pw.Kind), + ) +} + +var _ instrumentation.ProcessGroup = K8sProcessGroup{} + +type k8sReporter struct { + client client.Client +} + +type K8sConfigGroup struct { + Pw workload.PodWorkload + Lang common.ProgrammingLanguage +} + +var _ instrumentation.Reporter[K8sProcessGroup] = &k8sReporter{} + +type errRequiredEnvVarNotFound struct { + envVarName string +} + +func (e *errRequiredEnvVarNotFound) Error() string { + return fmt.Sprintf("required environment variable not found: %s", e.envVarName) +} + +var _ error = &errRequiredEnvVarNotFound{} + +var ( + errContainerNameNotReported = &errRequiredEnvVarNotFound{envVarName: consts.OdigosEnvVarContainerName} + errPodNameNotReported = &errRequiredEnvVarNotFound{envVarName: consts.OdigosEnvVarPodName} + errPodNameSpaceNotReported = &errRequiredEnvVarNotFound{envVarName: consts.OdigosEnvVarNamespace} +) + +type InstrumentationStatusReason string + +const ( + FailedToLoad InstrumentationStatusReason = "FailedToLoad" + FailedToInitialize InstrumentationStatusReason = "FailedToInitialize" + LoadedSuccessfully InstrumentationStatusReason = "LoadedSuccessfully" + FailedToRun InstrumentationStatusReason = "FailedToRun" +) + +type InstrumentationHealth bool + +const ( + InstrumentationHealthy InstrumentationHealth = true + InstrumentationUnhealthy InstrumentationHealth = false +) + +func (r *k8sReporter) OnInit(ctx context.Context, pid int, err error, e K8sProcessGroup) error { + if err == nil { + // currently we don't report on successful initialization + return nil + } + + return r.updateInstrumentationInstanceStatus(ctx, e, pid, InstrumentationUnhealthy, FailedToInitialize, err.Error()) +} + +func (r *k8sReporter) OnLoad(ctx context.Context, pid int, err error, e K8sProcessGroup) error { + if err != nil { + return r.updateInstrumentationInstanceStatus(ctx, e, pid, InstrumentationUnhealthy, FailedToLoad, err.Error()) + } + + msg := fmt.Sprintf("Successfully loaded eBPF probes to pod: %s container: %s", e.pod.Name, e.containerName) + return r.updateInstrumentationInstanceStatus(ctx, e, pid, InstrumentationHealthy, LoadedSuccessfully, msg) +} + +func (r *k8sReporter) OnRun(ctx context.Context, pid int, err error, e K8sProcessGroup) error { + if err == nil { + // finished running successfully + return nil + } + + return r.updateInstrumentationInstanceStatus(ctx, e, pid, InstrumentationUnhealthy, FailedToRun, err.Error()) +} + +func (r *k8sReporter) OnExit(ctx context.Context, pid int, e K8sProcessGroup) error { + if err := r.client.Delete(ctx, &odigosv1.InstrumentationInstance{ + ObjectMeta: metav1.ObjectMeta{ + Name: instance.InstrumentationInstanceName(e.pod.Name, pid), + Namespace: e.pod.Namespace, + }, + }); err != nil && !apierrors.IsNotFound(err) { + return fmt.Errorf("error deleting instrumentation instance for pod %s pid %d: %w", e.pod.Name, pid, err) + } + return nil +} + +func (r *k8sReporter) updateInstrumentationInstanceStatus(ctx context.Context, ke K8sProcessGroup, pid int, health InstrumentationHealth, reason InstrumentationStatusReason, msg string) error { + instrumentedAppName := workload.CalculateWorkloadRuntimeObjectName(ke.pw.Name, ke.pw.Kind) + healthy := bool(health) + return instance.UpdateInstrumentationInstanceStatus(ctx, ke.pod, ke.containerName, r.client, instrumentedAppName, pid, r.client.Scheme(), + instance.WithHealthy(&healthy, string(reason), &msg), + ) +} diff --git a/odiglet/pkg/ebpf/resolvers.go b/odiglet/pkg/ebpf/resolvers.go new file mode 100644 index 000000000..efb654f9e --- /dev/null +++ b/odiglet/pkg/ebpf/resolvers.go @@ -0,0 +1,79 @@ +package ebpf + +import ( + "context" + "fmt" + + "github.com/odigos-io/odigos/instrumentation" + "github.com/odigos-io/odigos/instrumentation/detector" + "github.com/odigos-io/odigos/k8sutils/pkg/consts" + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type k8sDetailsResolver struct { + client client.Client +} + +func (dr *k8sDetailsResolver) Resolve(ctx context.Context, event detector.ProcessEvent) (K8sProcessGroup, error) { + pod, err := dr.podFromProcEvent(ctx, event) + if err != nil { + return K8sProcessGroup{}, err + } + + containerName, found := containerNameFromProcEvent(event) + if !found { + return K8sProcessGroup{}, errContainerNameNotReported + } + + podWorkload, err := workload.PodWorkloadObjectOrError(ctx, pod) + if err != nil { + return K8sProcessGroup{}, fmt.Errorf("failed to find workload object from pod manifest owners references: %w", err) + } + + return K8sProcessGroup{ + pod: pod, + containerName: containerName, + pw: podWorkload, + }, nil +} + +func (dr *k8sDetailsResolver) podFromProcEvent(ctx context.Context, e detector.ProcessEvent) (*corev1.Pod, error) { + eventEnvs := e.ExecDetails.Environments + + podName, ok := eventEnvs[consts.OdigosEnvVarPodName] + if !ok { + return nil, errPodNameNotReported + } + + podNamespace, ok := eventEnvs[consts.OdigosEnvVarNamespace] + if !ok { + return nil, errPodNameSpaceNotReported + } + + pod := corev1.Pod{} + err := dr.client.Get(ctx, client.ObjectKey{Namespace: podNamespace, Name: podName}, &pod) + if err != nil { + return nil, fmt.Errorf("error fetching pod object: %w", err) + } + + return &pod, nil +} + +func containerNameFromProcEvent(event detector.ProcessEvent) (string, bool) { + containerName, ok := event.ExecDetails.Environments[consts.OdigosEnvVarContainerName] + return containerName, ok +} + +type k8sConfigGroupResolver struct{} + +func (cr *k8sConfigGroupResolver) Resolve(ctx context.Context, d K8sProcessGroup, dist instrumentation.OtelDistribution) (K8sConfigGroup, error) { + if d.pw == nil { + return K8sConfigGroup{}, fmt.Errorf("podWorkload is not provided, cannot resolve config group") + } + return K8sConfigGroup{ + Pw: *d.pw, + Lang: dist.Language, + }, nil +} diff --git a/odiglet/pkg/ebpf/sdks/go.go b/odiglet/pkg/ebpf/sdks/go.go index e22a576c7..72b0aa0a1 100644 --- a/odiglet/pkg/ebpf/sdks/go.go +++ b/odiglet/pkg/ebpf/sdks/go.go @@ -6,6 +6,7 @@ import ( odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/common" + "github.com/odigos-io/odigos/instrumentation" "github.com/odigos-io/odigos/odiglet/pkg/ebpf" "github.com/odigos-io/odigos/odiglet/pkg/env" @@ -26,11 +27,11 @@ var _ auto.ConfigProvider = (*ebpf.ConfigProvider[auto.InstrumentationConfig])(n type GoInstrumentationFactory struct { } -func NewGoInstrumentationFactory() ebpf.Factory { +func NewGoInstrumentationFactory() instrumentation.Factory { return &GoInstrumentationFactory{} } -func (g *GoInstrumentationFactory) CreateInstrumentation(ctx context.Context, pid int, settings ebpf.Settings) (ebpf.Instrumentation, error) { +func (g *GoInstrumentationFactory) CreateInstrumentation(ctx context.Context, pid int, settings instrumentation.Settings) (instrumentation.Instrumentation, error) { defaultExporter, err := otlptracegrpc.New( ctx, otlptracegrpc.WithInsecure(), @@ -40,7 +41,12 @@ func (g *GoInstrumentationFactory) CreateInstrumentation(ctx context.Context, pi return nil, fmt.Errorf("failed to create exporter: %w", err) } - cp := ebpf.NewConfigProvider(convertToGoInstrumentationConfig(settings.InitialConfig)) + initialConfig, err := convertToGoInstrumentationConfig(settings.InitialConfig) + if err != nil { + return nil, fmt.Errorf("invalid initial config type, expected *odigosv1.SdkConfig, got %T", settings.InitialConfig) + } + + cp := ebpf.NewConfigProvider(initialConfig) inst, err := auto.NewInstrumentation( ctx, @@ -72,18 +78,27 @@ func (g *GoOtelEbpfSdk) Close(_ context.Context) error { return g.inst.Close() } -func (g *GoOtelEbpfSdk) ApplyConfig(ctx context.Context, sdkConfig *odigosv1.SdkConfig) error { - return g.cp.SendConfig(ctx, convertToGoInstrumentationConfig(sdkConfig)) +func (g *GoOtelEbpfSdk) ApplyConfig(ctx context.Context, sdkConfig instrumentation.Config) error { + updatedConfig, err := convertToGoInstrumentationConfig(sdkConfig) + if err != nil { + return err + } + + return g.cp.SendConfig(ctx, updatedConfig) } -func convertToGoInstrumentationConfig(sdkConfig *odigosv1.SdkConfig) auto.InstrumentationConfig { +func convertToGoInstrumentationConfig(sdkConfig instrumentation.Config) (auto.InstrumentationConfig, error) { + initialConfig, ok := sdkConfig.(*odigosv1.SdkConfig) + if !ok { + return auto.InstrumentationConfig{}, fmt.Errorf("invalid initial config type, expected *odigosv1.SdkConfig, got %T", sdkConfig) + } ic := auto.InstrumentationConfig{} if sdkConfig == nil { log.Logger.V(0).Info("No SDK config provided for Go instrumentation, using default") - return ic + return ic, nil } ic.InstrumentationLibraryConfigs = make(map[auto.InstrumentationLibraryID]auto.InstrumentationLibrary) - for _, ilc := range sdkConfig.InstrumentationLibraryConfigs { + for _, ilc := range initialConfig.InstrumentationLibraryConfigs { libID := auto.InstrumentationLibraryID{ InstrumentedPkg: ilc.InstrumentationLibraryId.InstrumentationLibraryName, SpanKind: common.SpanKindOdigosToOtel(ilc.InstrumentationLibraryId.SpanKind), @@ -99,5 +114,5 @@ func convertToGoInstrumentationConfig(sdkConfig *odigosv1.SdkConfig) auto.Instru // TODO: take sampling config from the CR ic.Sampler = auto.DefaultSampler() - return ic + return ic, nil } diff --git a/odiglet/pkg/ebpf/settings_getter.go b/odiglet/pkg/ebpf/settings_getter.go new file mode 100644 index 000000000..40c420006 --- /dev/null +++ b/odiglet/pkg/ebpf/settings_getter.go @@ -0,0 +1,54 @@ +package ebpf + +import ( + "context" + "fmt" + + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + "github.com/odigos-io/odigos/instrumentation" + workload "github.com/odigos-io/odigos/k8sutils/pkg/workload" + "github.com/odigos-io/odigos/odiglet/pkg/kube/utils" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type k8sSettingsGetter struct { + client client.Client +} + +var _ instrumentation.SettingsGetter[K8sProcessGroup] = &k8sSettingsGetter{} + +func (ksg *k8sSettingsGetter) Settings(ctx context.Context, kd K8sProcessGroup, dist instrumentation.OtelDistribution) (instrumentation.Settings, error) { + sdkConfig, serviceName, err := ksg.instrumentationSDKConfig(ctx, kd, dist) + if err != nil { + return instrumentation.Settings{}, err + } + + OtelServiceName := serviceName + if serviceName == "" { + OtelServiceName = kd.pw.Name + } + + return instrumentation.Settings{ + ServiceName: OtelServiceName, + ResourceAttributes: utils.GetResourceAttributes(kd.pw, kd.pod.Name), + InitialConfig: sdkConfig, + }, nil +} + +func (ksg *k8sSettingsGetter) instrumentationSDKConfig(ctx context.Context, kd K8sProcessGroup, dist instrumentation.OtelDistribution) (*odigosv1.SdkConfig, string, error) { + instrumentationConfig := odigosv1.InstrumentationConfig{} + instrumentationConfigKey := client.ObjectKey{ + Namespace: kd.pw.Namespace, + Name: workload.CalculateWorkloadRuntimeObjectName(kd.pw.Name, kd.pw.Kind), + } + if err := ksg.client.Get(ctx, instrumentationConfigKey, &instrumentationConfig); err != nil { + // this can be valid when the instrumentation config is deleted and current pods will go down soon + return nil, "", err + } + for _, config := range instrumentationConfig.Spec.SdkConfigs { + if config.Language == dist.Language { + return &config, instrumentationConfig.Spec.ServiceName, nil + } + } + return nil, "", fmt.Errorf("no sdk config found for language %s", dist.Language) +} diff --git a/odiglet/pkg/kube/instrumentation_ebpf/instrumentationconfig.go b/odiglet/pkg/kube/instrumentation_ebpf/instrumentationconfig.go index e067d9f2c..ba900f630 100644 --- a/odiglet/pkg/kube/instrumentation_ebpf/instrumentationconfig.go +++ b/odiglet/pkg/kube/instrumentation_ebpf/instrumentationconfig.go @@ -5,6 +5,7 @@ import ( "errors" "time" + "github.com/odigos-io/odigos/instrumentation" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/k8sutils/pkg/workload" "github.com/odigos-io/odigos/odiglet/pkg/ebpf" @@ -18,7 +19,7 @@ type InstrumentationConfigReconciler struct { client.Client Scheme *runtime.Scheme Directors ebpf.DirectorsMap - ConfigUpdates chan<- ebpf.ConfigUpdate + ConfigUpdates chan<- instrumentation.ConfigUpdate[ebpf.K8sConfigGroup] } var ( @@ -62,15 +63,24 @@ func (i *InstrumentationConfigReconciler) Reconcile(ctx context.Context, req ctr } if i.ConfigUpdates != nil { + if len(instrumentationConfig.Spec.SdkConfigs) == 0 { + return ctrl.Result{}, nil + } + // send a config update request for all the instrumentation which are part of the workload. // if the config request is sent, the configuration updates will occur asynchronously. ctx, cancel := context.WithTimeout(ctx, configUpdateTimeout) defer cancel() + configUpdate := instrumentation.ConfigUpdate[ebpf.K8sConfigGroup]{} + for _, sdkConfig := range instrumentationConfig.Spec.SdkConfigs { + cg := ebpf.K8sConfigGroup{Pw: podWorkload, Lang: sdkConfig.Language} + currentConfig := sdkConfig + configUpdate[cg] = ¤tConfig + } + select { - case i.ConfigUpdates <- ebpf.ConfigUpdate{ - PodWorkload: podWorkload, - Config: instrumentationConfig}: + case i.ConfigUpdates <-configUpdate: return ctrl.Result{}, nil case <-ctx.Done(): if ctx.Err() == context.DeadlineExceeded { diff --git a/odiglet/pkg/kube/instrumentation_ebpf/manager.go b/odiglet/pkg/kube/instrumentation_ebpf/manager.go index 57c95f16b..05467692c 100644 --- a/odiglet/pkg/kube/instrumentation_ebpf/manager.go +++ b/odiglet/pkg/kube/instrumentation_ebpf/manager.go @@ -3,6 +3,7 @@ package instrumentation_ebpf import ( "sigs.k8s.io/controller-runtime/pkg/predicate" + "github.com/odigos-io/odigos/instrumentation" odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" odigospredicate "github.com/odigos-io/odigos/k8sutils/pkg/predicate" "github.com/odigos-io/odigos/odiglet/pkg/ebpf" @@ -12,7 +13,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/builder" ) -func SetupWithManager(mgr ctrl.Manager, ebpfDirectors ebpf.DirectorsMap, configUpdates chan<- ebpf.ConfigUpdate) error { +func SetupWithManager(mgr ctrl.Manager, ebpfDirectors ebpf.DirectorsMap, configUpdates chan<- instrumentation.ConfigUpdate[ebpf.K8sConfigGroup]) error { log.Logger.V(0).Info("Starting reconcileres for ebpf instrumentation") var err error diff --git a/odiglet/pkg/kube/instrumentation_ebpf/shared.go b/odiglet/pkg/kube/instrumentation_ebpf/shared.go index 14b2d3c94..71f95dcc7 100644 --- a/odiglet/pkg/kube/instrumentation_ebpf/shared.go +++ b/odiglet/pkg/kube/instrumentation_ebpf/shared.go @@ -36,7 +36,7 @@ func instrumentPodWithEbpf(ctx context.Context, pod *corev1.Pod, directors ebpf. continue } - language, sdk, found := odgiosK8s.GetLanguageAndOtelSdk(container) + language, sdk, found := odgiosK8s.GetLanguageAndOtelSdk(&container) if !found { continue } diff --git a/odiglet/pkg/kube/manager.go b/odiglet/pkg/kube/manager.go index 530a48c2a..3b1b911a9 100644 --- a/odiglet/pkg/kube/manager.go +++ b/odiglet/pkg/kube/manager.go @@ -2,6 +2,7 @@ package kube import ( "github.com/odigos-io/odigos/common/consts" + "github.com/odigos-io/odigos/instrumentation" "k8s.io/apimachinery/pkg/labels" "github.com/odigos-io/odigos/odiglet/pkg/ebpf" @@ -59,7 +60,7 @@ func CreateManager() (ctrl.Manager, error) { }) } -func SetupWithManager(mgr ctrl.Manager, ebpfDirectors ebpf.DirectorsMap, clientset *kubernetes.Clientset, configUpdates chan<- ebpf.ConfigUpdate) error { +func SetupWithManager(mgr ctrl.Manager, ebpfDirectors ebpf.DirectorsMap, clientset *kubernetes.Clientset, configUpdates chan<- instrumentation.ConfigUpdate[ebpf.K8sConfigGroup]) error { err := runtime_details.SetupWithManager(mgr, clientset) if err != nil { return err diff --git a/procdiscovery/go.mod b/procdiscovery/go.mod index 513daf78f..09d39486a 100644 --- a/procdiscovery/go.mod +++ b/procdiscovery/go.mod @@ -4,7 +4,7 @@ go 1.23.0 require ( github.com/hashicorp/go-version v1.7.0 - github.com/odigos-io/odigos/common v1.0.48 + github.com/odigos-io/odigos/common v0.0.0 ) require ( diff --git a/scheduler/controllers/nodecollectorsgroup/common.go b/scheduler/controllers/nodecollectorsgroup/common.go index 28156ce9f..18a054fb6 100644 --- a/scheduler/controllers/nodecollectorsgroup/common.go +++ b/scheduler/controllers/nodecollectorsgroup/common.go @@ -34,9 +34,14 @@ const ( // allowing the memory limit to be slightly above the memory request can help in reducing the chances of OOMs in edge cases. // instead of having the process killed, it can use extra memory available on the node without allocating it preemptively. memoryLimitAboveRequestFactor = 2.0 + + // the default CPU request in millicores + defaultRequestCPUm = 250 + // the default CPU limit in millicores + defaultLimitCPUm = 500 ) -func getMemorySettings(odigosConfig common.OdigosConfiguration) odigosv1.CollectorsGroupResourcesSettings { +func getResourceSettings(odigosConfig common.OdigosConfiguration) odigosv1.CollectorsGroupResourcesSettings { // memory request is expensive on daemonsets since it will consume this memory // on each node in the cluster. setting to 256, but allowing memory to spike higher // to consume more available memory on the node. @@ -78,12 +83,23 @@ func getMemorySettings(odigosConfig common.OdigosConfiguration) odigosv1.Collect gomemlimitMiB = nodeCollectorConfig.GoMemLimitMib } + cpuRequestm := defaultRequestCPUm + if nodeCollectorConfig != nil && nodeCollectorConfig.RequestCPUm > 0 { + cpuRequestm = nodeCollectorConfig.RequestCPUm + } + cpuLimitm := defaultLimitCPUm + if nodeCollectorConfig != nil && nodeCollectorConfig.LimitCPUm > 0 { + cpuLimitm = nodeCollectorConfig.LimitCPUm + } + return odigosv1.CollectorsGroupResourcesSettings{ MemoryRequestMiB: memoryRequestMiB, MemoryLimitMiB: memoryLimitMiB, MemoryLimiterLimitMiB: memoryLimiterLimitMiB, MemoryLimiterSpikeLimitMiB: memoryLimiterSpikeLimitMiB, GomemlimitMiB: gomemlimitMiB, + CpuRequestMillicores: cpuRequestm, + CpuLimitMillicores: cpuLimitm, } } @@ -106,7 +122,7 @@ func newNodeCollectorGroup(odigosConfig common.OdigosConfiguration) *odigosv1.Co Spec: odigosv1.CollectorsGroupSpec{ Role: odigosv1.CollectorsGroupRoleNodeCollector, CollectorOwnMetricsPort: ownMetricsPort, - ResourcesSettings: getMemorySettings(odigosConfig), + ResourcesSettings: getResourceSettings(odigosConfig), }, } } diff --git a/scheduler/go.mod b/scheduler/go.mod index 24b0507cb..f1c4c8f4e 100644 --- a/scheduler/go.mod +++ b/scheduler/go.mod @@ -9,10 +9,10 @@ require ( github.com/odigos-io/odigos/k8sutils v0.0.0 github.com/odigos-io/opentelemetry-zap-bridge v0.0.5 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.36.0 - k8s.io/api v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/client-go v0.31.3 + github.com/onsi/gomega v1.36.1 + k8s.io/api v0.32.0 + k8s.io/apimachinery v0.32.0 + k8s.io/client-go v0.32.0 sigs.k8s.io/controller-runtime v0.19.3 ) @@ -29,12 +29,11 @@ require ( github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/goccy/go-yaml v1.11.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect @@ -42,7 +41,6 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -68,27 +66,27 @@ require ( go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.30.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.7.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // 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.35.1 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + 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 // indirect ) diff --git a/scheduler/go.sum b/scheduler/go.sum index 59200f5eb..4781eb035 100644 --- a/scheduler/go.sum +++ b/scheduler/go.sum @@ -32,13 +32,14 @@ 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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= @@ -53,8 +54,6 @@ github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -75,8 +74,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 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/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= @@ -84,8 +83,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -123,12 +120,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= -github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.36.0 h1:Pb12RlruUtj4XUuPUqeEWc6j5DkVVVA49Uf6YLfC95Y= -github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -195,8 +192,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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= @@ -220,15 +217,15 @@ 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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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= @@ -263,32 +260,29 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +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= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/scripts/release-charts.sh b/scripts/release-charts.sh index 28a4eb23f..eec314841 100755 --- a/scripts/release-charts.sh +++ b/scripts/release-charts.sh @@ -30,8 +30,6 @@ if [[ $(git diff -- ${CHARTDIRS[*]} | wc -c) -ne 0 ]]; then exit 1 fi -# Ignore errors because it will mostly always error locally -helm repo add odigos https://odigos-io.github.io/odigos-charts 2> /dev/null || true git worktree add $TMPDIR gh-pages -f # Update index with new packages diff --git a/tests/common/assert/odigos-installed.yaml b/tests/common/assert/odigos-installed.yaml index 15ccf986c..0eed841e5 100644 --- a/tests/common/assert/odigos-installed.yaml +++ b/tests/common/assert/odigos-installed.yaml @@ -92,7 +92,7 @@ spec: port: 3000 protocol: TCP targetPort: 3000 - - name: beta-ui + - name: legacy-ui port: 3001 protocol: TCP targetPort: 3001 diff --git a/tests/common/assert/pipeline-ready.yaml b/tests/common/assert/pipeline-ready.yaml index 2c52b1386..6c023c690 100644 --- a/tests/common/assert/pipeline-ready.yaml +++ b/tests/common/assert/pipeline-ready.yaml @@ -55,12 +55,20 @@ spec: fieldPath: metadata.name - name: GOMEMLIMIT (value != null): true + - name: GOMAXPROCS + valueFrom: + resourceFieldRef: + containerName: gateway + divisor: "0" + resource: limits.cpu name: gateway resources: requests: (memory != null): true + (cpu != null): true limits: (memory != null): true + (cpu != null): true volumeMounts: - mountPath: /conf name: collector-conf diff --git a/tests/common/assert_pipeline_pods_ready.sh b/tests/common/assert_pipeline_pods_ready.sh new file mode 100755 index 000000000..43024745e --- /dev/null +++ b/tests/common/assert_pipeline_pods_ready.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +DEFAULT_NAMESPACE="odigos-test" +DEFAULT_TIMEOUT="120s" +CHECK_INTERVAL=5 + +NAMESPACE=${1:-$DEFAULT_NAMESPACE} +TIMEOUT=${2:-$DEFAULT_TIMEOUT} + +# Define expected labels for pods (adjust as needed) +EXPECTED_LABELS=( + "odigos.io/collector-role=NODE_COLLECTOR" # For odigos-data-collection pods + "odigos.io/collector-role=CLUSTER_GATEWAY" # For odigos-gateway pods + "app.kubernetes.io/name=odigos-autoscaler" + "app.kubernetes.io/name=odigos-instrumentor" + "app.kubernetes.io/name=odigos-scheduler" + "app=odigos-ui" +) + +echo "Waiting for all expected pods to be ready in namespace '$NAMESPACE' with timeout of $TIMEOUT..." + +for label in "${EXPECTED_LABELS[@]}"; do + echo "Waiting for pods with label: $label..." + # Wait for pods to exist before using kubectl wait + EXISTS=false + while [[ "$EXISTS" == "false" ]]; do + POD_COUNT=$(kubectl get pods -l "$label" -n "$NAMESPACE" --no-headers 2>/dev/null | wc -l) + if [[ "$POD_COUNT" -gt 0 ]]; then + EXISTS=true + echo "Found $POD_COUNT pod(s) with label '$label'. Proceeding to wait for readiness..." + else + echo "No pods found with label '$label'. Checking again in $CHECK_INTERVAL seconds..." + sleep $CHECK_INTERVAL + fi + done + + # Use `kubectl wait` to check all pods matching the label + kubectl wait --for=condition=Ready pods -l "$label" -n "$NAMESPACE" --timeout="$TIMEOUT" + if [[ $? -ne 0 ]]; then + echo "Pods with label '$label' did not become ready within $TIMEOUT in namespace '$NAMESPACE'" + exit 1 + fi +done + +echo "All expected pods are ready!" diff --git a/tests/e2e/cli-upgrade/chainsaw-test.yaml b/tests/e2e/cli-upgrade/chainsaw-test.yaml index b9907593f..e4171fe75 100644 --- a/tests/e2e/cli-upgrade/chainsaw-test.yaml +++ b/tests/e2e/cli-upgrade/chainsaw-test.yaml @@ -103,10 +103,15 @@ spec: try: - apply: file: ../../common/apply/add-tempo-traces-destination.yaml - - name: Odigos pipeline ready + - name: Odigos pipeline pods ready + # We make sure that the pipeline pods are ready before proceeding with the next steps + # This is intentionally different from pipeline-ready.yaml, which checks for the pipeline CRDs + # When adding a feature related to the pipeline, if we would use the same assert before the upgrade, the test would fail. + # Since the version installed here is latest official one. try: - - assert: - file: ../../common/assert/pipeline-ready.yaml + - script: + content: ../../common/assert_pipeline_pods_ready.sh + timeout: 60s - name: Simple-demo instrumented after destination added try: - assert: diff --git a/tests/e2e/ui/chainsaw-test.yaml b/tests/e2e/ui/chainsaw-test.yaml index 87ca791e0..f4f293dab 100644 --- a/tests/e2e/ui/chainsaw-test.yaml +++ b/tests/e2e/ui/chainsaw-test.yaml @@ -41,7 +41,7 @@ spec: - script: timeout: 10s content: | - nohup ../../../cli/odigos ui --beta > odigos-ui.log 2>&1 & + nohup ../../../cli/odigos ui > odigos-ui.log 2>&1 & sleep 5 - name: Wait for UI diff --git a/tests/e2e/workload-lifecycle/03-assert-action-created.yaml b/tests/e2e/workload-lifecycle/03-assert-action-created.yaml index 8729b705a..74e66e758 100644 --- a/tests/e2e/workload-lifecycle/03-assert-action-created.yaml +++ b/tests/e2e/workload-lifecycle/03-assert-action-created.yaml @@ -63,6 +63,12 @@ spec: fieldPath: metadata.name - name: GOMEMLIMIT (value != null): true + - name: GOMAXPROCS + valueFrom: + resourceFieldRef: + containerName: gateway + divisor: "0" + resource: limits.cpu name: gateway resources: requests: