From dcfbed0f0affff5366da68b156058004731b4507 Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Tue, 16 Jun 2026 16:15:33 -0700 Subject: [PATCH 1/2] feat(register): expose Backend wrapper for kplane-dev/storage registry (KPEP-0001 phase 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds register.go: an Options struct that implements registry.Backend so the kplane apiserver can register Spanner against a *registry.Backends in its Phase 3 dispatch swap, without the apiserver source needing to know anything Spanner-specific. The wrapper is thin: NewOptions() returns an Options whose Build() calls the existing NewBackendFactory. Identical store/broadcaster/watcher behavior to the legacy 'if opts.SpannerProject != ""' branch in the apiserver, so the Phase 3 cutover is behavior-preserving. Bumps go.mod fork pin to the cherry-pick branch (kplane-dev/kubernetes#3) so 'storage.WithDecodeCallback' resolves at the storage package — that's the symbol watcher.go/store.go have been calling since they were written, but no merged fork commit exposed it publicly until the cherry-pick. Also adds the github.com/kplane-dev/storage require (registry/ subpackage) pinned to the kpep-0001/add-registry branch head. Once both prerequisite PRs merge, a follow-up bumps these pins to whichever main SHAs end up containing them. Tested: - go build ./... clean (was previously broken at HEAD). - go test -run 'TestOptions|TestAddFlags|TestValidate|TestRegister' ./... passes against the published storage branch. - Existing emulator-backed store tests still gated on SPANNER_EMULATOR_HOST. See KPEP-0001 in kplane-dev/enhancements for the design rationale, and the chain: - kplane-dev/kubernetes#3 (fork: move DecodeCallback to storage pkg) - kplane-dev/storage#2 (registry types) - this PR (Spanner Register wrapper) - kplane-dev/apiserver#? (TBD: dispatch swap) --- go.mod | 15 ++++---- go.sum | 12 ++++++ register.go | 96 ++++++++++++++++++++++++++++++++++++++++++++++ register_test.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 register.go create mode 100644 register_test.go diff --git a/go.mod b/go.mod index b15ae4f..dcf2e5d 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.25.0 godebug default=go1.25 replace ( - k8s.io/api => github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260303044756-e9e2a52adaf0 - k8s.io/apimachinery => github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260303044756-e9e2a52adaf0 - k8s.io/apiserver => github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260303044756-e9e2a52adaf0 - k8s.io/client-go => github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260303044756-e9e2a52adaf0 - k8s.io/component-base => github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260303044756-e9e2a52adaf0 - k8s.io/kms => github.com/kplane-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20260303044756-e9e2a52adaf0 + k8s.io/api => github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260616231039-40baaf871491 + k8s.io/apimachinery => github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260616231039-40baaf871491 + k8s.io/apiserver => github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260616231039-40baaf871491 + k8s.io/client-go => github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260616231039-40baaf871491 + k8s.io/component-base => github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260616231039-40baaf871491 + k8s.io/kms => github.com/kplane-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20260616231039-40baaf871491 ) require ( @@ -64,6 +64,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kplane-dev/storage v0.0.0-20260616225019-c6a9aa30bcf3 github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -75,7 +76,7 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.19.2 // indirect - github.com/spf13/pflag v1.0.9 // indirect + github.com/spf13/pflag v1.0.9 github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/etcd/api/v3 v3.6.7 // indirect diff --git a/go.sum b/go.sum index 4b80c60..32ce0d8 100644 --- a/go.sum +++ b/go.sum @@ -147,6 +147,18 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260616231039-40baaf871491 h1:YxH+VkN3lU662izIZC9xabVf+ZUg+m3hvV2FkaXeIwA= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260616231039-40baaf871491/go.mod h1:KOrdwhDi3QHVXr50HAWBhO2r9uMtWbO46CiIHzRVtU8= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260616231039-40baaf871491 h1:nrCd6mHwGv2pe6bTc16aD3k8sFYAVh63qYU1+OuQsVc= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260616231039-40baaf871491/go.mod h1:7mgr/dli8ofwAbcIQXetFVX1fbOYsOYojq3AUbybVmQ= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260616231039-40baaf871491 h1:hnDQq2OwnjnyjaoKZW+Px0fJDebH20QHPFRoYYY7/Zw= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260616231039-40baaf871491/go.mod h1:GNWcUSRqjpm4i1hrLaGA7EQrl60YdahMic4aS+WUQVI= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260616231039-40baaf871491 h1:Hjur5wSCMN4qskSUUS2mnz0RJjcBLqOx7qyEPQ3HMd8= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260616231039-40baaf871491/go.mod h1:7IM9p4c8CafSxF7ZY0F46WHylFn3o4mLVW5T1VZbaY8= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260616231039-40baaf871491 h1:w5HwGqXhioDpa9VB8tWdAdZ4fV3MYtqDV6GvGN0GGuo= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260616231039-40baaf871491/go.mod h1:R6vYa1XRfX3PdQEGNkCaL3pt7NvLU2ti7FPzsEsA6GQ= +github.com/kplane-dev/storage v0.0.0-20260616225019-c6a9aa30bcf3 h1:uPD+VCKT3SJquAYas4i2mjLheiLD8M0XTg9kE+b+vRs= +github.com/kplane-dev/storage v0.0.0-20260616225019-c6a9aa30bcf3/go.mod h1:jEwJvr6Y6pzVJXxb8MwvePBk8kMMxaagduJdirNi7dE= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= diff --git a/register.go b/register.go new file mode 100644 index 0000000..e279a16 --- /dev/null +++ b/register.go @@ -0,0 +1,96 @@ +package spanner + +import ( + "fmt" + + "github.com/spf13/pflag" + + "github.com/kplane-dev/storage/registry" +) + +// Options is the Spanner backend's flag block. It implements +// registry.Backend so an apiserver constructing a *registry.Backends can +// register Spanner via NewOptions() (and switch to it at runtime via +// --storage-backend=spanner) without the apiserver knowing anything +// Spanner-specific. +// +// Lifecycle: +// - AddFlags binds the --spanner-* flags. Called for every registered +// backend at startup so the help text lists every backend's flags. +// - Validate runs flag-level checks (required fields, sensible +// combinations). Called only when --storage-backend=spanner. +// - Build dials the shared Spanner client and returns a registry.Factory +// that creates per-resource stores against it. +// +// The Build factory delegates to NewBackendFactory (the existing +// constructor used by the apiserver's pre-registry hardcoded path), so the +// underlying store, broadcaster, and watcher behavior is identical between +// the legacy --spanner-project dispatch and the new --storage-backend +// dispatch. That lets us flip between paths during the cutover without +// behavior drift. +type Options struct { + cfg SpannerConfig +} + +// NewOptions returns a fresh Options. The apiserver registers this via +// b.Register(spanner.NewOptions()) inside its backends/RegisterBuiltin +// aggregator. +func NewOptions() *Options { + return &Options{} +} + +// Name is the value matched against --storage-backend. +func (o *Options) Name() string { return "spanner" } + +// AddFlags binds the --spanner-* flags. The flag set is shared across all +// registered backends so the apiserver's --help lists every backend's +// flags regardless of which one is selected. +func (o *Options) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&o.cfg.Project, "spanner-project", o.cfg.Project, + "Google Cloud project hosting the Spanner instance.") + fs.StringVar(&o.cfg.Instance, "spanner-instance", o.cfg.Instance, + "Spanner instance ID.") + fs.StringVar(&o.cfg.Database, "spanner-database", o.cfg.Database, + "Spanner database name within the instance.") + fs.StringVar(&o.cfg.EmulatorHost, "spanner-emulator-host", o.cfg.EmulatorHost, + "Optional host:port of a local Spanner emulator. When set, the project/instance/database flags still apply but credentials are skipped.") +} + +// Validate runs flag-level checks. Only invoked for the selected backend, +// so users running with --storage-backend=etcd3 (or the legacy default) +// aren't forced to set Spanner flags. +func (o *Options) Validate() []error { + var errs []error + if o.cfg.Project == "" { + errs = append(errs, fmt.Errorf("--spanner-project is required when --storage-backend=spanner")) + } + if o.cfg.Instance == "" { + errs = append(errs, fmt.Errorf("--spanner-instance is required when --storage-backend=spanner")) + } + if o.cfg.Database == "" { + errs = append(errs, fmt.Errorf("--spanner-database is required when --storage-backend=spanner")) + } + return errs +} + +// Build returns the per-resource Factory. The apiserver calls this once +// after Validate; the resulting Factory is invoked once per GroupResource +// at REST registry construction time. +// +// We reuse the existing NewBackendFactory so the legacy hardcoded path +// (apiserver's `if opts.SpannerProject != ""` branch) and the registry +// path produce identical store/broadcaster/watcher behavior. That keeps +// the Phase 3 cutover in the apiserver behavior-preserving. +func (o *Options) Build() (registry.Factory, error) { + bf := NewBackendFactory(o.cfg) + return registry.Factory(bf), nil +} + +// Compile-time assertion: Options satisfies registry.Backend. +var _ registry.Backend = (*Options)(nil) + +// Config exposes the resolved SpannerConfig for callers that need to +// inspect the backend's wiring (e.g. health-probe construction, debug +// endpoints). Returns the value, not a pointer, so callers can't mutate +// the live config after Build. +func (o *Options) Config() SpannerConfig { return o.cfg } diff --git a/register_test.go b/register_test.go new file mode 100644 index 0000000..3c66422 --- /dev/null +++ b/register_test.go @@ -0,0 +1,99 @@ +package spanner_test + +import ( + "strings" + "testing" + + "github.com/spf13/pflag" + + "github.com/kplane-dev/spanner" + "github.com/kplane-dev/storage/registry" +) + +func TestOptionsImplementsBackend(t *testing.T) { + // Compile-time check that *Options implements registry.Backend. + // Repeated here as a runtime assertion so a future signature drift + // fails the test instead of silently breaking the registry contract. + var _ registry.Backend = spanner.NewOptions() + + if got := spanner.NewOptions().Name(); got != "spanner" { + t.Fatalf("Name() = %q; want %q", got, "spanner") + } +} + +func TestAddFlagsBindsSpannerPrefixed(t *testing.T) { + o := spanner.NewOptions() + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + o.AddFlags(fs) + + want := []string{ + "spanner-project", + "spanner-instance", + "spanner-database", + "spanner-emulator-host", + } + for _, name := range want { + if fs.Lookup(name) == nil { + t.Errorf("--%s not bound by AddFlags", name) + } + } +} + +func TestValidateRequiresCoreFields(t *testing.T) { + o := spanner.NewOptions() + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + o.AddFlags(fs) + + errs := o.Validate() + if len(errs) != 3 { + t.Fatalf("Validate() returned %d errors; want 3 (missing project/instance/database). errs=%v", len(errs), errs) + } + for _, want := range []string{"spanner-project", "spanner-instance", "spanner-database"} { + found := false + for _, e := range errs { + if strings.Contains(e.Error(), want) { + found = true + break + } + } + if !found { + t.Errorf("Validate() missing error mentioning --%s; got %v", want, errs) + } + } +} + +func TestValidatePassesWhenFlagsSet(t *testing.T) { + o := spanner.NewOptions() + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + o.AddFlags(fs) + + if err := fs.Parse([]string{ + "--spanner-project=p", + "--spanner-instance=i", + "--spanner-database=d", + }); err != nil { + t.Fatalf("flag parse: %v", err) + } + + if errs := o.Validate(); len(errs) != 0 { + t.Fatalf("Validate() returned %v; want none", errs) + } + + cfg := o.Config() + if cfg.Project != "p" || cfg.Instance != "i" || cfg.Database != "d" { + t.Fatalf("Config() did not capture flag values: %+v", cfg) + } +} + +func TestRegisterRoundtripsThroughRegistry(t *testing.T) { + b := registry.New() + b.Register(spanner.NewOptions()) + + got, ok := b.Get("spanner") + if !ok { + t.Fatalf("Get(spanner) ok=false; want true") + } + if _, isOptions := got.(*spanner.Options); !isOptions { + t.Fatalf("Get(spanner) returned %T; want *spanner.Options", got) + } +} From aa4660d474fb95ac956f6b26d1b9f035f1a21ea5 Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Tue, 16 Jun 2026 16:53:51 -0700 Subject: [PATCH 2/2] chore: pin fork to feat/per-cluster-allocators + bump storage to KPEP-0001 head Aligns with the consolidated KPEP-0001 fork branch. Both prerequisite features (DecodeCallback move + per-cluster allocators) live there; the former is what unblocks store.go/watcher.go from using storage.WithDecodeCallback. Apiserver Phase 3 will pin the same fork SHA. --- go.mod | 14 +++++++------- go.sum | 24 ++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index dcf2e5d..17ce075 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.25.0 godebug default=go1.25 replace ( - k8s.io/api => github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260616231039-40baaf871491 - k8s.io/apimachinery => github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260616231039-40baaf871491 - k8s.io/apiserver => github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260616231039-40baaf871491 - k8s.io/client-go => github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260616231039-40baaf871491 - k8s.io/component-base => github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260616231039-40baaf871491 - k8s.io/kms => github.com/kplane-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20260616231039-40baaf871491 + k8s.io/api => github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260311054814-32f5e9075db5 + k8s.io/apimachinery => github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260311054814-32f5e9075db5 + k8s.io/apiserver => github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260311054814-32f5e9075db5 + k8s.io/client-go => github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260311054814-32f5e9075db5 + k8s.io/component-base => github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260311054814-32f5e9075db5 + k8s.io/kms => github.com/kplane-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20260311054814-32f5e9075db5 ) require ( @@ -64,7 +64,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kplane-dev/storage v0.0.0-20260616225019-c6a9aa30bcf3 + github.com/kplane-dev/storage v0.0.0-20260616235235-c3b3554debc3 github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect diff --git a/go.sum b/go.sum index 32ce0d8..9805f2c 100644 --- a/go.sum +++ b/go.sum @@ -147,18 +147,18 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260616231039-40baaf871491 h1:YxH+VkN3lU662izIZC9xabVf+ZUg+m3hvV2FkaXeIwA= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260616231039-40baaf871491/go.mod h1:KOrdwhDi3QHVXr50HAWBhO2r9uMtWbO46CiIHzRVtU8= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260616231039-40baaf871491 h1:nrCd6mHwGv2pe6bTc16aD3k8sFYAVh63qYU1+OuQsVc= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260616231039-40baaf871491/go.mod h1:7mgr/dli8ofwAbcIQXetFVX1fbOYsOYojq3AUbybVmQ= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260616231039-40baaf871491 h1:hnDQq2OwnjnyjaoKZW+Px0fJDebH20QHPFRoYYY7/Zw= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260616231039-40baaf871491/go.mod h1:GNWcUSRqjpm4i1hrLaGA7EQrl60YdahMic4aS+WUQVI= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260616231039-40baaf871491 h1:Hjur5wSCMN4qskSUUS2mnz0RJjcBLqOx7qyEPQ3HMd8= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260616231039-40baaf871491/go.mod h1:7IM9p4c8CafSxF7ZY0F46WHylFn3o4mLVW5T1VZbaY8= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260616231039-40baaf871491 h1:w5HwGqXhioDpa9VB8tWdAdZ4fV3MYtqDV6GvGN0GGuo= -github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260616231039-40baaf871491/go.mod h1:R6vYa1XRfX3PdQEGNkCaL3pt7NvLU2ti7FPzsEsA6GQ= -github.com/kplane-dev/storage v0.0.0-20260616225019-c6a9aa30bcf3 h1:uPD+VCKT3SJquAYas4i2mjLheiLD8M0XTg9kE+b+vRs= -github.com/kplane-dev/storage v0.0.0-20260616225019-c6a9aa30bcf3/go.mod h1:jEwJvr6Y6pzVJXxb8MwvePBk8kMMxaagduJdirNi7dE= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260311054814-32f5e9075db5 h1:VGSKAidEijlXCza8zQ6pdFZz9QczfT98wBox+qRTaxc= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20260311054814-32f5e9075db5/go.mod h1:KOrdwhDi3QHVXr50HAWBhO2r9uMtWbO46CiIHzRVtU8= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260311054814-32f5e9075db5 h1:FdV1ooo4kmNti8PMOwjuzMgP+HaUjggCVW502sdMbV8= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20260311054814-32f5e9075db5/go.mod h1:7mgr/dli8ofwAbcIQXetFVX1fbOYsOYojq3AUbybVmQ= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260311054814-32f5e9075db5 h1:B32q2wQIQLDhK8jhqRgRIBUZS2WoSEYYFau0dh9pl/g= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20260311054814-32f5e9075db5/go.mod h1:GNWcUSRqjpm4i1hrLaGA7EQrl60YdahMic4aS+WUQVI= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260311054814-32f5e9075db5 h1:g1Cgzs09gUaCwXCtSzWJS5rFYaK1ycHKruIpwuCJJtI= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20260311054814-32f5e9075db5/go.mod h1:7IM9p4c8CafSxF7ZY0F46WHylFn3o4mLVW5T1VZbaY8= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260311054814-32f5e9075db5 h1:l4RLyYStWu+BL8uKL++4byqPwLc2cv76rnHW/7A+Cvw= +github.com/kplane-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20260311054814-32f5e9075db5/go.mod h1:R6vYa1XRfX3PdQEGNkCaL3pt7NvLU2ti7FPzsEsA6GQ= +github.com/kplane-dev/storage v0.0.0-20260616235235-c3b3554debc3 h1:JXZY2nstdsRtsi4o/a5bLSmmz2wkiz8VQpOfDBSvtTk= +github.com/kplane-dev/storage v0.0.0-20260616235235-c3b3554debc3/go.mod h1:IWZvGJkZ7X2hCchIa+EiGDMV2fctDh7bTtV2dc4s3Ug= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=