From 715baadfe32a6f559aa735179a9798eb00136c24 Mon Sep 17 00:00:00 2001 From: Christoph Barbian Date: Thu, 31 Aug 2023 23:02:48 +0200 Subject: [PATCH] adopt controller-runtime 0.16.0 and update code generators --- Makefile | 4 +- api/v1alpha1/zz_generated.deepcopy.go | 1 - internal/controllers/suite_test.go | 26 ++++-- main.go | 45 +++++++---- pkg/client/clientset/versioned/clientset.go | 7 +- pkg/client/clientset/versioned/doc.go | 9 --- .../v1alpha1/fake/fake_masqueradingrule.go | 5 +- .../informers/externalversions/factory.go | 81 +++++++++++++++++-- 8 files changed, 133 insertions(+), 45 deletions(-) delete mode 100644 pkg/client/clientset/versioned/doc.go diff --git a/Makefile b/Makefile index d706bda..c2830eb 100644 --- a/Makefile +++ b/Makefile @@ -139,8 +139,8 @@ LISTER_GEN ?= $(shell pwd)/bin/lister-gen ## Tool Versions KUSTOMIZE_VERSION ?= v4.5.5 -CONTROLLER_TOOLS_VERSION ?= v0.9.2 -CODE_GENERATOR_VERSION ?= v0.23.4 +CONTROLLER_TOOLS_VERSION ?= v0.13.0 +CODE_GENERATOR_VERSION ?= v0.28.1 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index f5a5d7a..3ad9d30 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and dns-masquerading-operator contributors diff --git a/internal/controllers/suite_test.go b/internal/controllers/suite_test.go index 2ad3d28..7902547 100644 --- a/internal/controllers/suite_test.go +++ b/internal/controllers/suite_test.go @@ -42,6 +42,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" dnsv1alpha1 "github.com/sap/dns-masquerading-operator/api/v1alpha1" "github.com/sap/dns-masquerading-operator/internal/controllers" @@ -226,13 +228,25 @@ var _ = BeforeSuite(func() { By("creating manager") mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: "0", + Scheme: scheme, + Client: client.Options{ + Cache: &client.CacheOptions{ + DisableFor: []client.Object{ + &dnsv1alpha1.MasqueradingRule{}, + &corev1.ConfigMap{}, + }, + }, + }, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + Metrics: metricsserver.Options{ + BindAddress: "0", + }, HealthProbeBindAddress: "0", - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - ClientDisableCacheFor: []client.Object{&dnsv1alpha1.MasqueradingRule{}}}) + }) Expect(err).NotTo(HaveOccurred()) err = (&controllers.MasqueradingRuleReconciler{ diff --git a/main.go b/main.go index 62a833f..717a1f2 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" dnsv1alpha1 "github.com/sap/dns-masquerading-operator/api/v1alpha1" "github.com/sap/dns-masquerading-operator/internal/controllers" @@ -93,7 +95,7 @@ func main() { os.Exit(1) } - host, port, err := parseAddress(webhookAddr) + webhookHost, webhookPort, err := parseAddress(webhookAddr) if err != nil { setupLog.Error(err, "unable to parse webhook bind address") os.Exit(1) @@ -110,18 +112,27 @@ func main() { mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, - ClientDisableCacheFor: []client.Object{ - &dnsv1alpha1.MasqueradingRule{}, - &corev1.ConfigMap{}, + Client: client.Options{ + Cache: &client.CacheOptions{ + DisableFor: []client.Object{ + &dnsv1alpha1.MasqueradingRule{}, + &corev1.ConfigMap{}, + }, + }, }, - MetricsBindAddress: metricsAddr, - HealthProbeBindAddress: probeAddr, - Host: host, - Port: int(port), - LeaderElection: enableLeaderElection, - LeaderElectionNamespace: leaderElectionNamespace, - LeaderElectionID: LeaderElectionID, - CertDir: webhookCertDir, + LeaderElection: enableLeaderElection, + LeaderElectionNamespace: leaderElectionNamespace, + LeaderElectionID: LeaderElectionID, + LeaderElectionReleaseOnCancel: true, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookHost, + Port: webhookPort, + CertDir: webhookCertDir, + }), + Metrics: metricsserver.Options{ + BindAddress: metricsAddr, + }, + HealthProbeBindAddress: probeAddr, }) if err != nil { setupLog.Error(err, "unable to start manager") @@ -192,16 +203,16 @@ func main() { } } -func parseAddress(address string) (string, uint16, error) { +func parseAddress(address string) (string, int, error) { host, p, err := net.SplitHostPort(address) if err != nil { - return "", 0, err + return "", -1, err } - port, err := strconv.ParseUint(p, 10, 16) + port, err := strconv.Atoi(p) if err != nil { - return "", 0, err + return "", -1, err } - return host, uint16(port), nil + return host, port, nil } func checkInCluster() (bool, string, error) { diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 2297f24..125964f 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -22,8 +22,7 @@ type Interface interface { DnsV1alpha1() dnsv1alpha1.DnsV1alpha1Interface } -// 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 dnsV1alpha1 *dnsv1alpha1.DnsV1alpha1Client @@ -50,6 +49,10 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface { func NewForConfig(c *rest.Config) (*Clientset, error) { configShallowCopy := *c + if configShallowCopy.UserAgent == "" { + configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() + } + // share the transport between all clients httpClient, err := rest.HTTPClientFor(&configShallowCopy) if err != nil { diff --git a/pkg/client/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go deleted file mode 100644 index c1a458b..0000000 --- a/pkg/client/clientset/versioned/doc.go +++ /dev/null @@ -1,9 +0,0 @@ -/* -SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and dns-masquerading-operator contributors -SPDX-License-Identifier: Apache-2.0 -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/pkg/client/clientset/versioned/typed/dns.cs.sap.com/v1alpha1/fake/fake_masqueradingrule.go b/pkg/client/clientset/versioned/typed/dns.cs.sap.com/v1alpha1/fake/fake_masqueradingrule.go index 305b523..da3c086 100644 --- a/pkg/client/clientset/versioned/typed/dns.cs.sap.com/v1alpha1/fake/fake_masqueradingrule.go +++ b/pkg/client/clientset/versioned/typed/dns.cs.sap.com/v1alpha1/fake/fake_masqueradingrule.go @@ -13,7 +13,6 @@ import ( v1alpha1 "github.com/sap/dns-masquerading-operator/api/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" testing "k8s.io/client-go/testing" @@ -25,9 +24,9 @@ type FakeMasqueradingRules struct { ns string } -var masqueradingrulesResource = schema.GroupVersionResource{Group: "dns.cs.sap.com", Version: "v1alpha1", Resource: "masqueradingrules"} +var masqueradingrulesResource = v1alpha1.SchemeGroupVersion.WithResource("masqueradingrules") -var masqueradingrulesKind = schema.GroupVersionKind{Group: "dns.cs.sap.com", Version: "v1alpha1", Kind: "MasqueradingRule"} +var masqueradingrulesKind = v1alpha1.SchemeGroupVersion.WithKind("MasqueradingRule") // Get takes name of the masqueradingRule, and returns the corresponding masqueradingRule object, and an error if there is any. func (c *FakeMasqueradingRules) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.MasqueradingRule, err error) { diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 9383689..11aec10 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -36,6 +36,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. @@ -96,20 +101,39 @@ func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResy return factory } -// Start initializes all requested informers. 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 } } } -// WaitForCacheSync waits for all started informers' cache were synced. +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() +} + func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { informers := func() map[reflect.Type]cache.SharedIndexInformer { f.lock.Lock() @@ -131,7 +155,7 @@ func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[ref return res } -// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// InformerFor returns the SharedIndexInformer for obj using an internal // client. func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { f.lock.Lock() @@ -156,11 +180,58 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal // 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 := NewSharedInformerFactory(client, resyncPeriod) +// defer factory.WaitForStop() // 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 - 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{}) + + // 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() + + // WaitForCacheSync blocks until all started informers' caches were synced + // or the stop channel gets closed. WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + // ForResource gives generic access to a shared informer of the matching type. + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + + // InformerFor returns the SharedIndexInformer for obj using an internal + // client. + InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer + Dns() dnscssapcom.Interface }