diff --git a/README.md b/README.md index d6a63de..74450e1 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,23 @@ The composition-dynamic-controller is an operator that is instantiated by the [c ## Summary -- [Summary](#summary) -- [Architecture](#architecture) -- [Overview](#overview) -- [Examples](#examples) -- [Configuration](#configuration) +- [Composition Dynamic Controller](#composition-dynamic-controller) + - [Summary](#summary) + - [Architecture](#architecture) + - [Workflow](#workflow) + - [Composition Dynamic Controller (CDC) \& Chart Inspector: Secure Helm Lifecycle Management](#composition-dynamic-controller-cdc--chart-inspector-secure-helm-lifecycle-management) + - [Core CDC Workflow (with Chart Inspector Integration)](#core-cdc-workflow-with-chart-inspector-integration) + - [Key Capabilities Enabled by This Collaboration](#key-capabilities-enabled-by-this-collaboration) + - [Why This Architecture Matters](#why-this-architecture-matters) + - [Real-World Example: Handling a Breaking Chart Change](#real-world-example-handling-a-breaking-chart-change) + - [Helm Release Name Logic](#helm-release-name-logic) + - [Prior Versions (\<= 0.19.9)](#prior-versions--0199) + - [Subsequent Versions (\>= 0.20.0)](#subsequent-versions--0200) + - [Composition Dynamic Controller Values Injection](#composition-dynamic-controller-values-injection) + - [About the `gracefullyPaused` value](#about-the-gracefullypaused-value) + - [Configuration](#configuration) + - [Operator Env Vars](#operator-env-vars) + ## Architecture @@ -18,11 +30,11 @@ The composition-dynamic-controller is an operator that is instantiated by the [c ![composition-dynamic-controller State Diagram](_diagrams/composition-dynamic-controller-flow.png "composition-dynamic-controller State Diagram") -### **Composition Dynamic Controller (CDC) & Chart Inspector: Secure Helm Lifecycle Management** +### Composition Dynamic Controller (CDC) & Chart Inspector: Secure Helm Lifecycle Management The **Composition Dynamic Controller (CDC)** is a specialized Kubernetes operator that orchestrates the end-to-end lifecycle of Krateo compositions. Acting as the reconciliation engine for Composition custom resources, it bridges declarative application definitions with Helm’s packaging system through intelligent automation. The **Chart Inspector** serves as its "safety advisor," enabling proactive decision-making via dry-run analysis. -#### **Core CDC Workflow (with Chart Inspector Integration)** +#### Core CDC Workflow (with Chart Inspector Integration) 1. **Reconciliation Trigger** - Watches for changes to `Composition` CRs or Helm chart versions. - Invokes the **Chart Inspector** to simulate installations/upgrades *before* execution. @@ -43,7 +55,7 @@ The **Composition Dynamic Controller (CDC)** is a specialized Kubernetes operato --- -### **Key Capabilities Enabled by This Collaboration** +### Key Capabilities Enabled by This Collaboration | **Feature** | **CDC’s Role** | **Chart Inspector’s Contribution** | |----------------------------|-----------------------------------------|----------------------------------------------------| @@ -55,7 +67,7 @@ The **Composition Dynamic Controller (CDC)** is a specialized Kubernetes operato --- -### **Why This Architecture Matters** +### Why This Architecture Matters 1. **Safety Net** - The Chart Inspector’s dry-run prevents "helm surprises" (e.g., undeclared CRD creations or namespace pollution). - Example: Blocks a chart upgrade if the new version requires a `ClusterRole` the CDC isn’t authorized to manage. @@ -70,7 +82,7 @@ The **Composition Dynamic Controller (CDC)** is a specialized Kubernetes operato --- -### **Real-World Example: Handling a Breaking Chart Change** +### Real-World Example: Handling a Breaking Chart Change 1. **Scenario**: A Helm chart v1.2.0 introduces a new `CustomResourceDefinition` (CRD). 2. **CDC+Inspector Flow**: - **Dry-run** detects the new CRD and its required API group permissions. @@ -79,6 +91,33 @@ The **Composition Dynamic Controller (CDC)** is a specialized Kubernetes operato 3. **Result**: Zero downtime; no "helm upgrade failed: CRD missing" errors. +--- + +## Helm Release Name Logic + +### Prior Versions (<= 0.19.9) + +For versions up to 0.19.9, the **`composition-dynamic-controller`** used the following logic to determine the **Helm release name** associated with a composition resource: + +1. If the **label** `krateo.io/release-name` is set on the composition resource, its value is used as the Helm release name. +2. Otherwise, the **composition resource name** is used as the Helm release name. + +--- + +### Subsequent Versions (>= 0.20.0) + +Starting from version 0.20.0, the **`composition-dynamic-controller`** uses the following logic to determine the Helm release name associated with a composition resource: + +1. If the **annotation** `krateo.io/release-name` is set on the composition resource, its value is used as the Helm release name. +2. Otherwise, the release name is composed as follows: **`{composition.metadata.name}-{composition.metadata.uid[:8]}`**. + +**N.B.:** From this version onward, the `metadata.name` field of the composition cannot exceed **44 characters**. This is because the UID suffix adds **9 characters** to the release name (the hyphen `-` plus the 8 characters of the UID), and Helm release names cannot exceed **53 characters** in total. + +This change was implemented to avoid conflicts when multiple resources belonging to the `composition.krateo.io` group with the same `metadata.name` are created in **different namespaces**. + +--- + + ## Composition Dynamic Controller Values Injection The composition-dynamic-controller inject labels and values into the installed resources and in the helm chart release values. This values contains informations about the composition resource associated with the helm release. diff --git a/go.mod b/go.mod index c05e391..15e35d5 100644 --- a/go.mod +++ b/go.mod @@ -4,22 +4,22 @@ go 1.25.0 require ( github.com/Masterminds/semver v1.5.0 - github.com/Masterminds/semver/v3 v3.3.0 + github.com/Masterminds/semver/v3 v3.4.0 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/go-logr/logr v1.4.3 github.com/gobuffalo/flect v1.0.3 github.com/golang/mock v1.6.0 github.com/krateoplatformops/plumbing v0.7.2 - github.com/krateoplatformops/unstructured-runtime v0.2.8 + github.com/krateoplatformops/unstructured-runtime v0.3.0 github.com/pkg/errors v0.9.1 - github.com/spf13/pflag v1.0.7 - github.com/stretchr/testify v1.10.0 - helm.sh/helm/v3 v3.18.6 - k8s.io/api v0.34.1 - k8s.io/apiextensions-apiserver v0.33.3 - k8s.io/apimachinery v0.34.1 - k8s.io/cli-runtime v0.34.0 - k8s.io/client-go v0.34.1 + github.com/spf13/pflag v1.0.10 + github.com/stretchr/testify v1.11.1 + helm.sh/helm/v3 v3.19.2 + k8s.io/api v0.34.2 + k8s.io/apiextensions-apiserver v0.34.2 + k8s.io/apimachinery v0.34.2 + k8s.io/cli-runtime v0.34.2 + k8s.io/client-go v0.34.2 sigs.k8s.io/e2e-framework v0.6.0 sigs.k8s.io/kustomize/kyaml v0.20.1 sigs.k8s.io/yaml v1.6.0 @@ -38,17 +38,17 @@ require ( github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/containerd/containerd v1.7.27 // indirect + github.com/containerd/containerd v1.7.29 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/cyphar/filepath-securejoin v0.6.0 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/evanphx/json-patch v5.9.11+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.18.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect @@ -106,35 +106,35 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/cobra v1.10.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/twmb/murmur3 v1.1.8 // indirect github.com/vladimirvivien/gexe v0.4.1 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/net v0.41.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/time v0.9.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/grpc v1.68.1 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/time v0.12.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/grpc v1.72.1 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.33.3 // indirect - k8s.io/component-base v0.33.3 // indirect + k8s.io/apiserver v0.34.2 // indirect + k8s.io/component-base v0.34.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect - k8s.io/kubectl v0.33.3 // indirect + k8s.io/kubectl v0.34.0 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect oras.land/oras-go/v2 v2.6.0 // indirect sigs.k8s.io/controller-runtime v0.20.0 // indirect diff --git a/go.sum b/go.sum index e9bcc0c..aa60936 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJ github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= @@ -38,8 +38,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII= -github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0= +github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE= +github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -51,8 +51,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is= +github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= 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= @@ -87,8 +87,8 @@ github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7Dlme github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -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.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= @@ -146,9 +146,8 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -178,8 +177,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/krateoplatformops/plumbing v0.7.2 h1:4UuWy9747p9ligMtNEiOOQGsuK6d9lczg7R1no8ERsE= github.com/krateoplatformops/plumbing v0.7.2/go.mod h1:mQ/sm0viyKgfR2ARzHuwCpY0rcyMKqCv8a8SOu52yYQ= -github.com/krateoplatformops/unstructured-runtime v0.2.8 h1:Hwvf/aPMM1mSiO+AzlxmybHt5yEe/58D8MtkMPxbBZA= -github.com/krateoplatformops/unstructured-runtime v0.2.8/go.mod h1:19uT87wZzRSjrfk3731Xhdt8ww7vnsXhljy4jk0cuWA= +github.com/krateoplatformops/unstructured-runtime v0.3.0 h1:0lQDUDTViPEBx988b1JJYlVJNwbycTngsyqdaUOzUTQ= +github.com/krateoplatformops/unstructured-runtime v0.3.0/go.mod h1:19uT87wZzRSjrfk3731Xhdt8ww7vnsXhljy4jk0cuWA= 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/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= @@ -277,11 +276,11 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/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= @@ -294,8 +293,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/vladimirvivien/gexe v0.4.1 h1:W9gWkp8vSPjDoXDu04Yp4KljpVMaSt8IQuHswLDd5LY= @@ -315,8 +314,8 @@ go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQ go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -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 v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= 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= @@ -325,10 +324,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.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 v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= 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= @@ -341,18 +340,18 @@ 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.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/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= 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.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.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -366,28 +365,28 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 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.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -398,35 +397,35 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 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.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 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.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= -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.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -441,28 +440,28 @@ 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= -helm.sh/helm/v3 v3.18.6 h1:S/2CqcYnNfLckkHLI0VgQbxgcDaU3N4A/46E3n9wSNY= -helm.sh/helm/v3 v3.18.6/go.mod h1:L/dXDR2r539oPlFP1PJqKAC1CUgqHJDLkxKpDGrWnyg= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apiextensions-apiserver v0.33.3 h1:qmOcAHN6DjfD0v9kxL5udB27SRP6SG/MTopmge3MwEs= -k8s.io/apiextensions-apiserver v0.33.3/go.mod h1:oROuctgo27mUsyp9+Obahos6CWcMISSAPzQ77CAQGz8= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.33.3 h1:Wv0hGc+QFdMJB4ZSiHrCgN3zL3QRatu56+rpccKC3J4= -k8s.io/apiserver v0.33.3/go.mod h1:05632ifFEe6TxwjdAIrwINHWE2hLwyADFk5mBsQa15E= -k8s.io/cli-runtime v0.34.0 h1:N2/rUlJg6TMEBgtQ3SDRJwa8XyKUizwjlOknT1mB2Cw= -k8s.io/cli-runtime v0.34.0/go.mod h1:t/skRecS73Piv+J+FmWIQA2N2/rDjdYSQzEE67LUUs8= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= -k8s.io/component-base v0.33.3 h1:mlAuyJqyPlKZM7FyaoM/LcunZaaY353RXiOd2+B5tGA= -k8s.io/component-base v0.33.3/go.mod h1:ktBVsBzkI3imDuxYXmVxZ2zxJnYTZ4HAsVj9iF09qp4= +helm.sh/helm/v3 v3.19.2 h1:psQjaM8aIWrSVEly6PgYtLu/y6MRSmok4ERiGhZmtUY= +helm.sh/helm/v3 v3.19.2/go.mod h1:gX10tB5ErM+8fr7bglUUS/UfTOO8UUTYWIBH1IYNnpE= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= +k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo= +k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.2 h1:2/yu8suwkmES7IzwlehAovo8dDE07cFRC7KMDb1+MAE= +k8s.io/apiserver v0.34.2/go.mod h1:gqJQy2yDOB50R3JUReHSFr+cwJnL8G1dzTA0YLEqAPI= +k8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE= +k8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= +k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= +k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= 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-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= -k8s.io/kubectl v0.33.3 h1:r/phHvH1iU7gO/l7tTjQk2K01ER7/OAJi8uFHHyWSac= -k8s.io/kubectl v0.33.3/go.mod h1:euj2bG56L6kUGOE/ckZbCoudPwuj4Kud7BR0GzyNiT0= +k8s.io/kubectl v0.34.0 h1:NcXz4TPTaUwhiX4LU+6r6udrlm0NsVnSkP3R9t0dmxs= +k8s.io/kubectl v0.34.0/go.mod h1:bmd0W5i+HuG7/p5sqicr0Li0rR2iIhXL0oUyLF3OjR4= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= diff --git a/internal/composition/composition.go b/internal/composition/composition.go index 37c2bf8..d2de723 100644 --- a/internal/composition/composition.go +++ b/internal/composition/composition.go @@ -8,6 +8,8 @@ import ( "path/filepath" "time" + xcontext "github.com/krateoplatformops/unstructured-runtime/pkg/context" + "github.com/krateoplatformops/composition-dynamic-controller/internal/chartinspector" compositionMeta "github.com/krateoplatformops/composition-dynamic-controller/internal/meta" @@ -21,7 +23,6 @@ import ( "github.com/krateoplatformops/plumbing/kubeutil/event" "github.com/krateoplatformops/plumbing/maps" "github.com/krateoplatformops/unstructured-runtime/pkg/controller" - "github.com/krateoplatformops/unstructured-runtime/pkg/logging" "github.com/krateoplatformops/unstructured-runtime/pkg/meta" "github.com/krateoplatformops/unstructured-runtime/pkg/pluralizer" "github.com/krateoplatformops/unstructured-runtime/pkg/tools" @@ -61,7 +62,7 @@ const ( var _ controller.ExternalClient = (*handler)(nil) -func NewHandler(cfg *rest.Config, log logging.Logger, pig archive.Getter, event event.APIRecorder, pluralizer pluralizer.PluralizerInterface, chartInspectorUrl string, saName string, saNamespace string) controller.ExternalClient { +func NewHandler(cfg *rest.Config, pig archive.Getter, event event.APIRecorder, pluralizer pluralizer.PluralizerInterface, chartInspectorUrl string, saName string, saNamespace string) controller.ExternalClient { val, ok := os.LookupEnv(helmRegistryConfigPathEnvVar) if ok { helmRegistryConfigPath = val @@ -72,10 +73,8 @@ func NewHandler(cfg *rest.Config, log logging.Logger, pig archive.Getter, event return &handler{ kubeconfig: cfg, pluralizer: pluralizer, - logger: log, packageInfoGetter: pig, eventRecorder: event, - chartInspectorUrl: chartInspectorUrl, saName: saName, saNamespace: saNamespace, @@ -84,7 +83,6 @@ func NewHandler(cfg *rest.Config, log logging.Logger, pig archive.Getter, event type handler struct { kubeconfig *rest.Config - logger logging.Logger pluralizer pluralizer.PluralizerInterface packageInfoGetter archive.Getter eventRecorder event.APIRecorder @@ -96,7 +94,10 @@ type handler struct { func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (controller.ExternalObservation, error) { mg = mg.DeepCopy() - log := h.logger.WithValues("op", "Observe"). + + log := xcontext.Logger(ctx) + + log = log.WithValues("op", "Observe"). WithValues("apiVersion", mg.GetAPIVersion()). WithValues("kind", mg.GetKind()). WithValues("name", mg.GetName()). @@ -112,7 +113,7 @@ func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (c DynamicClient: dyn, } - compositionMeta.SetReleaseName(mg, mg.GetName()) + compositionMeta.SetReleaseName(mg, compositionMeta.CalculateReleaseName(mg)) if _, p := compositionMeta.GetGracefullyPausedTime(mg); p && compositionMeta.IsGracefullyPaused(mg) { log.Debug("Composition is gracefully paused, skipping observe.") h.eventRecorder.Event(mg, event.Normal(reasonReconciliationGracefullyPaused, "Observe", "Reconciliation is paused via the gracefully paused annotation.")) @@ -147,12 +148,12 @@ func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (c return controller.ExternalObservation{}, fmt.Errorf("updating cr with values: %w", err) } - hc, _, err := h.helmClientForResource(mg, pkg.RegistryAuth) + hc, _, err := h.helmClientForResource(ctx, mg, pkg.RegistryAuth) if err != nil { return controller.ExternalObservation{}, fmt.Errorf("getting helm client: %w", err) } - rel, err := helmchart.FindRelease(hc, meta.GetReleaseName(mg)) + rel, err := helmchart.FindRelease(hc, compositionMeta.GetReleaseName(mg)) if err != nil { return controller.ExternalObservation{}, fmt.Errorf("finding helm release: %w", err) } @@ -168,7 +169,7 @@ func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (c log.Debug("Composition stuck install or upgrade in progress. Rolling back to previous release before re-attempting.") // Rollback to previous release err = hc.RollbackRelease(&helmclient.ChartSpec{ - ReleaseName: meta.GetReleaseName(mg), + ReleaseName: compositionMeta.GetReleaseName(mg), Namespace: mg.GetNamespace(), Repo: pkg.Repo, ChartName: pkg.URL, @@ -189,7 +190,7 @@ func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (c rbgen := rbacgen.NewRBACGen(h.saName, h.saNamespace, &chartInspector) // Get Resources and generate RBAC generated, err := rbgen. - WithBaseName(meta.GetReleaseName(mg)). + WithBaseName(compositionMeta.GetReleaseName(mg)). Generate(rbacgen.Parameters{ CompositionName: mg.GetName(), CompositionNamespace: mg.GetNamespace(), @@ -207,7 +208,7 @@ func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (c return controller.ExternalObservation{}, fmt.Errorf("installing rbac: %w", err) } - tracer := &tracer.Tracer{} + tracer := tracer.NewTracer(ctx, meta.IsVerbose(mg)) hc, clientset, err := h.helmClientForResourceWithTransportWrapper(mg, pkg.RegistryAuth, func(rt http.RoundTripper) http.RoundTripper { return tracer.WithRoundTripper(rt) }) @@ -302,7 +303,9 @@ func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (c func (h *handler) Create(ctx context.Context, mg *unstructured.Unstructured) error { mg = mg.DeepCopy() - log := h.logger.WithValues("op", "Create"). + + log := xcontext.Logger(ctx) + log = log.WithValues("op", "Create"). WithValues("apiVersion", mg.GetAPIVersion()). WithValues("kind", mg.GetKind()). WithValues("name", mg.GetName()). @@ -324,7 +327,7 @@ func (h *handler) Create(ctx context.Context, mg *unstructured.Unstructured) err return nil } - compositionMeta.SetReleaseName(mg, mg.GetName()) + compositionMeta.SetReleaseName(mg, compositionMeta.CalculateReleaseName(mg)) mg, err = tools.Update(ctx, mg, updateOpts) if err != nil { return fmt.Errorf("updating cr with values: %w", err) @@ -347,7 +350,7 @@ func (h *handler) Create(ctx context.Context, mg *unstructured.Unstructured) err rbgen := rbacgen.NewRBACGen(h.saName, h.saNamespace, &chartInspector) // Get Resources and generate RBAC generated, err := rbgen. - WithBaseName(meta.GetReleaseName(mg)). + WithBaseName(compositionMeta.GetReleaseName(mg)). Generate(rbacgen.Parameters{ CompositionName: mg.GetName(), CompositionNamespace: mg.GetNamespace(), @@ -366,7 +369,7 @@ func (h *handler) Create(ctx context.Context, mg *unstructured.Unstructured) err } // Install the helm chart - hc, clientset, err := h.helmClientForResource(mg, pkg.RegistryAuth) + hc, clientset, err := h.helmClientForResource(ctx, mg, pkg.RegistryAuth) if err != nil { return fmt.Errorf("getting helm client: %w", err) } @@ -437,7 +440,9 @@ func (h *handler) Create(ctx context.Context, mg *unstructured.Unstructured) err func (h *handler) Update(ctx context.Context, mg *unstructured.Unstructured) error { mg = mg.DeepCopy() - log := h.logger.WithValues("op", "Update"). + log := xcontext.Logger(ctx) + + log = log.WithValues("op", "Update"). WithValues("apiVersion", mg.GetAPIVersion()). WithValues("kind", mg.GetKind()). WithValues("name", mg.GetName()). @@ -471,12 +476,12 @@ func (h *handler) Update(ctx context.Context, mg *unstructured.Unstructured) err } // Update the helm chart - hc, clientset, err := h.helmClientForResource(mg, pkg.RegistryAuth) + hc, clientset, err := h.helmClientForResource(ctx, mg, pkg.RegistryAuth) if err != nil { return fmt.Errorf("getting helm client: %w", err) } - rel, err := helmchart.FindRelease(hc, meta.GetReleaseName(mg)) + rel, err := helmchart.FindRelease(hc, compositionMeta.GetReleaseName(mg)) if err != nil { return fmt.Errorf("finding helm release: %w", err) } @@ -544,7 +549,9 @@ func (h *handler) Update(ctx context.Context, mg *unstructured.Unstructured) err func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) error { mg = mg.DeepCopy() - log := h.logger.WithValues("op", "Delete"). + log := xcontext.Logger(ctx) + + log = log.WithValues("op", "Delete"). WithValues("apiVersion", mg.GetAPIVersion()). WithValues("kind", mg.GetKind()). WithValues("name", mg.GetName()). @@ -570,7 +577,7 @@ func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) err return fmt.Errorf("helm chart package info getter must be specified") } - hc, _, err := h.helmClientForResource(mg, nil) + hc, _, err := h.helmClientForResource(ctx, mg, nil) if err != nil { return fmt.Errorf("getting helm client: %w", err) } @@ -581,7 +588,7 @@ func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) err } chartSpec := helmclient.ChartSpec{ - ReleaseName: meta.GetReleaseName(mg), + ReleaseName: compositionMeta.GetReleaseName(mg), Namespace: mg.GetNamespace(), ChartName: pkg.URL, Version: pkg.Version, @@ -589,7 +596,7 @@ func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) err } // Check if the release exists before uninstalling - rel, err := helmchart.FindRelease(hc, meta.GetReleaseName(mg)) + rel, err := helmchart.FindRelease(hc, compositionMeta.GetReleaseName(mg)) if err != nil { return fmt.Errorf("finding helm release: %w", err) } @@ -604,12 +611,12 @@ func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) err return fmt.Errorf("uninstalling helm chart: %w", err) } - rel, err = helmchart.FindRelease(hc, meta.GetReleaseName(mg)) + rel, err = helmchart.FindRelease(hc, compositionMeta.GetReleaseName(mg)) if err != nil { return fmt.Errorf("finding helm release: %w", err) } if rel != nil { - return fmt.Errorf("composition not deleted, release %s still exists", meta.GetReleaseName(mg)) + return fmt.Errorf("composition not deleted, release %s still exists", compositionMeta.GetReleaseName(mg)) } log.Debug("Uninstalling RBAC", "package", pkg.URL) @@ -622,7 +629,7 @@ func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) err rbgen := rbacgen.NewRBACGen(h.saName, h.saNamespace, &chartInspector) // Get Resources and generate RBAC generated, err := rbgen. - WithBaseName(meta.GetReleaseName(mg)). + WithBaseName(compositionMeta.GetReleaseName(mg)). Generate(rbacgen.Parameters{ CompositionName: mg.GetName(), CompositionNamespace: mg.GetNamespace(), @@ -652,8 +659,11 @@ func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) err return nil } -func (h *handler) helmClientForResource(mg *unstructured.Unstructured, registryAuth *helmclient.RegistryAuth) (helmclient.Client, helmclient.CachedClientsInterface, error) { - log := h.logger.WithValues("apiVersion", mg.GetAPIVersion()). +func (h *handler) helmClientForResource(ctx context.Context, mg *unstructured.Unstructured, registryAuth *helmclient.RegistryAuth) (helmclient.Client, helmclient.CachedClientsInterface, error) { + + log := xcontext.Logger(ctx) + + log = log.WithValues("apiVersion", mg.GetAPIVersion()). WithValues("kind", mg.GetKind()). WithValues("name", mg.GetName()). WithValues("namespace", mg.GetNamespace()) diff --git a/internal/composition/composition_test.go b/internal/composition/composition_test.go index 2b31298..5411241 100644 --- a/internal/composition/composition_test.go +++ b/internal/composition/composition_test.go @@ -7,7 +7,6 @@ import ( "context" "encoding/json" "fmt" - "log/slog" "net/http" "net/http/httptest" "os" @@ -26,15 +25,12 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "github.com/go-logr/logr" "github.com/gobuffalo/flect" + compositionMeta "github.com/krateoplatformops/composition-dynamic-controller/internal/meta" "github.com/krateoplatformops/composition-dynamic-controller/internal/tools/helmchart/archive" "github.com/krateoplatformops/plumbing/kubeutil/event" "github.com/krateoplatformops/plumbing/kubeutil/eventrecorder" - prettylog "github.com/krateoplatformops/plumbing/slogs/pretty" "github.com/krateoplatformops/unstructured-runtime/pkg/controller" - "github.com/krateoplatformops/unstructured-runtime/pkg/logging" - "github.com/krateoplatformops/unstructured-runtime/pkg/meta" "github.com/krateoplatformops/unstructured-runtime/pkg/pluralizer" "github.com/krateoplatformops/plumbing/e2e" @@ -135,17 +131,6 @@ func TestController(t *testing.T) { return ctx } - lh := prettylog.New(&slog.HandlerOptions{ - Level: slog.LevelDebug, - AddSource: false, - }, - prettylog.WithDestinationWriter(os.Stderr), - prettylog.WithColor(), - prettylog.WithOutputEmptyAttrs(), - ) - - log := logging.NewLogrLogger(logr.FromSlogHandler(slog.New(lh).Handler())) - var pig archive.Getter pluralizer := FakePluralizer{} @@ -204,7 +189,7 @@ func TestController(t *testing.T) { return ctx } - handler = NewHandler(cfg.Client().RESTConfig(), log, pig, *event.NewAPIRecorder(rec), pluralizer, chartInspectorMockURL, "test-sa", altNamespace) + handler = NewHandler(cfg.Client().RESTConfig(), pig, *event.NewAPIRecorder(rec), pluralizer, chartInspectorMockURL, "test-sa", altNamespace) // handler = NewHandler(cfg.Client().RESTConfig(), log, pig, *event.NewAPIRecorder(rec), pluralizer, chartInspectorUrl, "test-sa", altNamespace) @@ -527,7 +512,7 @@ func TestController(t *testing.T) { Version: "v1", Resource: "secrets", }).Namespace(obj.GetNamespace()).List(ctx, metav1.ListOptions{ - LabelSelector: "name=" + meta.GetReleaseName(u) + ",owner=helm", + LabelSelector: "name=" + compositionMeta.GetReleaseName(u) + ",owner=helm", }) if tmp != nil && len(tmp.Items) > 0 { t.Error("Helm release secret still exists after deletion.") diff --git a/internal/helmclient/tracer/tracer.go b/internal/helmclient/tracer/tracer.go index 0002f05..0260f33 100644 --- a/internal/helmclient/tracer/tracer.go +++ b/internal/helmclient/tracer/tracer.go @@ -1,9 +1,12 @@ package tracer import ( + "context" + "io" "net/http" - "net/http/httputil" "strings" + + xcontext "github.com/krateoplatformops/unstructured-runtime/pkg/context" ) type Resource struct { @@ -20,6 +23,15 @@ type Resource struct { type Tracer struct { http.RoundTripper resources []Resource + verbose bool + context context.Context +} + +func NewTracer(ctx context.Context, verbose bool) *Tracer { + return &Tracer{ + verbose: verbose, + context: ctx, + } } func (t *Tracer) WithRoundTripper(rt http.RoundTripper) *Tracer { @@ -31,14 +43,25 @@ func (t *Tracer) GetResources() []Resource { return t.resources } -// RoundTrip calls the nested RoundTripper while printing each request and -// response/error to t.OutFile on either side of the nested call. WARNING: this -// may output sensitive information including bearer tokens. -func (t *Tracer) RoundTrip(req *http.Request) (*http.Response, error) { - // Dump the request to t.OutFile. - _, err := httputil.DumpRequestOut(req, true) +// RoundTrip implements the http.RoundTripper interface. +// It dumps each request different from GET and its response using the context logger provided. +func (t *Tracer) RoundTrip(req *http.Request) (resp *http.Response, err error) { + + reqBody := "" + if t.verbose && req.Body != nil { // We need to read the body to log it because t.RoundTripper.RoundTrip may consume it. + b, err := io.ReadAll(req.Body) + if err == nil { + reqBody = string(b) + // Reconstruct the Body since it was read. + req.Body = io.NopCloser(strings.NewReader(reqBody)) + } + } + + // Call the nested RoundTripper. + resp, err = t.RoundTripper.RoundTrip(req) + // If an error was returned, dump it to t.OutFile. if err != nil { - return nil, err + return resp, err } if req.Method != http.MethodGet && req.URL.Query().Get("fieldManager") != "" { split := strings.Split(req.URL.Path, "/") @@ -78,25 +101,31 @@ func (t *Tracer) RoundTrip(req *http.Request) (*http.Response, error) { }) } } - } - // Call the nested RoundTripper. - resp, err := t.RoundTripper.RoundTrip(req) - // If an error was returned, dump it to t.OutFile. - if err != nil { - // fmt.Fprintln(t.OutFile, err) - return resp, err - } + if t.verbose && t.context != nil { + log := xcontext.Logger(t.context).WithValues("op", "http-tracer") + respBody := "" + if resp.Body != nil { + b, err := io.ReadAll(resp.Body) + if err == nil { + respBody = string(b) + // Reconstruct the Body since it was read. + resp.Body = io.NopCloser(strings.NewReader(respBody)) + } + } - //Dump the response to t.OutFile. - // _, err = httputil.DumpResponse(resp, req.URL.Query().Get("watch") != "true") - // if err != nil { - // return nil, err - // } - // if req.Method != http.MethodGet { - // t.outFile.Write(b) - // t.outFile.Write([]byte{'\n'}) - // } + log.WithValues("method", req.Method). + WithValues("url", req.URL.String()). + WithValues("content-type", req.Header.Get("Content-Type")). + WithValues("content-length", req.Header.Get("Content-Length")). + WithValues("accept-encoding", req.Header.Get("Accept")). + WithValues("authorization", "redacted"). + WithValues("requestBody", reqBody). + WithValues("responseStatus", resp.Status). + WithValues("responseBody", respBody). + Debug("HTTP Request traced") + } + } return resp, err } diff --git a/internal/meta/meta.go b/internal/meta/meta.go index 3a0940b..658498b 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -1,11 +1,14 @@ package meta import ( + "fmt" "time" "github.com/krateoplatformops/unstructured-runtime/pkg/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/rand" ) const ( @@ -30,6 +33,17 @@ const ( AnnotationKeyReconciliationGracefullyPausedTime = "krateo.io/gracefully-paused-time" ) +func CalculateReleaseName(o runtime.Object) string { + obj := o.(metav1.Object) + uid := obj.GetUID() + if uid == "" { + // Generate random string if UID is not set + return fmt.Sprintf("%s-%s", obj.GetName(), rand.SafeEncodeString(rand.String(8))) + } + hashstr := rand.SafeEncodeString(string(obj.GetUID())[:8]) + return fmt.Sprintf("%s-%s", obj.GetName(), hashstr) +} + func GetReleaseName(o metav1.Object) string { return o.GetLabels()[ReleaseNameLabel] } diff --git a/internal/meta/meta_test.go b/internal/meta/meta_test.go index 1d547ed..000903b 100644 --- a/internal/meta/meta_test.go +++ b/internal/meta/meta_test.go @@ -1,10 +1,13 @@ package meta import ( + "fmt" + "strings" "testing" "time" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -403,3 +406,102 @@ func TestGetGracefullyPausedTime(t *testing.T) { }) } } +func TestCalculateReleaseName_Deterministic(t *testing.T) { + obj := unstructured.Unstructured{} + obj.SetName("my-resource") + obj.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "test.group", + Version: "v1", + Kind: "MyKind", + }) + obj.SetUID(types.UID("5a47edd9-c710-4b4b-b5ea-b6cdf9fc1f58")) + + r1 := CalculateReleaseName(&obj) + r2 := CalculateReleaseName(&obj) + fmt.Println(r1) + fmt.Println(r2) + + if r1 == "" { + t.Fatalf("CalculateReleaseName returned empty string") + } + if r1 != r2 { + t.Fatalf("CalculateReleaseName not deterministic: %q vs %q", r1, r2) + } + if !strings.HasPrefix(r1, "my-resource-") { + t.Fatalf("CalculateReleaseName result %q does not have expected prefix %q", r1, "my-resource-") + } +} + +func TestCalculateReleaseName_DifferentGVKProducesDifferentHash(t *testing.T) { + name := "same-name" + + obj1 := unstructured.Unstructured{} + obj1.SetName(name) + obj1.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "group.one", + Version: "v1", + Kind: "KindA", + }) + obj1.SetUID(types.UID("5a47edd9-c710-4b4b-b5ea-b6cdf9fc1f58")) + + obj2 := unstructured.Unstructured{} + obj2.SetName(name) + obj2.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "group.two", + Version: "v1", + Kind: "KindA", + }) + obj2.SetUID(types.UID("b3d6f4e2-1c2d-4e5f-9a6b-7c8d9e0f1a2b")) + + r1 := CalculateReleaseName(&obj1) + r2 := CalculateReleaseName(&obj2) + + if r1 == r2 { + t.Fatalf("Expected different release names for different GVKs but got same: %q", r1) + } +} + +func TestCalculateReleaseName_NameIncludedAndUniqueForDifferentNames(t *testing.T) { + gvk := schema.GroupVersionKind{Group: "example.io", Version: "v1", Kind: "Example"} + objA := unstructured.Unstructured{} + objA.SetName("alpha") + objA.SetGroupVersionKind(gvk) + objA.SetUID(types.UID("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")) + + objB := unstructured.Unstructured{} + objB.SetName("beta") + objB.SetGroupVersionKind(gvk) + objB.SetUID(types.UID("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb")) + + ra := CalculateReleaseName(&objA) + rb := CalculateReleaseName(&objB) + + if !strings.HasPrefix(ra, "alpha-") { + t.Fatalf("Release name %q does not start with expected prefix %q", ra, "alpha-") + } + if !strings.HasPrefix(rb, "beta-") { + t.Fatalf("Release name %q does not start with expected prefix %q", rb, "beta-") + } + if ra == rb { + t.Fatalf("Expected different release names for different resource names but got same: %q", ra) + } +} + +func TestCalculateReleaseName_UIDNotFound(t *testing.T) { + obj := unstructured.Unstructured{} + obj.SetName("no-uid-resource") + obj.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "test.group", + Version: "v1", + Kind: "NoUIDKind", + }) + // Not setting UID + + releaseName := CalculateReleaseName(&obj) + + fmt.Println(releaseName) + + if !strings.HasPrefix(releaseName, "no-uid-resource-") { + t.Fatalf("CalculateReleaseName result %q does not have expected prefix %q", releaseName, "no-uid-resource-") + } +} diff --git a/internal/tools/helmchart/helmchart_test.go b/internal/tools/helmchart/helmchart_test.go index a7f30d3..473f2db 100644 --- a/internal/tools/helmchart/helmchart_test.go +++ b/internal/tools/helmchart/helmchart_test.go @@ -8,6 +8,8 @@ import ( "fmt" "testing" + compositionMeta "github.com/krateoplatformops/composition-dynamic-controller/internal/meta" + "github.com/krateoplatformops/composition-dynamic-controller/internal/helmclient" "github.com/krateoplatformops/plumbing/e2e" "github.com/krateoplatformops/unstructured-runtime/pkg/meta" @@ -36,53 +38,6 @@ func ExampleExtractValuesFromSpec() { // like: false } -// func TestRenderTemplate(t *testing.T) { -// f := features.New("RenderTemplate"). -// Setup(e2e.Logger("test")). -// Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { -// return ctx -// }).Assess("RenderTemplate", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - -// helmClient, err := helmclient.NewClientFromRestConf(&helmclient.RestConfClientOptions{ -// RestConfig: cfg.Client().RESTConfig(), -// Options: &helmclient.Options{ -// Namespace: namespace, -// }, -// }) -// if err != nil { -// t.Fatalf("failed to create helm client: %v", err) -// } - -// res := createDummyResource() - -// opts := RenderTemplateOptions{ -// PackageUrl: "oci://registry-1.docker.io/bitnamicharts/postgresql", -// PackageVersion: "12.8.3", -// HelmClient: helmClient, -// Resource: res, -// } - -// _, all, err := RenderTemplate(ctx, opts) -// if err != nil { -// t.Fatalf("RenderTemplate failed: %v", err) -// } - -// assert.Equal(t, "v1", all[0].APIVersion) -// assert.Equal(t, "Secret", all[0].Kind) -// assert.Equal(t, "demo-postgresql", all[0].Name) -// assert.Equal(t, "demo-system", all[0].Namespace) - -// assert.Equal(t, "v1", all[1].APIVersion) -// assert.Equal(t, "Service", all[1].Kind) -// assert.Equal(t, "demo-postgresql-hl", all[1].Name) -// assert.Equal(t, "demo-system", all[1].Namespace) - -// return ctx -// }).Feature() - -// testenv.Test(t, f) -// } - func TestFindRelease(t *testing.T) { f := features.New("FindRelease"). Setup(e2e.Logger("test")). @@ -399,7 +354,7 @@ func createDummyResource() *unstructured.Unstructured { } res := &unstructured.Unstructured{} - meta.SetReleaseName(res, "demo") + compositionMeta.SetReleaseName(res, "demo") res.SetGroupVersionKind(schema.FromAPIVersionAndKind("dummy-charts.krateo.io/v0-2-0", "DummyChart")) res.SetName("demo") res.SetNamespace("demo-system") diff --git a/internal/tools/helmchart/install.go b/internal/tools/helmchart/install.go index a6577b4..649b001 100644 --- a/internal/tools/helmchart/install.go +++ b/internal/tools/helmchart/install.go @@ -6,7 +6,6 @@ import ( "github.com/krateoplatformops/composition-dynamic-controller/internal/helmclient" compositionMeta "github.com/krateoplatformops/composition-dynamic-controller/internal/meta" - "github.com/krateoplatformops/unstructured-runtime/pkg/meta" "helm.sh/helm/v3/pkg/release" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -30,7 +29,7 @@ type InstallOptions struct { func Install(ctx context.Context, opts InstallOptions) (*release.Release, int64, error) { chartSpec := helmclient.ChartSpec{ - ReleaseName: meta.GetReleaseName(opts.Resource), + ReleaseName: compositionMeta.GetReleaseName(opts.Resource), Namespace: opts.Resource.GetNamespace(), Version: opts.Version, Repo: opts.Repo, diff --git a/internal/tools/helmchart/update.go b/internal/tools/helmchart/update.go index cce2f6d..f3a2b75 100644 --- a/internal/tools/helmchart/update.go +++ b/internal/tools/helmchart/update.go @@ -6,7 +6,6 @@ import ( "github.com/krateoplatformops/composition-dynamic-controller/internal/helmclient" compositionMeta "github.com/krateoplatformops/composition-dynamic-controller/internal/meta" - "github.com/krateoplatformops/unstructured-runtime/pkg/meta" "helm.sh/helm/v3/pkg/release" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -25,7 +24,7 @@ type UpdateOptions struct { func Update(ctx context.Context, opts UpdateOptions) (*release.Release, error) { chartSpec := helmclient.ChartSpec{ - ReleaseName: meta.GetReleaseName(opts.Resource), + ReleaseName: compositionMeta.GetReleaseName(opts.Resource), Namespace: opts.Resource.GetNamespace(), Repo: opts.Repo, ChartName: opts.ChartName, diff --git a/main.go b/main.go index aae5ec8..730ec5d 100644 --- a/main.go +++ b/main.go @@ -165,7 +165,7 @@ func main() { } labelselector := labels.NewSelector().Add(*labelreq) - handler := composition.NewHandler(cfg, log, pig, *event.NewAPIRecorder(rec), *pluralizer, *urlChartInspector, *saName, *saNamespace) + handler := composition.NewHandler(cfg, pig, *event.NewAPIRecorder(rec), *pluralizer, *urlChartInspector, *saName, *saNamespace) opts := []builder.FuncOption{ builder.WithLogger(log),