diff --git a/pkg/openapi/zz_generated.openapi.go b/pkg/openapi/zz_generated.openapi.go index dc7c93d6ea9..b12e04b0ee3 100644 --- a/pkg/openapi/zz_generated.openapi.go +++ b/pkg/openapi/zz_generated.openapi.go @@ -6119,13 +6119,6 @@ func schema_pkg_apis_meta_v1_ObjectMeta(ref common.ReferenceCallback) common.Ope }, }, }, - "clusterName": { - SchemaProps: spec.SchemaProps{ - Description: "Deprecated: ClusterName is a legacy field that was always cleared by the system and never used; it will be removed completely in 1.25.\n\nThe name in the go struct is changed to help clients detect accidental use.", - Type: []string{"string"}, - Format: "", - }, - }, "managedFields": { SchemaProps: spec.SchemaProps{ Description: "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", @@ -7035,7 +7028,7 @@ func schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref common.ReferenceCall return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this: {\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", + Description: "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", Type: []string{"object"}, }, }, @@ -7046,7 +7039,7 @@ func schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref common.ReferenceCallback return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type, like this: type MyAwesomeAPIObject struct {\n runtime.TypeMeta `json:\",inline\"`\n ... // other fields\n} func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind\n\nTypeMeta is provided here for convenience. You may use it directly from this package or define your own with the same fields.", + Description: "TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type, like this:\n\n\ttype MyAwesomeAPIObject struct {\n\t runtime.TypeMeta `json:\",inline\"`\n\t ... // other fields\n\t}\n\nfunc (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind\n\nTypeMeta is provided here for convenience. You may use it directly from this package or define your own with the same fields.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "apiVersion": { diff --git a/sdk/client/clientset/versioned/clientset.go b/sdk/client/clientset/versioned/clientset.go index 1f97d5ac2c4..89ebf104dd4 100644 --- a/sdk/client/clientset/versioned/clientset.go +++ b/sdk/client/clientset/versioned/clientset.go @@ -46,8 +46,7 @@ type Interface interface { WorkloadV1alpha1() workloadv1alpha1.WorkloadV1alpha1Interface } -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. +// Clientset contains the clients for groups. type Clientset struct { *discovery.DiscoveryClient apiresourceV1alpha1 *apiresourcev1alpha1.ApiresourceV1alpha1Client diff --git a/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/apiresource_client.go b/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/apiresource_client.go index fe190b1a9e1..6955984e365 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/apiresource_client.go +++ b/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/apiresource_client.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "github.com/kcp-dev/logicalcluster/v3" diff --git a/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/apiresourceimport.go b/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/apiresourceimport.go index c2a0f133604..51429da766d 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/apiresourceimport.go +++ b/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/apiresourceimport.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/negotiatedapiresource.go b/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/negotiatedapiresource.go index 36c2bb49c03..36039a9b64f 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/negotiatedapiresource.go +++ b/sdk/client/clientset/versioned/cluster/typed/apiresource/v1alpha1/fake/negotiatedapiresource.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apibinding.go b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apibinding.go index 1e723abd1fe..1989a31aa6c 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apibinding.go +++ b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apibinding.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiconversion.go b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiconversion.go index 351ebf3ea02..972db61520a 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiconversion.go +++ b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiconversion.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiexport.go b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiexport.go index 504abcf37dc..e6ae6aa9afe 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiexport.go +++ b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiexport.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiexportendpointslice.go b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiexportendpointslice.go index df3ad74c2eb..dd35fbe6e64 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiexportendpointslice.go +++ b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiexportendpointslice.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiresourceschema.go b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiresourceschema.go index 64dcc12497e..f2b504a20e6 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiresourceschema.go +++ b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apiresourceschema.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apis_client.go b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apis_client.go index 18de4830f63..31db28c5f71 100644 --- a/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apis_client.go +++ b/sdk/client/clientset/versioned/cluster/typed/apis/v1alpha1/fake/apis_client.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "github.com/kcp-dev/logicalcluster/v3" diff --git a/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/core_client.go b/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/core_client.go index 2c08991cfdf..19bc5757f3e 100644 --- a/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/core_client.go +++ b/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/core_client.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "github.com/kcp-dev/logicalcluster/v3" diff --git a/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/logicalcluster.go b/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/logicalcluster.go index 9b0f62ab6b4..92b680a0341 100644 --- a/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/logicalcluster.go +++ b/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/logicalcluster.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/shard.go b/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/shard.go index 8151f1aed22..d69df210523 100644 --- a/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/shard.go +++ b/sdk/client/clientset/versioned/cluster/typed/core/v1alpha1/fake/shard.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/location.go b/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/location.go index aff98ef34c5..3c53a12b126 100644 --- a/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/location.go +++ b/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/location.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/placement.go b/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/placement.go index 72a39e27bdf..cf5d56b4b80 100644 --- a/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/placement.go +++ b/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/placement.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/scheduling_client.go b/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/scheduling_client.go index f9a7e4ea2fa..b11d93d88e3 100644 --- a/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/scheduling_client.go +++ b/sdk/client/clientset/versioned/cluster/typed/scheduling/v1alpha1/fake/scheduling_client.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "github.com/kcp-dev/logicalcluster/v3" diff --git a/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/tenancy_client.go b/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/tenancy_client.go index 4706aae21d2..0af1f5e7c64 100644 --- a/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/tenancy_client.go +++ b/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/tenancy_client.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "github.com/kcp-dev/logicalcluster/v3" diff --git a/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/workspace.go b/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/workspace.go index 51ea8115c9d..6dffae0c05e 100644 --- a/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/workspace.go +++ b/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/workspace.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/workspacetype.go b/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/workspacetype.go index c2a59a2473f..8d47940eb1b 100644 --- a/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/workspacetype.go +++ b/sdk/client/clientset/versioned/cluster/typed/tenancy/v1alpha1/fake/workspacetype.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/partition.go b/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/partition.go index 52b17cb144f..2831638ec8a 100644 --- a/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/partition.go +++ b/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/partition.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/partitionset.go b/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/partitionset.go index 4d45927af3f..0bbdcb69eb5 100644 --- a/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/partitionset.go +++ b/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/partitionset.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/topology_client.go b/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/topology_client.go index a4166af14e5..f47795f076c 100644 --- a/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/topology_client.go +++ b/sdk/client/clientset/versioned/cluster/typed/topology/v1alpha1/fake/topology_client.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "github.com/kcp-dev/logicalcluster/v3" diff --git a/sdk/client/clientset/versioned/cluster/typed/workload/v1alpha1/fake/synctarget.go b/sdk/client/clientset/versioned/cluster/typed/workload/v1alpha1/fake/synctarget.go index 2c87523e082..9e5170d066a 100644 --- a/sdk/client/clientset/versioned/cluster/typed/workload/v1alpha1/fake/synctarget.go +++ b/sdk/client/clientset/versioned/cluster/typed/workload/v1alpha1/fake/synctarget.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/sdk/client/clientset/versioned/cluster/typed/workload/v1alpha1/fake/workload_client.go b/sdk/client/clientset/versioned/cluster/typed/workload/v1alpha1/fake/workload_client.go index 340a6b0a35a..ab4be1e189f 100644 --- a/sdk/client/clientset/versioned/cluster/typed/workload/v1alpha1/fake/workload_client.go +++ b/sdk/client/clientset/versioned/cluster/typed/workload/v1alpha1/fake/workload_client.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "github.com/kcp-dev/logicalcluster/v3" diff --git a/sdk/client/informers/externalversions/factory.go b/sdk/client/informers/externalversions/factory.go index cfbfcc73659..fa6b4e4a8cb 100644 --- a/sdk/client/informers/externalversions/factory.go +++ b/sdk/client/informers/externalversions/factory.go @@ -66,6 +66,11 @@ type sharedInformerFactory struct { // startedInformers is used for tracking which informers have been started. // This allows Start() to be called multiple times safely. startedInformers map[reflect.Type]bool + // wg tracks how many goroutines were started. + wg sync.WaitGroup + // shuttingDown is true when Shutdown has been called. It may still be running + // because it needs to wait for goroutines. + shuttingDown bool } // WithCustomResyncConfig sets a custom resync period for the specified informer types. @@ -122,14 +127,35 @@ func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { f.lock.Lock() defer f.lock.Unlock() + if f.shuttingDown { + return + } + for informerType, informer := range f.informers { if !f.startedInformers[informerType] { - go informer.Run(stopCh) + f.wg.Add(1) + // We need a new variable in each loop iteration, + // otherwise the goroutine would use the loop variable + // and that keeps changing. + informer := informer + go func() { + defer f.wg.Done() + informer.Run(stopCh) + }() f.startedInformers[informerType] = true } } } +func (f *sharedInformerFactory) Shutdown() { + f.lock.Lock() + f.shuttingDown = true + f.lock.Unlock() + + // Will return immediately if there is nothing to wait for. + f.wg.Wait() +} + // WaitForCacheSync waits for all started informers' cache were synced. func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { informers := func() map[reflect.Type]kcpcache.ScopeableSharedIndexInformer { @@ -152,8 +178,7 @@ func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[ref return res } -// InformerFor returns the SharedIndexInformer for obj using an internal -// client. +// InformerFor returns the SharedIndexInformer for obj. func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) kcpcache.ScopeableSharedIndexInformer { f.lock.Lock() defer f.lock.Unlock() @@ -176,18 +201,69 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal } type ScopedDynamicSharedInformerFactory interface { + // ForResource gives generic access to a shared informer of the matching type. ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + + // Start initializes all requested informers. They are handled in goroutines + // which run until the stop channel gets closed. Start(stopCh <-chan struct{}) } // SharedInformerFactory provides shared informers for resources in all known // API group versions. +// +// It is typically used like this: +// +// ctx, cancel := context.Background() +// defer cancel() +// factory := NewSharedInformerFactoryWithOptions(client, resyncPeriod) +// defer factory.Shutdown() // Returns immediately if nothing was started. +// genericInformer := factory.ForResource(resource) +// typedInformer := factory.SomeAPIGroup().V1().SomeType() +// factory.Start(ctx.Done()) // Start processing these informers. +// synced := factory.WaitForCacheSync(ctx.Done()) +// for v, ok := range synced { +// if !ok { +// fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) +// return +// } +// } +// +// // Creating informers can also be created after Start, but then +// // Start must be called again: +// anotherGenericInformer := factory.ForResource(resource) +// factory.Start(ctx.Done()) type SharedInformerFactory interface { internalinterfaces.SharedInformerFactory + Cluster(logicalcluster.Name) ScopedDynamicSharedInformerFactory + + // Start initializes all requested informers. They are handled in goroutines + // which run until the stop channel gets closed. + Start(stopCh <-chan struct{}) + + // Shutdown marks a factory as shutting down. At that point no new + // informers can be started anymore and Start will return without + // doing anything. + // + // In addition, Shutdown blocks until all goroutines have terminated. For that + // to happen, the close channel(s) that they were started with must be closed, + // either before Shutdown gets called or while it is waiting. + // + // Shutdown may be called multiple times, even concurrently. All such calls will + // block until all goroutines have terminated. + Shutdown() + + // ForResource gives generic access to a shared informer of the matching type. ForResource(resource schema.GroupVersionResource) (GenericClusterInformer, error) + + // WaitForCacheSync blocks until all started informers' caches were synced + // or the stop channel gets closed. WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + // InformerFor returns the SharedIndexInformer for obj. + InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) kcpcache.ScopeableSharedIndexInformer + Apiresource() apiresourceinformers.ClusterInterface Apis() apisinformers.ClusterInterface Core() coreinformers.ClusterInterface @@ -338,8 +414,7 @@ func (f *sharedScopedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) m return res } -// InformerFor returns the SharedIndexInformer for obj using an internal -// client. +// InformerFor returns the SharedIndexInformer for obj. func (f *sharedScopedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewScopedInformerFunc) cache.SharedIndexInformer { f.lock.Lock() defer f.lock.Unlock() diff --git a/test/e2e/fixtures/wildwest/client/clientset/versioned/clientset.go b/test/e2e/fixtures/wildwest/client/clientset/versioned/clientset.go index 18384cdfa31..62fd6c92c1c 100644 --- a/test/e2e/fixtures/wildwest/client/clientset/versioned/clientset.go +++ b/test/e2e/fixtures/wildwest/client/clientset/versioned/clientset.go @@ -34,8 +34,7 @@ type Interface interface { WildwestV1alpha1() wildwestv1alpha1.WildwestV1alpha1Interface } -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. +// Clientset contains the clients for groups. type Clientset struct { *discovery.DiscoveryClient wildwestV1alpha1 *wildwestv1alpha1.WildwestV1alpha1Client diff --git a/test/e2e/fixtures/wildwest/client/clientset/versioned/cluster/typed/wildwest/v1alpha1/fake/cowboy.go b/test/e2e/fixtures/wildwest/client/clientset/versioned/cluster/typed/wildwest/v1alpha1/fake/cowboy.go index 1d77f394f50..4cb3bc27f07 100644 --- a/test/e2e/fixtures/wildwest/client/clientset/versioned/cluster/typed/wildwest/v1alpha1/fake/cowboy.go +++ b/test/e2e/fixtures/wildwest/client/clientset/versioned/cluster/typed/wildwest/v1alpha1/fake/cowboy.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "context" diff --git a/test/e2e/fixtures/wildwest/client/clientset/versioned/cluster/typed/wildwest/v1alpha1/fake/wildwest_client.go b/test/e2e/fixtures/wildwest/client/clientset/versioned/cluster/typed/wildwest/v1alpha1/fake/wildwest_client.go index 6432099a32b..a55fade4c6b 100644 --- a/test/e2e/fixtures/wildwest/client/clientset/versioned/cluster/typed/wildwest/v1alpha1/fake/wildwest_client.go +++ b/test/e2e/fixtures/wildwest/client/clientset/versioned/cluster/typed/wildwest/v1alpha1/fake/wildwest_client.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by kcp code-generator. DO NOT EDIT. -package v1alpha1 +package fake import ( "github.com/kcp-dev/logicalcluster/v3" diff --git a/test/e2e/fixtures/wildwest/client/informers/externalversions/factory.go b/test/e2e/fixtures/wildwest/client/informers/externalversions/factory.go index 977b5b18a7d..c655fdfcbe7 100644 --- a/test/e2e/fixtures/wildwest/client/informers/externalversions/factory.go +++ b/test/e2e/fixtures/wildwest/client/informers/externalversions/factory.go @@ -60,6 +60,11 @@ type sharedInformerFactory struct { // startedInformers is used for tracking which informers have been started. // This allows Start() to be called multiple times safely. startedInformers map[reflect.Type]bool + // wg tracks how many goroutines were started. + wg sync.WaitGroup + // shuttingDown is true when Shutdown has been called. It may still be running + // because it needs to wait for goroutines. + shuttingDown bool } // WithCustomResyncConfig sets a custom resync period for the specified informer types. @@ -116,14 +121,35 @@ func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { f.lock.Lock() defer f.lock.Unlock() + if f.shuttingDown { + return + } + for informerType, informer := range f.informers { if !f.startedInformers[informerType] { - go informer.Run(stopCh) + f.wg.Add(1) + // We need a new variable in each loop iteration, + // otherwise the goroutine would use the loop variable + // and that keeps changing. + informer := informer + go func() { + defer f.wg.Done() + informer.Run(stopCh) + }() f.startedInformers[informerType] = true } } } +func (f *sharedInformerFactory) Shutdown() { + f.lock.Lock() + f.shuttingDown = true + f.lock.Unlock() + + // Will return immediately if there is nothing to wait for. + f.wg.Wait() +} + // WaitForCacheSync waits for all started informers' cache were synced. func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { informers := func() map[reflect.Type]kcpcache.ScopeableSharedIndexInformer { @@ -146,8 +172,7 @@ func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[ref return res } -// InformerFor returns the SharedIndexInformer for obj using an internal -// client. +// InformerFor returns the SharedIndexInformer for obj. func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) kcpcache.ScopeableSharedIndexInformer { f.lock.Lock() defer f.lock.Unlock() @@ -170,18 +195,69 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal } type ScopedDynamicSharedInformerFactory interface { + // ForResource gives generic access to a shared informer of the matching type. ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + + // Start initializes all requested informers. They are handled in goroutines + // which run until the stop channel gets closed. Start(stopCh <-chan struct{}) } // SharedInformerFactory provides shared informers for resources in all known // API group versions. +// +// It is typically used like this: +// +// ctx, cancel := context.Background() +// defer cancel() +// factory := NewSharedInformerFactoryWithOptions(client, resyncPeriod) +// defer factory.Shutdown() // Returns immediately if nothing was started. +// genericInformer := factory.ForResource(resource) +// typedInformer := factory.SomeAPIGroup().V1().SomeType() +// factory.Start(ctx.Done()) // Start processing these informers. +// synced := factory.WaitForCacheSync(ctx.Done()) +// for v, ok := range synced { +// if !ok { +// fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) +// return +// } +// } +// +// // Creating informers can also be created after Start, but then +// // Start must be called again: +// anotherGenericInformer := factory.ForResource(resource) +// factory.Start(ctx.Done()) type SharedInformerFactory interface { internalinterfaces.SharedInformerFactory + Cluster(logicalcluster.Name) ScopedDynamicSharedInformerFactory + + // Start initializes all requested informers. They are handled in goroutines + // which run until the stop channel gets closed. + Start(stopCh <-chan struct{}) + + // Shutdown marks a factory as shutting down. At that point no new + // informers can be started anymore and Start will return without + // doing anything. + // + // In addition, Shutdown blocks until all goroutines have terminated. For that + // to happen, the close channel(s) that they were started with must be closed, + // either before Shutdown gets called or while it is waiting. + // + // Shutdown may be called multiple times, even concurrently. All such calls will + // block until all goroutines have terminated. + Shutdown() + + // ForResource gives generic access to a shared informer of the matching type. ForResource(resource schema.GroupVersionResource) (GenericClusterInformer, error) + + // WaitForCacheSync blocks until all started informers' caches were synced + // or the stop channel gets closed. WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + // InformerFor returns the SharedIndexInformer for obj. + InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) kcpcache.ScopeableSharedIndexInformer + Wildwest() wildwestinformers.ClusterInterface } @@ -302,8 +378,7 @@ func (f *sharedScopedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) m return res } -// InformerFor returns the SharedIndexInformer for obj using an internal -// client. +// InformerFor returns the SharedIndexInformer for obj. func (f *sharedScopedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewScopedInformerFunc) cache.SharedIndexInformer { f.lock.Lock() defer f.lock.Unlock()