diff --git a/.golangci.yml b/.golangci.yml index effc62551f66..91ff591f8d7e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -163,6 +163,8 @@ linters-settings: - pkg: sigs.k8s.io/cluster-api/exp/runtime/catalog alias: runtimecatalog - pkg: sigs.k8s.io/cluster-api/internal/runtime/client + alias: internalruntimeclient + - pkg: sigs.k8s.io/cluster-api/exp/runtime/client alias: runtimeclient - pkg: sigs.k8s.io/cluster-api/internal/runtime/registry alias: runtimeregistry diff --git a/controllers/alias.go b/controllers/alias.go index 929d1988d941..5789dd07c7b4 100644 --- a/controllers/alias.go +++ b/controllers/alias.go @@ -25,6 +25,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/cluster-api/controllers/clustercache" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" clustercontroller "sigs.k8s.io/cluster-api/internal/controllers/cluster" clusterclasscontroller "sigs.k8s.io/cluster-api/internal/controllers/clusterclass" machinecontroller "sigs.k8s.io/cluster-api/internal/controllers/machine" @@ -34,7 +35,6 @@ import ( clustertopologycontroller "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster" machinedeploymenttopologycontroller "sigs.k8s.io/cluster-api/internal/controllers/topology/machinedeployment" machinesettopologycontroller "sigs.k8s.io/cluster-api/internal/controllers/topology/machineset" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" ) // Following types provides access to reconcilers implemented in internal/controllers, thus diff --git a/exp/runtime/client/client.go b/exp/runtime/client/client.go new file mode 100644 index 000000000000..705c7a7ef0fa --- /dev/null +++ b/exp/runtime/client/client.go @@ -0,0 +1,94 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package client provides the Runtime SDK client. +package client + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" + runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + "sigs.k8s.io/cluster-api/util/cache" +) + +// CallExtensionOption is the interface for configuration that modifies CallExtensionOptions for a CallExtension call. +type CallExtensionOption interface { + // ApplyToOptions applies this configuration to the given CallExtensionOptions. + ApplyToOptions(*CallExtensionOptions) +} + +// CallExtensionCacheEntry is a cache entry for the cache that can be used with the CallExtension call via +// the WithCaching option. +type CallExtensionCacheEntry struct { + CacheKey string + Response runtimehooksv1.ResponseObject +} + +// Key returns the cache key of a CallExtensionCacheEntry. +func (c CallExtensionCacheEntry) Key() string { + return c.CacheKey +} + +// WithCaching enables caching for the CallExtension call. +type WithCaching struct { + Cache cache.Cache[CallExtensionCacheEntry] + CacheKeyFunc func(extensionName, extensionConfigResourceVersion string, request runtimehooksv1.RequestObject) string +} + +// ApplyToOptions applies WithCaching to the given CallExtensionOptions. +func (w WithCaching) ApplyToOptions(in *CallExtensionOptions) { + in.WithCaching = true + in.Cache = w.Cache + in.CacheKeyFunc = w.CacheKeyFunc +} + +// CallExtensionOptions contains the options for the CallExtension call. +type CallExtensionOptions struct { + WithCaching bool + Cache cache.Cache[CallExtensionCacheEntry] + CacheKeyFunc func(extensionName, extensionConfigResourceVersion string, request runtimehooksv1.RequestObject) string +} + +// Client is the runtime client to interact with extensions. +type Client interface { + // WarmUp can be used to initialize a "cold" RuntimeClient with all + // known runtimev1.ExtensionConfigs at a given time. + // After WarmUp completes the RuntimeClient is considered ready. + WarmUp(extensionConfigList *runtimev1.ExtensionConfigList) error + + // IsReady return true after the RuntimeClient finishes warmup. + IsReady() bool + + // Discover makes the discovery call on the extension and returns an updated ExtensionConfig + // with extension handlers information in the ExtensionConfig status. + Discover(context.Context, *runtimev1.ExtensionConfig) (*runtimev1.ExtensionConfig, error) + + // Register registers the ExtensionConfig. + Register(extensionConfig *runtimev1.ExtensionConfig) error + + // Unregister unregisters the ExtensionConfig. + Unregister(extensionConfig *runtimev1.ExtensionConfig) error + + // CallAllExtensions calls all the ExtensionHandler registered for the hook. + CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject) error + + // CallExtension calls the ExtensionHandler with the given name. + CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject, opts ...CallExtensionOption) error +} diff --git a/exp/runtime/controllers/alias.go b/exp/runtime/controllers/alias.go index acd88825d959..48b1a173a3fc 100644 --- a/exp/runtime/controllers/alias.go +++ b/exp/runtime/controllers/alias.go @@ -24,8 +24,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimecontrollers "sigs.k8s.io/cluster-api/exp/runtime/internal/controllers" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" ) // ExtensionConfigReconciler reconciles an ExtensionConfig object. diff --git a/exp/runtime/internal/controllers/extensionconfig_controller.go b/exp/runtime/internal/controllers/extensionconfig_controller.go index 63c762f1b267..50ae4b8af095 100644 --- a/exp/runtime/internal/controllers/extensionconfig_controller.go +++ b/exp/runtime/internal/controllers/extensionconfig_controller.go @@ -38,7 +38,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" diff --git a/exp/runtime/internal/controllers/extensionconfig_controller_test.go b/exp/runtime/internal/controllers/extensionconfig_controller_test.go index 3ea9e3bfcafd..31ce7f7641d7 100644 --- a/exp/runtime/internal/controllers/extensionconfig_controller_test.go +++ b/exp/runtime/internal/controllers/extensionconfig_controller_test.go @@ -41,7 +41,7 @@ import ( runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/feature" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" + internalruntimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry" fakev1alpha1 "sigs.k8s.io/cluster-api/internal/runtime/test/v1alpha1" "sigs.k8s.io/cluster-api/util" @@ -59,7 +59,7 @@ func TestExtensionReconciler_Reconcile(t *testing.T) { g.Expect(fakev1alpha1.AddToCatalog(cat)).To(Succeed()) registry := runtimeregistry.New() - runtimeClient := runtimeclient.New(runtimeclient.Options{ + runtimeClient := internalruntimeclient.New(internalruntimeclient.Options{ Catalog: cat, Registry: registry, }) @@ -214,7 +214,7 @@ func TestExtensionReconciler_discoverExtensionConfig(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) defer srv1.Close() - runtimeClient := runtimeclient.New(runtimeclient.Options{ + runtimeClient := internalruntimeclient.New(internalruntimeclient.Options{ Catalog: cat, Registry: registry, }) @@ -248,7 +248,7 @@ func TestExtensionReconciler_discoverExtensionConfig(t *testing.T) { // srv1 := fakeSecureExtensionServer(discoveryHandler("first")) // defer srv1.Close() - runtimeClient := runtimeclient.New(runtimeclient.Options{ + runtimeClient := internalruntimeclient.New(internalruntimeclient.Options{ Catalog: cat, Registry: registry, }) diff --git a/exp/runtime/internal/controllers/warmup.go b/exp/runtime/internal/controllers/warmup.go index 224ca6336de8..f72d84f47a69 100644 --- a/exp/runtime/internal/controllers/warmup.go +++ b/exp/runtime/internal/controllers/warmup.go @@ -29,7 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" ) const ( diff --git a/exp/runtime/internal/controllers/warmup_test.go b/exp/runtime/internal/controllers/warmup_test.go index e07065d83593..6cf0e22fa2da 100644 --- a/exp/runtime/internal/controllers/warmup_test.go +++ b/exp/runtime/internal/controllers/warmup_test.go @@ -31,7 +31,7 @@ import ( runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/feature" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" + internalruntimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry" fakev1alpha1 "sigs.k8s.io/cluster-api/internal/runtime/test/v1alpha1" ) @@ -75,7 +75,7 @@ func Test_warmupRunnable_Start(t *testing.T) { r := &warmupRunnable{ Client: env.GetClient(), APIReader: env.GetAPIReader(), - RuntimeClient: runtimeclient.New(runtimeclient.Options{ + RuntimeClient: internalruntimeclient.New(internalruntimeclient.Options{ Catalog: cat, Registry: registry, }), @@ -140,7 +140,7 @@ func Test_warmupRunnable_Start(t *testing.T) { r := &warmupRunnable{ Client: env.GetClient(), APIReader: env.GetAPIReader(), - RuntimeClient: runtimeclient.New(runtimeclient.Options{ + RuntimeClient: internalruntimeclient.New(internalruntimeclient.Options{ Catalog: cat, Registry: registry, }), diff --git a/exp/topology/desiredstate/desired_state.go b/exp/topology/desiredstate/desired_state.go index b8efaa8d0af8..785273281812 100644 --- a/exp/topology/desiredstate/desired_state.go +++ b/exp/topology/desiredstate/desired_state.go @@ -36,13 +36,13 @@ import ( "sigs.k8s.io/cluster-api/controllers/external" expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/exp/topology/scope" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/contract" "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches" "sigs.k8s.io/cluster-api/internal/hooks" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" "sigs.k8s.io/cluster-api/internal/topology/clustershim" topologynames "sigs.k8s.io/cluster-api/internal/topology/names" "sigs.k8s.io/cluster-api/internal/topology/ownerrefs" diff --git a/internal/controllers/clusterclass/clusterclass_controller.go b/internal/controllers/clusterclass/clusterclass_controller.go index e54be4e6b262..a577066e6883 100644 --- a/internal/controllers/clusterclass/clusterclass_controller.go +++ b/internal/controllers/clusterclass/clusterclass_controller.go @@ -44,13 +44,13 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/controllers/external" runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/feature" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" - runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry" + internalruntimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" "sigs.k8s.io/cluster-api/internal/topology/variables" - "sigs.k8s.io/cluster-api/internal/util/cache" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/cache" "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/paused" @@ -473,7 +473,7 @@ func (r *Reconciler) extensionConfigToClusterClass(ctx context.Context, o client } for _, patch := range clusterClass.Spec.Patches { if patch.External != nil && patch.External.DiscoverVariablesExtension != nil { - extName, err := runtimeclient.ExtensionNameFromHandlerName(*patch.External.DiscoverVariablesExtension) + extName, err := internalruntimeclient.ExtensionNameFromHandlerName(*patch.External.DiscoverVariablesExtension) if err != nil { log.Error(err, "failed to reconcile ClusterClass for ExtensionConfig") continue @@ -508,9 +508,9 @@ func matchNamespace(ctx context.Context, c client.Client, selector labels.Select return selector.Matches(labels.Set(ns.GetLabels())) } -func cacheKeyFunc(registration *runtimeregistry.ExtensionRegistration, request runtimehooksv1.RequestObject) string { +func cacheKeyFunc(extensionName, extensionConfigResourceVersion string, request runtimehooksv1.RequestObject) string { // Note: registration.Name is identical to the value of the patch.External.DiscoverVariablesExtension field in the ClusterClass. - s := fmt.Sprintf("%s-%s", registration.Name, registration.ExtensionConfigResourceVersion) + s := fmt.Sprintf("%s-%s", extensionName, extensionConfigResourceVersion) for k, v := range request.GetSettings() { s += fmt.Sprintf(",%s=%s", k, v) } diff --git a/internal/controllers/clusterclass/clusterclass_controller_test.go b/internal/controllers/clusterclass/clusterclass_controller_test.go index 6cb63b0ca45d..d547019593ff 100644 --- a/internal/controllers/clusterclass/clusterclass_controller_test.go +++ b/internal/controllers/clusterclass/clusterclass_controller_test.go @@ -41,11 +41,11 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/feature" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" fakeruntimeclient "sigs.k8s.io/cluster-api/internal/runtime/client/fake" - "sigs.k8s.io/cluster-api/internal/util/cache" + "sigs.k8s.io/cluster-api/util/cache" "sigs.k8s.io/cluster-api/util/test/builder" ) diff --git a/internal/controllers/machine/machine_controller.go b/internal/controllers/machine/machine_controller.go index 249eef45b259..adbd51840684 100644 --- a/internal/controllers/machine/machine_controller.go +++ b/internal/controllers/machine/machine_controller.go @@ -53,10 +53,10 @@ import ( "sigs.k8s.io/cluster-api/controllers/noderefutil" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/controllers/machine/drain" - "sigs.k8s.io/cluster-api/internal/util/cache" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" + "sigs.k8s.io/cluster-api/util/cache" "sigs.k8s.io/cluster-api/util/collections" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/finalizers" diff --git a/internal/controllers/machine/machine_controller_test.go b/internal/controllers/machine/machine_controller_test.go index 373a5b0053df..1f963923ad5e 100644 --- a/internal/controllers/machine/machine_controller_test.go +++ b/internal/controllers/machine/machine_controller_test.go @@ -48,9 +48,9 @@ import ( "sigs.k8s.io/cluster-api/controllers/external" externalfake "sigs.k8s.io/cluster-api/controllers/external/fake" "sigs.k8s.io/cluster-api/feature" - "sigs.k8s.io/cluster-api/internal/util/cache" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/cache" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/test/builder" diff --git a/internal/controllers/topology/cluster/cluster_controller.go b/internal/controllers/topology/cluster/cluster_controller.go index a5cd93bf1b06..30c140734fe0 100644 --- a/internal/controllers/topology/cluster/cluster_controller.go +++ b/internal/controllers/topology/cluster/cluster_controller.go @@ -50,13 +50,13 @@ import ( externalfake "sigs.k8s.io/cluster-api/controllers/external/fake" expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/exp/topology/desiredstate" "sigs.k8s.io/cluster-api/exp/topology/scope" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge" "sigs.k8s.io/cluster-api/internal/hooks" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/internal/webhooks" "sigs.k8s.io/cluster-api/util" diff --git a/internal/controllers/topology/cluster/patches/engine.go b/internal/controllers/topology/cluster/patches/engine.go index e21b89c4931c..10633db0e027 100644 --- a/internal/controllers/topology/cluster/patches/engine.go +++ b/internal/controllers/topology/cluster/patches/engine.go @@ -31,6 +31,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/exp/topology/scope" "sigs.k8s.io/cluster-api/feature" @@ -39,7 +40,6 @@ import ( "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/external" "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/inline" "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/variables" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" ) // Engine is a patch engine which applies patches defined in a ClusterBlueprint to a ClusterState. diff --git a/internal/controllers/topology/cluster/patches/external/external_patch_generator.go b/internal/controllers/topology/cluster/patches/external/external_patch_generator.go index e3dc40d67265..0bca6da1b8c3 100644 --- a/internal/controllers/topology/cluster/patches/external/external_patch_generator.go +++ b/internal/controllers/topology/cluster/patches/external/external_patch_generator.go @@ -24,10 +24,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/api" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" ) // externalPatchGenerator generates JSON patches for a GeneratePatchesRequest based on a ClusterClassPatch. diff --git a/internal/controllers/topology/cluster/patches/external/external_patch_generator_test.go b/internal/controllers/topology/cluster/patches/external/external_patch_generator_test.go index 56e8c94a631b..bdcd4b8bf4b7 100644 --- a/internal/controllers/topology/cluster/patches/external/external_patch_generator_test.go +++ b/internal/controllers/topology/cluster/patches/external/external_patch_generator_test.go @@ -28,9 +28,9 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/feature" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" ) func TestExternalPatchGenerator_Generate(t *testing.T) { diff --git a/internal/controllers/topology/cluster/patches/external/external_validator.go b/internal/controllers/topology/cluster/patches/external/external_validator.go index 997d4770b786..9693e456c5a8 100644 --- a/internal/controllers/topology/cluster/patches/external/external_validator.go +++ b/internal/controllers/topology/cluster/patches/external/external_validator.go @@ -24,10 +24,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/api" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" ) // externalValidator validates templates. diff --git a/internal/runtime/client/client.go b/internal/runtime/client/client.go index b0cb562759ce..c4ac40a3febb 100644 --- a/internal/runtime/client/client.go +++ b/internal/runtime/client/client.go @@ -47,10 +47,10 @@ import ( runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" runtimemetrics "sigs.k8s.io/cluster-api/internal/runtime/metrics" runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry" - "sigs.k8s.io/cluster-api/internal/util/cache" "sigs.k8s.io/cluster-api/util" ) @@ -66,7 +66,7 @@ type Options struct { } // New returns a new Client. -func New(options Options) Client { +func New(options Options) runtimeclient.Client { return &client{ catalog: options.Catalog, registry: options.Registry, @@ -74,34 +74,7 @@ func New(options Options) Client { } } -// Client is the runtime client to interact with extensions. -type Client interface { - // WarmUp can be used to initialize a "cold" RuntimeClient with all - // known runtimev1.ExtensionConfigs at a given time. - // After WarmUp completes the RuntimeClient is considered ready. - WarmUp(extensionConfigList *runtimev1.ExtensionConfigList) error - - // IsReady return true after the RuntimeClient finishes warmup. - IsReady() bool - - // Discover makes the discovery call on the extension and returns an updated ExtensionConfig - // with extension handlers information in the ExtensionConfig status. - Discover(context.Context, *runtimev1.ExtensionConfig) (*runtimev1.ExtensionConfig, error) - - // Register registers the ExtensionConfig. - Register(extensionConfig *runtimev1.ExtensionConfig) error - - // Unregister unregisters the ExtensionConfig. - Unregister(extensionConfig *runtimev1.ExtensionConfig) error - - // CallAllExtensions calls all the ExtensionHandler registered for the hook. - CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject) error - - // CallExtension calls the ExtensionHandler with the given name. - CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject, opts ...CallExtensionOption) error -} - -var _ Client = &client{} +var _ runtimeclient.Client = &client{} type client struct { catalog *runtimecatalog.Catalog @@ -278,44 +251,6 @@ func aggregateSuccessfulResponses(aggregatedResponse runtimehooksv1.ResponseObje aggregatedResponse.SetMessage(strings.Join(messages, ", ")) } -// CallExtensionOption is the interface for configuration that modifies CallExtensionOptions for a CallExtension call. -type CallExtensionOption interface { - // ApplyToOptions applies this configuration to the given CallExtensionOptions. - ApplyToOptions(*CallExtensionOptions) -} - -// CallExtensionCacheEntry is a cache entry for the cache that can be used with the CallExtension call via -// the WithCaching option. -type CallExtensionCacheEntry struct { - CacheKey string - Response runtimehooksv1.ResponseObject -} - -// Key returns the cache key of a CallExtensionCacheEntry. -func (c CallExtensionCacheEntry) Key() string { - return c.CacheKey -} - -// WithCaching enables caching for the CallExtension call. -type WithCaching struct { - Cache cache.Cache[CallExtensionCacheEntry] - CacheKeyFunc func(*runtimeregistry.ExtensionRegistration, runtimehooksv1.RequestObject) string -} - -// ApplyToOptions applies WithCaching to the given CallExtensionOptions. -func (w WithCaching) ApplyToOptions(in *CallExtensionOptions) { - in.WithCaching = true - in.Cache = w.Cache - in.CacheKeyFunc = w.CacheKeyFunc -} - -// CallExtensionOptions contains the options for the CallExtension call. -type CallExtensionOptions struct { - WithCaching bool - Cache cache.Cache[CallExtensionCacheEntry] - CacheKeyFunc func(*runtimeregistry.ExtensionRegistration, runtimehooksv1.RequestObject) string -} - // CallExtension makes the call to the extension with the given name. // The response object passed will be updated with the response of the call. // An error is returned if the extension is not compatible with the hook. @@ -328,9 +263,9 @@ type CallExtensionOptions struct { // Nb. FailurePolicy does not affect the following kinds of errors: // - Internal errors. Examples: hooks is incompatible with ExtensionHandler, ExtensionHandler information is missing. // - Error when ExtensionHandler returns a response with `Status` set to `Failure`. -func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject, opts ...CallExtensionOption) error { +func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject, opts ...runtimeclient.CallExtensionOption) error { // Calculate the options. - options := &CallExtensionOptions{} + options := &runtimeclient.CallExtensionOptions{} for _, opt := range opts { opt.ApplyToOptions(options) } @@ -380,7 +315,7 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, fo var cacheKey string if options.WithCaching { // Return a cached response if response is cached. - cacheKey = options.CacheKeyFunc(registration, request) + cacheKey = options.CacheKeyFunc(registration.Name, registration.ExtensionConfigResourceVersion, request) if cacheEntry, ok := options.Cache.Has(cacheKey); ok { // Set response to cacheEntry.Response. outVal := reflect.ValueOf(response) @@ -432,7 +367,7 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, fo if options.WithCaching { // Add response to the cache. - options.Cache.Add(CallExtensionCacheEntry{ + options.Cache.Add(runtimeclient.CallExtensionCacheEntry{ CacheKey: cacheKey, Response: response, }) diff --git a/internal/runtime/client/client_test.go b/internal/runtime/client/client_test.go index 5d868e36acea..72be50e66fac 100644 --- a/internal/runtime/client/client_test.go +++ b/internal/runtime/client/client_test.go @@ -40,11 +40,12 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry" fakev1alpha1 "sigs.k8s.io/cluster-api/internal/runtime/test/v1alpha1" fakev1alpha2 "sigs.k8s.io/cluster-api/internal/runtime/test/v1alpha2" - "sigs.k8s.io/cluster-api/internal/util/cache" + "sigs.k8s.io/cluster-api/util/cache" ) func TestClient_httpCall(t *testing.T) { @@ -808,9 +809,9 @@ func TestClient_CallExtension(t *testing.T) { // Call again with caching. serverCallCount = 0 - cache := cache.New[CallExtensionCacheEntry]() + cache := cache.New[runtimeclient.CallExtensionCacheEntry]() err = c.CallExtension(context.Background(), tt.args.hook, obj, tt.args.name, tt.args.request, tt.args.response, - WithCaching{Cache: cache, CacheKeyFunc: cacheKeyFunc}) + runtimeclient.WithCaching{Cache: cache, CacheKeyFunc: cacheKeyFunc}) if tt.wantErr { g.Expect(err).To(HaveOccurred()) } else { @@ -825,7 +826,7 @@ func TestClient_CallExtension(t *testing.T) { g.Expect(cacheEntry).ToNot(BeNil()) err = c.CallExtension(context.Background(), tt.args.hook, obj, tt.args.name, tt.args.request, tt.args.response, - WithCaching{Cache: cache, CacheKeyFunc: cacheKeyFunc}) + runtimeclient.WithCaching{Cache: cache, CacheKeyFunc: cacheKeyFunc}) // When we expect the response to be cached we always expect no errors. g.Expect(err).ToNot(HaveOccurred()) // As the response is cached we expect no further calls to the server. @@ -841,9 +842,9 @@ func TestClient_CallExtension(t *testing.T) { } } -func cacheKeyFunc(registration *runtimeregistry.ExtensionRegistration, request runtimehooksv1.RequestObject) string { - // Note: registration.Name is identical to the value of the name parameter passed into CallExtension. - s := fmt.Sprintf("%s-%s", registration.Name, registration.ExtensionConfigResourceVersion) +func cacheKeyFunc(extensionName, extensionConfigResourceVersion string, request runtimehooksv1.RequestObject) string { + // Note: extensionName is identical to the value of the name parameter passed into CallExtension. + s := fmt.Sprintf("%s-%s", extensionName, extensionConfigResourceVersion) for k, v := range request.GetSettings() { s += fmt.Sprintf(",%s=%s", k, v) } diff --git a/internal/runtime/client/fake/fake_client.go b/internal/runtime/client/fake/fake_client.go index c37efe186e2f..114eb261260c 100644 --- a/internal/runtime/client/fake/fake_client.go +++ b/internal/runtime/client/fake/fake_client.go @@ -26,8 +26,8 @@ import ( runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" ) // RuntimeClientBuilder is used to build a fake runtime client. diff --git a/main.go b/main.go index f2037700176d..59b7e3f9ce56 100644 --- a/main.go +++ b/main.go @@ -62,6 +62,7 @@ import ( expipamwebhooks "sigs.k8s.io/cluster-api/exp/ipam/webhooks" runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimecontrollers "sigs.k8s.io/cluster-api/exp/runtime/controllers" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" expwebhooks "sigs.k8s.io/cluster-api/exp/webhooks" @@ -72,7 +73,7 @@ import ( expv1alpha4 "sigs.k8s.io/cluster-api/internal/apis/core/exp/v1alpha4" clusterv1alpha3 "sigs.k8s.io/cluster-api/internal/apis/core/v1alpha3" clusterv1alpha4 "sigs.k8s.io/cluster-api/internal/apis/core/v1alpha4" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" + internalruntimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry" runtimewebhooks "sigs.k8s.io/cluster-api/internal/webhooks/runtime" "sigs.k8s.io/cluster-api/util/apiwarnings" @@ -459,7 +460,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchNamespaces map var runtimeClient runtimeclient.Client if feature.Gates.Enabled(feature.RuntimeSDK) { // This is the creation of the runtimeClient for the controllers, embedding a shared catalog and registry instance. - runtimeClient = runtimeclient.New(runtimeclient.Options{ + runtimeClient = internalruntimeclient.New(internalruntimeclient.Options{ Catalog: catalog, Registry: runtimeregistry.New(), Client: mgr.GetClient(), diff --git a/test/extension/handlers/topologymutation/handler_integration_test.go b/test/extension/handlers/topologymutation/handler_integration_test.go index c1bd5435980f..99a3c6accfae 100644 --- a/test/extension/handlers/topologymutation/handler_integration_test.go +++ b/test/extension/handlers/topologymutation/handler_integration_test.go @@ -50,11 +50,11 @@ import ( "sigs.k8s.io/cluster-api/controllers" runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" "sigs.k8s.io/cluster-api/exp/topology/desiredstate" "sigs.k8s.io/cluster-api/exp/topology/scope" "sigs.k8s.io/cluster-api/feature" - runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" v1beta2conditions "sigs.k8s.io/cluster-api/util/conditions/v1beta2" "sigs.k8s.io/cluster-api/util/contract" "sigs.k8s.io/cluster-api/webhooks" @@ -406,6 +406,8 @@ type TopologyMutationHook interface { ValidateTopology(ctx context.Context, req *runtimehooksv1.ValidateTopologyRequest, resp *runtimehooksv1.ValidateTopologyResponse) } +var _ runtimeclient.Client = &injectRuntimeClient{} + // injectRuntimeClient implements a runtimeclient.Client. // It allows us to plug a TopologyMutationHook into Cluster and ClusterClass controllers. type injectRuntimeClient struct { diff --git a/internal/util/cache/cache.go b/util/cache/cache.go similarity index 100% rename from internal/util/cache/cache.go rename to util/cache/cache.go diff --git a/internal/util/cache/cache_test.go b/util/cache/cache_test.go similarity index 100% rename from internal/util/cache/cache_test.go rename to util/cache/cache_test.go diff --git a/internal/util/cache/doc.go b/util/cache/doc.go similarity index 100% rename from internal/util/cache/doc.go rename to util/cache/doc.go