Andrey Smirnov 061ee8e57d
refactor: remove pre-ServerBinding leftovers, use remote cached clients
This PR has two major changes:

* retire the code which supported seamless migration from
pre-ServerBinding era to ServerBindings: creating `ServerBinding` on the
fly from the `MetalMachine` and `Server`; as there's no migration path
from pre-ServerBinding Sidero to the new version, it's time to drop it
* instead of creating workload cluster Kubernetes client each time, use
CAPI standard class to cache the client; the problem with "leaking"
clients is that HTTP/2 clients are almost never gc'ed, so they stay in
memory keeping an open connection with keepalives going both ways, so
caching lowers the load both on the controller and the control plane
endpoint

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2022-04-12 22:35:46 +03:00

201 lines
6.7 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package main
import (
"context"
"flag"
"os"
debug "github.com/talos-systems/go-debug"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
cgrecord "k8s.io/client-go/tools/record"
capiv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/controllers/remote"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
infrav1alpha2 "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha2"
infrav1alpha3 "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha3"
"github.com/talos-systems/sidero/app/caps-controller-manager/controllers"
metalv1alpha1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1"
// +kubebuilder:scaffold:imports
)
const (
defaultMaxConcurrentReconciles = 10
debugAddr = ":9994"
)
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
//nolint:wsl
func init() {
_ = clientgoscheme.AddToScheme(scheme)
_ = capiv1.AddToScheme(scheme)
_ = infrav1alpha2.AddToScheme(scheme)
_ = infrav1alpha3.AddToScheme(scheme)
_ = metalv1alpha1.AddToScheme(scheme)
// +kubebuilder:scaffold:scheme
}
func main() {
var (
metricsAddr string
healthAddr string
enableLeaderElection bool
webhookPort int
)
flag.StringVar(&metricsAddr, "metrics-bind-addr", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&healthAddr, "health-addr", ":9440", "The address the health endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "enable-leader-election", true,
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
flag.IntVar(&webhookPort, "webhook-port", 9443, "Webhook Server port, disabled by default. When enabled, the manager will only work as webhook server, no reconcilers are installed.")
flag.Parse()
ctrl.SetLogger(zap.New(func(o *zap.Options) {
o.Development = true
}))
go func() {
debugLogFunc := func(msg string) {
setupLog.Info(msg)
}
if err := debug.ListenAndServe(context.TODO(), debugAddr, debugLogFunc); err != nil {
setupLog.Error(err, "failed to start debug server")
os.Exit(1)
}
}()
// Machine and cluster operations can create enough events to trigger the event recorder spam filter
// Setting the burst size higher ensures all events will be recorded and submitted to the API
broadcaster := cgrecord.NewBroadcasterWithCorrelatorOptions(cgrecord.CorrelatorOptions{
BurstSize: 100,
})
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "controller-leader-election-capm",
Port: webhookPort,
EventBroadcaster: broadcaster,
HealthProbeBindAddress: healthAddr,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
ctx := ctrl.SetupSignalHandler()
// Set up a ClusterCacheTracker to provide to controllers
// requiring a connection to a remote cluster
tracker, err := remote.NewClusterCacheTracker(mgr, remote.ClusterCacheTrackerOptions{
Indexes: remote.DefaultIndexes,
ClientUncachedObjects: []client.Object{},
})
if err != nil {
setupLog.Error(err, "unable to create cluster cache tracker")
os.Exit(1)
}
if err := (&remote.ClusterCacheReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("remote").WithName("ClusterCacheReconciler"),
Tracker: tracker,
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: defaultMaxConcurrentReconciles}); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ClusterCacheReconciler")
os.Exit(1)
}
recorder := mgr.GetEventRecorderFor("caps-controller-manager")
if err = (&controllers.MetalClusterReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("MetalCluster"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: defaultMaxConcurrentReconciles}); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "MetalCluster")
os.Exit(1)
}
if err = (&controllers.MetalMachineReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("MetalMachine"),
Scheme: mgr.GetScheme(),
Recorder: recorder,
Tracker: tracker,
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: defaultMaxConcurrentReconciles}); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "MetalMachine")
os.Exit(1)
}
if err = (&controllers.ServerBindingReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("ServerBinding"),
Scheme: mgr.GetScheme(),
Recorder: recorder,
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: defaultMaxConcurrentReconciles}); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ServerBinding")
os.Exit(1)
}
// +kubebuilder:scaffold:builder
setupWebhooks(mgr)
setupChecks(mgr)
setupLog.Info("starting manager")
if err := mgr.Start(ctx); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}
func setupWebhooks(mgr ctrl.Manager) {
var err error
if err = (&infrav1alpha3.MetalCluster{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "MetalCluster")
os.Exit(1)
}
if err = (&infrav1alpha3.MetalMachine{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "MetalMachine")
os.Exit(1)
}
if err = (&infrav1alpha3.MetalMachineTemplate{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "MetalMachineTemplate")
os.Exit(1)
}
if err = (&infrav1alpha3.ServerBinding{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "ServerBinding")
os.Exit(1)
}
}
func setupChecks(mgr ctrl.Manager) {
if err := mgr.AddReadyzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil {
setupLog.Error(err, "unable to create ready check")
os.Exit(1)
}
if err := mgr.AddHealthzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil {
setupLog.Error(err, "unable to create health check")
os.Exit(1)
}
}