diff --git a/.ai/spec/how/cli.md b/.ai/spec/how/cli.md index 6265d4e85..c46aa2af9 100644 --- a/.ai/spec/how/cli.md +++ b/.ai/spec/how/cli.md @@ -15,15 +15,16 @@ Audience: AI agents. This document describes **code layout, client wiring, and I | File | Types | Key functions | |------|-------|---------------| -| `root.go` | — | `NewRootCmd(streams)` — registers `ask`, `troubleshoot` (default mode dispatching), `config` subtree, and `version` | +| `root.go` | — | `NewRootCmd(streams)` — registers subcommands, default mode dispatching, global flags | | `version.go` | Package var `Version` (default `dev`) | `NewVersionCmd(streams)` | +| `kubeconfig.go` | `KubeConfig` | `LoadKubeConfig(kubeconfigPath, contextName, insecureSkipTLS, caCertPath)` — bearer token extraction, TLS config | | `ask.go` | `AskOptions` | `NewAskCmd`, `Complete`, `Validate`, `Run` — streams query in `ask` mode | | `troubleshoot.go` | `TroubleshootOptions` | `NewTroubleshootCmd`, `Complete`, `Validate`, `Run` — streams query in `troubleshooting` mode | | `streaming.go` | `SSEClient` | `NewSSEClient`, `StreamQuery` — shared HTTP + SSE streaming logic | | `attachments.go` | — | `ReadAttachments(paths)` — reads files, builds attachment array | | `render.go` | — | `RenderMarkdown(text)` — terminal markdown rendering via glamour | -*File names are planned. Update during implementation.* +*Implemented: `root.go`, `version.go`, `kubeconfig.go` (OLS-3632). Remaining files are planned.* --- @@ -254,4 +255,11 @@ User invokes: oc ols "why is my pod crashing" --file pod.yaml ## Implementation notes -*Placeholder for findings during implementation. Update with actual file names, type signatures, and discovered constraints as implementation proceeds under OLS-1062.* +### OLS-3632: Scaffolding + kubeconfig integration + +- `cmd/oc-ols/main.go` entry point follows oc-agentic pattern exactly (IOStreams → `NewRootCmd` → `Execute`) +- Global flags registered on root command: `--kubeconfig`, `--insecure-skip-tls-verify`, `--ca-cert`. `--endpoint` deferred to OLS-3633. +- `LoadKubeConfig` checks `restConfig.BearerToken` first, then `restConfig.BearerTokenFile`. Exec-based auth providers that populate tokens via transport wrappers (not `BearerToken` field) will be rejected — this is by design per the spec's "as long as `BearerToken` or `BearerTokenFile` is populated" requirement. +- TLS CA priority: `--ca-cert` flag > kubeconfig `CAData` > kubeconfig `CAFile`. +- New direct dependencies: `github.com/spf13/cobra`, `k8s.io/cli-runtime` (neither was in go.mod previously). +- Build: `go build -o bin/oc-ols ./cmd/oc-ols/`. No Makefile target yet (deferred to OLS-3640). diff --git a/AGENTS.md b/AGENTS.md index 4abda2950..e9f4a2d41 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -98,6 +98,10 @@ make test-e2e # E2E tests (requires cluster) ## Key File Locations +### CLI Plugin +- `cmd/oc-ols/main.go` - CLI binary entry point (IOStreams, root command) +- `cli/` - CLI command implementations (root, version, kubeconfig integration) + ### Controllers - `internal/controller/olsconfig_controller.go` - Main reconciler with finalizer logic - `internal/controller/appserver/` - App server (also owns client CA Secrets `lightspeed-agentic-otel-ca` / `lightspeed-agentic-mcp-ca` / `lightspeed-agentic-rhokp-ca`; `RestartAppServer` calls `RefreshClientCASecrets` and touches the handoff ConfigMap) diff --git a/cli/kubeconfig.go b/cli/kubeconfig.go new file mode 100644 index 000000000..0c8225a38 --- /dev/null +++ b/cli/kubeconfig.go @@ -0,0 +1,123 @@ +package cli + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "os" + "strings" + + "k8s.io/client-go/tools/clientcmd" +) + +const ( + ErrLoadKubeConfig = "failed to load kubeconfig" + ErrResolveContext = "failed to resolve kubeconfig context" + ErrReadTokenFile = "failed to read bearer token file" + ErrReadCACert = "failed to read CA certificate" + ErrInvalidCACert = "CA certificate contains no valid certificates" + ErrInvalidCAData = "kubeconfig CA data contains no valid certificates" + ErrNoBearerToken = "kubeconfig context does not provide a bearer token" //#nosec G101 -- error message, not a credential +) + +// KubeConfig holds the resolved authentication and TLS configuration +// extracted from a kubeconfig file. +type KubeConfig struct { + BearerToken string + TLSConfig *tls.Config + ContextName string +} + +// LoadKubeConfig reads a kubeconfig file, resolves the current context, +// and extracts the bearer token and TLS settings. oc-ols requires +// token-based auth — client-certificate-only contexts are rejected. +func LoadKubeConfig(kubeconfigPath string, contextName string, insecureSkipTLS bool, caCertPath string) (*KubeConfig, error) { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + if kubeconfigPath != "" { + loadingRules.ExplicitPath = kubeconfigPath + } + overrides := &clientcmd.ConfigOverrides{} + if contextName != "" { + overrides.CurrentContext = contextName + } + + clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) + + rawConfig, err := clientConfig.RawConfig() + if err != nil { + return nil, fmt.Errorf("%s: %w", ErrLoadKubeConfig, err) + } + + resolvedContext := rawConfig.CurrentContext + if contextName != "" { + resolvedContext = contextName + } + + restConfig, err := clientConfig.ClientConfig() + if err != nil { + return nil, fmt.Errorf("%s %q: %w", ErrResolveContext, resolvedContext, err) + } + + token := strings.TrimSpace(restConfig.BearerToken) + if token == "" && restConfig.BearerTokenFile != "" { + tokenBytes, err := os.ReadFile(restConfig.BearerTokenFile) + if err != nil { + return nil, fmt.Errorf("%s: %w", ErrReadTokenFile, err) + } + token = strings.TrimSpace(string(tokenBytes)) + } + + if token == "" { + return nil, fmt.Errorf( + "%s %q: oc-ols requires token-based authentication", + ErrNoBearerToken, resolvedContext, + ) + } + + insecure := insecureSkipTLS || restConfig.Insecure + tlsConfig := &tls.Config{ + InsecureSkipVerify: insecure, //#nosec G402 -- user-controlled via --insecure-skip-tls-verify flag or kubeconfig + ServerName: restConfig.ServerName, + } + + if !insecure { + if caCertPath != "" { + pool, err := loadCACertPool(caCertPath) + if err != nil { + return nil, err + } + tlsConfig.RootCAs = pool + } else if len(restConfig.CAData) > 0 { + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(restConfig.CAData) { + return nil, fmt.Errorf("%s", ErrInvalidCAData) + } + tlsConfig.RootCAs = pool + } else if restConfig.CAFile != "" { + pool, err := loadCACertPool(restConfig.CAFile) + if err != nil { + return nil, err + } + tlsConfig.RootCAs = pool + } + } + + return &KubeConfig{ + BearerToken: token, + TLSConfig: tlsConfig, + ContextName: resolvedContext, + }, nil +} + +// loadCACertPool reads a PEM-encoded CA certificate file and returns a certificate pool. +func loadCACertPool(path string) (*x509.CertPool, error) { + caCert, err := os.ReadFile(path) //#nosec G304 -- path is user-controlled via --ca-cert flag or kubeconfig CAFile + if err != nil { + return nil, fmt.Errorf("%s %q: %w", ErrReadCACert, path, err) + } + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(caCert) { + return nil, fmt.Errorf("%s %q", ErrInvalidCACert, path) + } + return pool, nil +} diff --git a/cli/kubeconfig_test.go b/cli/kubeconfig_test.go new file mode 100644 index 000000000..cd59cf1c3 --- /dev/null +++ b/cli/kubeconfig_test.go @@ -0,0 +1,206 @@ +package cli + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "os" + "path/filepath" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func writeTestKubeconfig(content string) string { + dir := GinkgoT().TempDir() + path := filepath.Join(dir, "kubeconfig") + Expect(os.WriteFile(path, []byte(content), 0600)).To(Succeed()) + return path +} + +const testKubeconfigWithToken = ` +apiVersion: v1 +kind: Config +current-context: test-ctx +clusters: +- cluster: + server: https://api.test.example.com:6443 + name: test-cluster +contexts: +- context: + cluster: test-cluster + user: test-user + namespace: test-ns + name: test-ctx +users: +- name: test-user + user: + token: sha256~testtoken123 +` + +const testKubeconfigNoToken = ` +apiVersion: v1 +kind: Config +current-context: cert-ctx +clusters: +- cluster: + server: https://api.test.example.com:6443 + name: test-cluster +contexts: +- context: + cluster: test-cluster + user: cert-user + name: cert-ctx +users: +- name: cert-user + user: + client-certificate-data: dGVzdA== + client-key-data: dGVzdA== +` + +var _ = Describe("LoadKubeConfig", func() { + It("extracts the bearer token and context name", func() { + path := writeTestKubeconfig(testKubeconfigWithToken) + kc, err := LoadKubeConfig(path, "", false, "") + Expect(err).NotTo(HaveOccurred()) + Expect(kc.BearerToken).To(Equal("sha256~testtoken123")) + Expect(kc.ContextName).To(Equal("test-ctx")) + }) + + It("returns ErrNoBearerToken for kubeconfig without bearer token", func() { + path := writeTestKubeconfig(testKubeconfigNoToken) + _, err := LoadKubeConfig(path, "", false, "") + Expect(err).To(MatchError(ContainSubstring(ErrNoBearerToken))) + }) + + It("sets InsecureSkipVerify from CLI flag", func() { + path := writeTestKubeconfig(testKubeconfigWithToken) + kc, err := LoadKubeConfig(path, "", true, "") + Expect(err).NotTo(HaveOccurred()) + Expect(kc.TLSConfig.InsecureSkipVerify).To(BeTrue()) + }) + + It("sets InsecureSkipVerify from kubeconfig cluster setting", func() { + kubeconfig := ` +apiVersion: v1 +kind: Config +current-context: insecure-ctx +clusters: +- cluster: + server: https://api.test.example.com:6443 + insecure-skip-tls-verify: true + name: test-cluster +contexts: +- context: + cluster: test-cluster + user: test-user + name: insecure-ctx +users: +- name: test-user + user: + token: sha256~testtoken123 +` + path := writeTestKubeconfig(kubeconfig) + kc, err := LoadKubeConfig(path, "", false, "") + Expect(err).NotTo(HaveOccurred()) + Expect(kc.TLSConfig.InsecureSkipVerify).To(BeTrue()) + }) + + It("returns ErrLoadKubeConfig for nonexistent kubeconfig", func() { + _, err := LoadKubeConfig("/nonexistent/kubeconfig", "", false, "") + Expect(err).To(MatchError(ContainSubstring(ErrLoadKubeConfig))) + }) + + It("loads default kubeconfig via KUBECONFIG env when path is empty", func() { + path := writeTestKubeconfig(testKubeconfigWithToken) + GinkgoT().Setenv("KUBECONFIG", path) + kc, err := LoadKubeConfig("", "", false, "") + Expect(err).NotTo(HaveOccurred()) + Expect(kc.BearerToken).To(Equal("sha256~testtoken123")) + }) + + It("loads a custom CA certificate", func() { + dir := GinkgoT().TempDir() + caPath := filepath.Join(dir, "ca.crt") + + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + Expect(err).NotTo(HaveOccurred()) + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{Organization: []string{"Test"}}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + IsCA: true, + BasicConstraintsValid: true, + } + certDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + Expect(err).NotTo(HaveOccurred()) + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + Expect(os.WriteFile(caPath, certPEM, 0600)).To(Succeed()) + + path := writeTestKubeconfig(testKubeconfigWithToken) + kc, err := LoadKubeConfig(path, "", false, caPath) + Expect(err).NotTo(HaveOccurred()) + Expect(kc.TLSConfig.RootCAs).NotTo(BeNil()) + }) + + It("reads token from file and trims whitespace", func() { + dir := GinkgoT().TempDir() + tokenPath := filepath.Join(dir, "token") + Expect(os.WriteFile(tokenPath, []byte("file-based-token\n"), 0600)).To(Succeed()) + + kubeconfig := ` +apiVersion: v1 +kind: Config +current-context: sa-ctx +clusters: +- cluster: + server: https://api.test.example.com:6443 + name: test-cluster +contexts: +- context: + cluster: test-cluster + user: sa-user + name: sa-ctx +users: +- name: sa-user + user: + tokenFile: ` + tokenPath + ` +` + path := writeTestKubeconfig(kubeconfig) + kc, err := LoadKubeConfig(path, "", false, "") + Expect(err).NotTo(HaveOccurred()) + Expect(kc.BearerToken).To(Equal("file-based-token")) + }) + + It("preserves tls-server-name from kubeconfig", func() { + kubeconfig := ` +apiVersion: v1 +kind: Config +current-context: sni-ctx +clusters: +- cluster: + server: https://api.test.example.com:6443 + tls-server-name: custom-sni.example.com + name: test-cluster +contexts: +- context: + cluster: test-cluster + user: test-user + name: sni-ctx +users: +- name: test-user + user: + token: sha256~testtoken123 +` + path := writeTestKubeconfig(kubeconfig) + kc, err := LoadKubeConfig(path, "", false, "") + Expect(err).NotTo(HaveOccurred()) + Expect(kc.TLSConfig.ServerName).To(Equal("custom-sni.example.com")) + }) +}) diff --git a/cli/root.go b/cli/root.go new file mode 100644 index 000000000..153f32898 --- /dev/null +++ b/cli/root.go @@ -0,0 +1,48 @@ +// Package cli implements the oc-ols kubectl plugin for querying OpenShift Lightspeed from the terminal. +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +const ( + ErrWriteOutput = "failed to write output" +) + +// NewRootCmd creates the root oc-ols command and registers subcommands. +func NewRootCmd(streams genericclioptions.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "oc-ols [command]", + Short: "CLI for OpenShift Lightspeed", + Long: "Ask questions and troubleshoot OpenShift clusters using OpenShift Lightspeed.", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return cmd.Help() + } + if _, err := fmt.Fprintf(streams.ErrOut, "ask command not yet implemented\n"); err != nil { + return fmt.Errorf("%s: %w", ErrWriteOutput, err) + } + return nil + }, + SilenceUsage: true, + Args: cobra.ArbitraryArgs, + } + + cmd.SetIn(streams.In) + cmd.SetOut(streams.Out) + cmd.SetErr(streams.ErrOut) + + cmd.PersistentFlags().String("kubeconfig", "", + "Path to kubeconfig file (default: $KUBECONFIG or ~/.kube/config)") + cmd.PersistentFlags().Bool("insecure-skip-tls-verify", false, + "Skip TLS certificate verification") + cmd.PersistentFlags().String("ca-cert", "", + "Path to CA certificate for TLS verification") + + cmd.AddCommand(NewVersionCmd(streams)) + + return cmd +} diff --git a/cli/root_test.go b/cli/root_test.go new file mode 100644 index 000000000..fed7813ec --- /dev/null +++ b/cli/root_test.go @@ -0,0 +1,40 @@ +package cli + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("RootCmd", func() { + It("routes the version subcommand", func() { + streams, out, _ := fakeStreams() + cmd := NewRootCmd(streams) + cmd.SetArgs([]string{"version"}) + Expect(cmd.Execute()).To(Succeed()) + Expect(out.String()).To(ContainSubstring("oc-ols")) + }) + + It("shows help when no args are given", func() { + streams, out, _ := fakeStreams() + cmd := NewRootCmd(streams) + cmd.SetArgs([]string{}) + _ = cmd.Execute() + Expect(out.Len()).NotTo(BeZero()) + }) + + It("registers global flags", func() { + streams, _, _ := fakeStreams() + cmd := NewRootCmd(streams) + for _, name := range []string{"kubeconfig", "insecure-skip-tls-verify", "ca-cert"} { + Expect(cmd.PersistentFlags().Lookup(name)).NotTo(BeNil(), "expected persistent flag %q", name) + } + }) + + It("dispatches unrecognized args to the default mode stub", func() { + streams, _, errOut := fakeStreams() + cmd := NewRootCmd(streams) + cmd.SetArgs([]string{"why is my pod crashing"}) + Expect(cmd.Execute()).To(Succeed()) + Expect(errOut.String()).To(ContainSubstring("not yet implemented")) + }) +}) diff --git a/cli/suite_test.go b/cli/suite_test.go new file mode 100644 index 000000000..0b71fcdb9 --- /dev/null +++ b/cli/suite_test.go @@ -0,0 +1,13 @@ +package cli + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCLI(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "CLI Suite") +} diff --git a/cli/testutil_test.go b/cli/testutil_test.go new file mode 100644 index 000000000..ef141d2a3 --- /dev/null +++ b/cli/testutil_test.go @@ -0,0 +1,18 @@ +package cli + +import ( + "bytes" + + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +func fakeStreams() (genericclioptions.IOStreams, *bytes.Buffer, *bytes.Buffer) { + out := &bytes.Buffer{} + errOut := &bytes.Buffer{} + streams := genericclioptions.IOStreams{ + In: &bytes.Buffer{}, + Out: out, + ErrOut: errOut, + } + return streams, out, errOut +} diff --git a/cli/version.go b/cli/version.go new file mode 100644 index 000000000..335c5e50b --- /dev/null +++ b/cli/version.go @@ -0,0 +1,25 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +// Version is overridden at build time via ldflags. +var Version = "dev" + +// NewVersionCmd returns a command that prints the CLI version. +func NewVersionCmd(streams genericclioptions.IOStreams) *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Print the plugin version", + RunE: func(cmd *cobra.Command, args []string) error { + if _, err := fmt.Fprintf(streams.Out, "oc-ols %s\n", Version); err != nil { + return fmt.Errorf("%s: %w", ErrWriteOutput, err) + } + return nil + }, + } +} diff --git a/cli/version_test.go b/cli/version_test.go new file mode 100644 index 000000000..fca322920 --- /dev/null +++ b/cli/version_test.go @@ -0,0 +1,28 @@ +package cli + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("VersionCmd", func() { + It("prints the default version", func() { + streams, out, _ := fakeStreams() + cmd := NewVersionCmd(streams) + cmd.SetArgs([]string{}) + Expect(cmd.Execute()).To(Succeed()) + Expect(out.String()).To(ContainSubstring("oc-ols dev")) + }) + + It("prints an injected version", func() { + original := Version + defer func() { Version = original }() + + Version = "v1.2.3-abc" + streams, out, _ := fakeStreams() + cmd := NewVersionCmd(streams) + cmd.SetArgs([]string{}) + Expect(cmd.Execute()).To(Succeed()) + Expect(out.String()).To(ContainSubstring("oc-ols v1.2.3-abc")) + }) +}) diff --git a/cmd/oc-ols/main.go b/cmd/oc-ols/main.go new file mode 100644 index 000000000..9528126d0 --- /dev/null +++ b/cmd/oc-ols/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "os" + + "k8s.io/cli-runtime/pkg/genericclioptions" + + "github.com/openshift/lightspeed-operator/cli" +) + +func main() { + streams := genericclioptions.IOStreams{ + In: os.Stdin, + Out: os.Stdout, + ErrOut: os.Stderr, + } + cmd := cli.NewRootCmd(streams) + if err := cmd.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index 0a46200f4..a4564ad34 100644 --- a/go.mod +++ b/go.mod @@ -18,12 +18,14 @@ require ( require ( cyphar.com/go-pathrs v0.2.5 // indirect dario.cat/mergo v1.0.2 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.6.0 // indirect github.com/Masterminds/semver/v3 v3.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.4.1 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect + github.com/blang/semver/v4 v4.0.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cloudflare/circl v1.6.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect @@ -41,6 +43,7 @@ require ( github.com/felixge/httpsnoop v1.1.0 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/fxamacker/cbor/v2 v2.9.2 // indirect + github.com/go-errors/errors v1.4.2 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/swag/cmdutils v0.28.0 // indirect @@ -55,12 +58,15 @@ require ( github.com/go-openapi/swag/typeutils v0.28.0 // indirect github.com/go-openapi/swag/yamlutils v0.28.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/go-containerregistry v0.21.9 // indirect github.com/google/go-intervals v0.0.2 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.19.1 // indirect github.com/klauspost/pgzip v1.2.6 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mattn/go-runewidth v0.0.27 // indirect github.com/mattn/go-sqlite3 v1.14.49 // indirect github.com/miekg/pkcs11 v1.1.2 // indirect @@ -72,10 +78,13 @@ require ( github.com/moby/sys/capability v0.4.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/user v0.4.1 // indirect + github.com/moby/term v0.5.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.3.0 // indirect github.com/opencontainers/selinux v1.15.1 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/proglottis/gpgme v0.1.6 // indirect github.com/secure-systems-lab/go-securesystemslib v0.11.0 // indirect @@ -92,6 +101,7 @@ require ( github.com/vbauerster/cupwriter v0.0.4 // indirect github.com/vbauerster/mpb/v8 v8.14.0 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xlab/treeprint v1.2.0 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 // indirect @@ -111,13 +121,17 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect k8s.io/streaming v0.36.3 // indirect k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 // indirect + sigs.k8s.io/kustomize/api v0.21.1 // indirect + sigs.k8s.io/kustomize/kyaml v0.21.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.4.1 // indirect ) require ( github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.93.0 + github.com/spf13/cobra v1.10.2 go.podman.io/image/v5 v5.41.0 + k8s.io/cli-runtime v0.36.3 ) require ( diff --git a/go.sum b/go.sum index 90a6b05ac..9a29f3afc 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ cyphar.com/go-pathrs v0.2.5 h1:SnX9FBvnoyn3lUs1dkMgZ52bAETpirNu3FTRh5HlRik= cyphar.com/go-pathrs v0.2.5/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg= @@ -20,6 +22,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= @@ -36,6 +40,9 @@ github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYgle github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.3.2 h1:MuqHSfiPpGzoAQdgDSX85FgixSgIm9mSDXTLTgugY5E= github.com/containers/ocicrypt v1.3.2/go.mod h1:ntBZabYG0rlvstB/1rK/ba2laaU5EHE0pD5ioHoPTq4= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/cyphar/filepath-securejoin v0.7.0 h1:s0Y3ITPy6sQn5xt54DuYvTF8hu134ooYLUb58DX/HjE= @@ -74,6 +81,8 @@ github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZ github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -123,6 +132,8 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= 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 v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -143,6 +154,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -157,6 +170,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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhVlIR0= @@ -183,12 +198,16 @@ github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9Kou github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/user v0.4.1 h1:RgjRlaDKi/Xmyrz4t8lyzXT6v2ooFeO/7xtchmhVWE0= github.com/moby/sys/user v0.4.1/go.mod h1:E9QsW5WRe1kUAf7kW8hXKwu1uhsZEAdPLYHYSDudF4Y= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= @@ -207,6 +226,8 @@ github.com/openshift/api v0.0.0-20260420151639-34e60874783e h1:ENxXUo0uksvseiBAo github.com/openshift/api v0.0.0-20260420151639-34e60874783e/go.mod h1:pyVjK0nZ4sRs4fuQVQ4rubsJdahI1PB94LnQ8sGdvxo= github.com/openshift/client-go v0.0.0-20260428164731-4b85fc5b4e75 h1:UMBIwb0f9Zre46LksO8P7V8dCNrGOBUdn8fXDgAhepA= github.com/openshift/client-go v0.0.0-20260428164731-4b85fc5b4e75/go.mod h1:lITKsplmZ9kJ6zvk4hW52XMZ9tt621GZGb69YSp+CSY= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 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= @@ -226,6 +247,7 @@ github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+ github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc= @@ -244,6 +266,9 @@ github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/smallstep/pkcs7 v0.2.3 h1:bhoQ3TeZmdoXTatcwxCbk+FMcdsyr0gYrrW2Xq2qr+s= github.com/smallstep/pkcs7 v0.2.3/go.mod h1:7STkdKhZaZe4xNEXTtY4j1NGeST1gYM4GA40kC5iqr8= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +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/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw= @@ -252,6 +277,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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/sylabs/sif/v2 v2.24.1 h1:OhTOfTwBaGXfbWYWXK9C6Pojma3A1bTaNJ6CEhyuKok= @@ -276,6 +302,8 @@ github.com/vbauerster/mpb/v8 v8.14.0 h1:55SR80dptMfASxIG/oCEkBXgBhxeSu4GrVsjl16o github.com/vbauerster/mpb/v8 v8.14.0/go.mod h1:HgpQPKfcWe3kbuGGPmi+jatHreMase5C3Fp5dpdAy0Q= 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/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -304,6 +332,7 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= @@ -316,6 +345,7 @@ golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= @@ -338,10 +368,12 @@ google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ= google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= google.golang.org/protobuf v1.36.12-0.20260806062936-644d0267c26e h1:EIVWoj7hKWJiXy9OdaukBGcSR+NH3cGL/uswTzfvuec= google.golang.org/protobuf v1.36.12-0.20260806062936-644d0267c26e/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.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.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/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= @@ -352,6 +384,8 @@ k8s.io/apiextensions-apiserver v0.36.3 h1:dPmOAPhwTtqb1bTxbFPsy18KHPhktQeO3WUPXu k8s.io/apiextensions-apiserver v0.36.3/go.mod h1:KTXFqgXiuw2pRoL+Wpmttqc+up9Xt/GohadPWeLLOa4= k8s.io/apimachinery v0.36.3 h1:PkzMRBRG8joFD8EhCuQAtNPvJlxb82FwplP26HIzvAM= k8s.io/apimachinery v0.36.3/go.mod h1:cTSjBWgPe/6CQyBKzY/hDIRWCQQQeK0mfLbml0UYFHE= +k8s.io/cli-runtime v0.36.3 h1:g+eJ+M1sYpnNYp/q5fzaw2KejIL0Q7DH+xFl6YVoL4U= +k8s.io/cli-runtime v0.36.3/go.mod h1:hZpAqK8nSFXvvLaVCbzUPVp8e9TRLSTCfpNzMt7s3tE= k8s.io/client-go v0.36.3 h1:M4JdVzXxYcZk4fGpfDdYnxSwhLKWCFoQsHW6t+z8Hfg= k8s.io/client-go v0.36.3/go.mod h1:gcPwr0c87vjjG6HB6pWEqOeuYVoXSsREjzux2j6GF30= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= @@ -368,6 +402,10 @@ sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9 sigs.k8s.io/controller-runtime v0.24.1/go.mod h1:vFkfY5fGt5xAC/sKb8IBFKgWPNKG9OUG29dR8Y2wImw= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/kustomize/api v0.21.1 h1:lzqbzvz2CSvsjIUZUBNFKtIMsEw7hVLJp0JeSIVmuJs= +sigs.k8s.io/kustomize/api v0.21.1/go.mod h1:f3wkKByTrgpgltLgySCntrYoq5d3q7aaxveSagwTlwI= +sigs.k8s.io/kustomize/kyaml v0.21.1 h1:IVlbmhC076nf6foyL6Taw4BkrLuEsXUXNpsE+ScX7fI= +sigs.k8s.io/kustomize/kyaml v0.21.1/go.mod h1:hmxADesM3yUN2vbA5z1/YTBnzLJ1dajdqpQonwBL1FQ= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v6 v6.4.1 h1:AkER7js0XVWi/F/V2Iwl5N7O/B9VP2JyrOMmHPdco+g=