diff --git a/controllers/deployment/controller.go b/controllers/deployment/controller.go index 6366b87..a9419aa 100644 --- a/controllers/deployment/controller.go +++ b/controllers/deployment/controller.go @@ -14,6 +14,7 @@ import ( "github.com/metal-stack/firewall-controller-manager/api/v2/defaults" "github.com/metal-stack/firewall-controller-manager/api/v2/validation" "github.com/metal-stack/firewall-controller-manager/controllers" + "github.com/metal-stack/firewall-controller-manager/controllers/monitor" ) type controller struct { @@ -21,6 +22,8 @@ type controller struct { log logr.Logger lastSetCreation map[string]time.Time recorder record.EventRecorder + + monitorScheduler *monitor.MonitorManagerScheduler } func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.Manager, c *config.ControllerConfig) error { diff --git a/controllers/deployment/delete.go b/controllers/deployment/delete.go index 646ffcf..81c95c0 100644 --- a/controllers/deployment/delete.go +++ b/controllers/deployment/delete.go @@ -9,6 +9,8 @@ import ( ) func (c *controller) Delete(r *controllers.Ctx[*v2.FirewallDeployment]) error { + c.monitorScheduler.Stop(r.Target) + ownedSets, _, err := controllers.GetOwnedResources(r.Ctx, c.c.GetSeedClient(), nil, r.Target, &v2.FirewallSetList{}, func(fsl *v2.FirewallSetList) []*v2.FirewallSet { return fsl.GetItems() }) @@ -21,8 +23,6 @@ func (c *controller) Delete(r *controllers.Ctx[*v2.FirewallDeployment]) error { func (c *controller) deleteFirewallSets(r *controllers.Ctx[*v2.FirewallDeployment], sets ...*v2.FirewallSet) error { for _, set := range sets { - set := set - if set.DeletionTimestamp != nil { r.Log.Info("deletion timestamp on firewall set already set", "firewall-name", set.Name) continue diff --git a/controllers/deployment/reconcile.go b/controllers/deployment/reconcile.go index fa6adaa..5de62a3 100644 --- a/controllers/deployment/reconcile.go +++ b/controllers/deployment/reconcile.go @@ -1,6 +1,7 @@ package deployment import ( + "context" "fmt" "strconv" "time" @@ -20,6 +21,13 @@ func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallDeployment]) error return err } + go func() { + err := c.monitorScheduler.StartIfNeeded(context.TODO(), r.Target) + if err != nil { + c.log.Error(err, "unable to start monitor scheduler") + } + }() + ownedSets, _, err := controllers.GetOwnedResources(r.Ctx, c.c.GetSeedClient(), nil, r.Target, &v2.FirewallSetList{}, func(fsl *v2.FirewallSetList) []*v2.FirewallSet { return fsl.GetItems() }) diff --git a/controllers/monitor/manager-state.go b/controllers/monitor/manager-state.go new file mode 100644 index 0000000..a92ec83 --- /dev/null +++ b/controllers/monitor/manager-state.go @@ -0,0 +1,106 @@ +package monitor + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/go-logr/logr" + v2 "github.com/metal-stack/firewall-controller-manager/api/v2" + "github.com/metal-stack/firewall-controller-manager/api/v2/config" + "github.com/metal-stack/metal-lib/pkg/pointer" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/metrics/server" +) + +type deploymentRef struct { + namespace string + name string +} + +func deploymentRefTo(deploy *v2.FirewallDeployment) deploymentRef { + return deploymentRef{ + namespace: deploy.Namespace, + name: deploy.Name, + } +} + +type MonitorManagerScheduler struct { + m sync.RWMutex + managers map[deploymentRef]context.CancelFunc + + scheme *runtime.Scheme + log logr.Logger + cc *config.NewControllerConfig + c *config.ControllerConfig +} + +func NewMonitorManagerState(log logr.Logger, c *config.ControllerConfig) *MonitorManagerScheduler { + return &MonitorManagerScheduler{ + managers: make(map[deploymentRef]context.CancelFunc), + } +} + +func (m *MonitorManagerScheduler) StartIfNeeded(ctx context.Context, deploy *v2.FirewallDeployment) error { + m.m.Lock() + defer m.m.Unlock() + + if _, exists := m.managers[deploymentRefTo(deploy)]; exists { + return nil + } + + ref := deploymentRefTo(deploy) + log := m.log.WithValues("namespace", ref.namespace, "name", ref.name) + + shootConfig, err := m.cc.ShootAccessHelper.RESTConfig(ctx) // TODO: adjust to fetch the kubeconfig + if err != nil { + return fmt.Errorf("unable to get shoot rest config %w", err) + } + + shootMgr, err := ctrl.NewManager(shootConfig, ctrl.Options{ + Scheme: m.scheme, + Metrics: server.Options{ + BindAddress: "0", + }, + LeaderElection: false, + Cache: cache.Options{ + DefaultNamespaces: map[string]cache.Config{ + v2.FirewallShootNamespace: {}, + }, + }, + GracefulShutdownTimeout: pointer.Pointer(time.Duration(0)), + }) + if err != nil { + return fmt.Errorf("unable to start firewall-controller-manager-monitor %w", err) + } + + err = SetupWithManager(log, shootMgr, m.c) + if err != nil { + return fmt.Errorf("unable to setup firewall-controller-manager-monitor %w", err) + } + + shootCtx, cancel := context.WithCancel(ctx) + + err = shootMgr.Start(shootCtx) + if err != nil { + cancel() + return fmt.Errorf("unable to start firewall-controller-manager-monitor %w", err) + } + + m.managers[ref] = cancel + return nil +} + +func (m *MonitorManagerScheduler) Stop(deploy *v2.FirewallDeployment) { + m.m.Lock() + defer m.m.Unlock() + + cancel, exists := m.managers[deploymentRefTo(deploy)] + if exists { + cancel() + delete(m.managers, deploymentRefTo(deploy)) + } +}