From cdf55e48c993e0288f4fc52add16ed285d147ce1 Mon Sep 17 00:00:00 2001 From: Artem Bortnikov Date: Thu, 13 Jun 2024 07:58:07 +0300 Subject: [PATCH] inventory controller Signed-off-by: Artem Bortnikov --- cmd/main.go | 25 ++++- internal/controller/inventory_controller.go | 61 ++++++++++++ internal/controller/machine_controller.go | 101 +++----------------- internal/controller/suite_test.go | 6 ++ 4 files changed, 101 insertions(+), 92 deletions(-) create mode 100644 internal/controller/inventory_controller.go diff --git a/cmd/main.go b/cmd/main.go index 515bd08..f16ea49 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -34,7 +34,7 @@ import ( "github.com/ironcore-dev/metal/internal/controller" "github.com/ironcore-dev/metal/internal/log" "github.com/ironcore-dev/metal/internal/namespace" - //+kubebuilder:scaffold:imports + // +kubebuilder:scaffold:imports ) type params struct { @@ -49,6 +49,7 @@ type params struct { enableMachineController bool enableMachineClaimController bool enableOOBController bool + enableInventoryController bool oobIpLabelSelector string oobMacDB string oobCredsRenewalBeforeExpiry time.Duration @@ -73,6 +74,7 @@ func parseCmdLine() params { pflag.Bool("enable-machine-controller", true, "Enable the Machine controller.") pflag.Bool("enable-machineclaim-controller", true, "Enable the MachineClaim controller.") pflag.Bool("enable-oob-controller", true, "Enable the OOB controller.") + pflag.Bool("enable-inventory-controller", true, "Enable the Inventory controller") pflag.String("oob-ip-label-selector", "", "OOB: Filter IP objects by labels.") pflag.String("oob-mac-db", "", "OOB: Load MAC DB from file.") pflag.Duration("oob-creds-renewal-before-expiry", time.Hour*24*7, "OOB: Renew expiring credentials this long before they expire.") @@ -171,7 +173,7 @@ func main() { exitCode = 1 return } - //+kubebuilder:scaffold:scheme + // +kubebuilder:scaffold:scheme var kcfg *rest.Config kcfg, err = ctrl.GetConfig() @@ -286,7 +288,24 @@ func main() { } } - //+kubebuilder:scaffold:builder + if p.enableInventoryController { + var inventoryReconciler *controller.InventoryReconciler + inventoryReconciler, err = controller.NewInventoryReconciler() + if err != nil { + log.Error(ctx, fmt.Errorf("cannot create controller: %w", err), "controller", "Inventory") + exitCode = 1 + return + } + + err = inventoryReconciler.SetupWithManager(mgr) + if err != nil { + log.Error(ctx, fmt.Errorf("cannot create controller: %w", err), "controller", "Inventory") + exitCode = 1 + return + } + } + + // +kubebuilder:scaffold:builder err = mgr.AddHealthzCheck("health", healthz.Ping) if err != nil { diff --git a/internal/controller/inventory_controller.go b/internal/controller/inventory_controller.go new file mode 100644 index 0000000..f3d3ccb --- /dev/null +++ b/internal/controller/inventory_controller.go @@ -0,0 +1,61 @@ +package controller + +import ( + "context" + "fmt" + "slices" + + metalv1alpha1 "github.com/ironcore-dev/metal/api/v1alpha1" + metalv1alpha1apply "github.com/ironcore-dev/metal/client/applyconfiguration/api/v1alpha1" + "github.com/ironcore-dev/metal/internal/ssa" + v1 "k8s.io/api/core/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func NewInventoryReconciler() (*InventoryReconciler, error) { + return &InventoryReconciler{}, nil +} + +type InventoryReconciler struct { + client.Client +} + +func (r *InventoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var inventory metalv1alpha1.Inventory + if err := r.Get(ctx, req.NamespacedName, &inventory); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(fmt.Errorf("cannot get Inventory: %w", err)) + } + if !inventory.DeletionTimestamp.IsZero() { + return ctrl.Result{}, nil + } + return ctrl.Result{}, r.reconcile(ctx, inventory) +} + +func (r *InventoryReconciler) reconcile(ctx context.Context, inventory metalv1alpha1.Inventory) error { + machines := &metalv1alpha1.MachineList{} + if err := r.List(ctx, machines); err != nil { + return err + } + idx := slices.IndexFunc(machines.Items, func(machine metalv1alpha1.Machine) bool { + return machine.Spec.InventoryRef == nil && machine.Spec.UUID == inventory.Name + }) + if idx == -1 { + return nil + } + machine := machines.Items[idx].DeepCopy() + machineApply := metalv1alpha1apply.Machine(machine.Name, machine.Namespace) + machineSpecApply := metalv1alpha1apply.MachineSpec(). + WithPower(metalv1alpha1.PowerOff). + WithInventoryRef(v1.LocalObjectReference{Name: inventory.Name}) + machineApply = machineApply.WithSpec(machineSpecApply) + return r.Patch(ctx, machine, ssa.Apply(machineApply), client.FieldOwner(MachineFieldOwner), client.ForceOwnership) +} + +func (r *InventoryReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.Client = mgr.GetClient() + + return ctrl.NewControllerManagedBy(mgr). + For(&metalv1alpha1.Inventory{}). + Complete(r) +} diff --git a/internal/controller/machine_controller.go b/internal/controller/machine_controller.go index 4519420..f6d55fe 100644 --- a/internal/controller/machine_controller.go +++ b/internal/controller/machine_controller.go @@ -118,14 +118,20 @@ func (r *MachineReconciler) reconcile( machineApply := metalv1alpha1apply.Machine(machine.Name, machine.Namespace) machineStatusApply := metalv1alpha1apply.MachineStatus() r.fillConditions(machine, machineStatusApply) - if machine.Spec.Maintenance { + switch { + case machine.Spec.Maintenance: machineStatusApply = machineStatusApply.WithState(metalv1alpha1.MachineStateMaintenance) - } else { + case machine.Spec.InventoryRef == nil && !slices.Contains(nonInitialStates, machine.Status.State): + if machine.Spec.Power != metalv1alpha1.PowerOn { + return machineApply.WithSpec(metalv1alpha1apply.MachineSpec().WithPower(metalv1alpha1.PowerOn)) + } + default: r.evaluateConditions(ctx, machine, machineStatusApply) r.evaluateCleanupRequired(machine, machineStatusApply) r.evaluateReadiness(machine, machineStatusApply) r.evaluateAvailability(machine, machineStatusApply) } + r.evaluateErrorState(machine, machineStatusApply) machineApply = machineApply.WithStatus(machineStatusApply) return machineApply @@ -556,7 +562,10 @@ func (r *MachineReconciler) SetupWithManager(mgr ctrl.Manager) error { return requests } for _, machine := range machineList.Items { - if machine.Spec.UUID == source.Name { + if machine.Spec.InventoryRef == nil { + continue + } + if machine.Spec.InventoryRef.Name == source.Name { requests = append(requests, reconcile.Request{ NamespacedName: types.NamespacedName{ Name: machine.GetName(), @@ -569,95 +578,9 @@ func (r *MachineReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -// func convertToApplyConfiguration(base, machine metalv1alpha1.Machine) *metalv1alpha1apply.MachineApplyConfiguration { -// machineApply := metalv1alpha1apply.Machine(machine.Name, machine.Namespace) -// specApply := metalv1alpha1apply.MachineSpec() -// if base.Spec.Power != machine.Spec.Power && machine.Spec.Power != "" { -// specApply = specApply.WithPower(machine.Spec.Power) -// } -// -// statusApply := metalv1alpha1apply.MachineStatus() -// if networkInterfacesChanged(base.Status.NetworkInterfaces, machine.Status.NetworkInterfaces) { -// nicApplyList := make([]*metalv1alpha1apply.MachineNetworkInterfaceApplyConfiguration, 0, len(machine.Status.NetworkInterfaces)) -// for _, nic := range machine.Status.NetworkInterfaces { -// nicApply := metalv1alpha1apply.MachineNetworkInterface(). -// WithName(nic.Name). -// WithMacAddress(nic.MacAddress) -// if nic.IPRef != nil { -// nicApply = nicApply.WithIPRef(*nic.IPRef) -// } -// if nic.SwitchRef != nil { -// nicApply = nicApply.WithSwitchRef(*nic.SwitchRef) -// } -// nicApplyList = append(nicApplyList, nicApply) -// } -// statusApply = statusApply.WithNetworkInterfaces(nicApplyList...) -// } -// if conditionsChanged(base.Status.Conditions, machine.Status.Conditions) { -// conditionsApply := make([]*v1.ConditionApplyConfiguration, 0, len(machine.Status.Conditions)) -// for _, c := range machine.Status.Conditions { -// conditionApply := v1.Condition(). -// WithType(c.Type). -// WithStatus(c.Status). -// WithReason(c.Reason). -// WithMessage(c.Message). -// WithLastTransitionTime(c.LastTransitionTime). -// WithObservedGeneration(c.ObservedGeneration) -// conditionsApply = append(conditionsApply, conditionApply) -// } -// statusApply = statusApply.WithConditions(conditionsApply...) -// } -// if base.Status.State != machine.Status.State { -// statusApply = statusApply.WithState(machine.Status.State) -// } -// -// return machineApply.WithSpec(specApply).WithStatus(statusApply) -// } - func convertMacAddress(src string) string { var mac = src mac = strings.ReplaceAll(mac, ":", "") mac = strings.ReplaceAll(mac, "-", "") return mac } - -// func resourceChanged(objOld, objNew metalv1alpha1.Machine) bool { -// return metadataChanged(objOld, objNew) || specChanged(objOld, objNew) -// } - -// func metadataChanged(objOld, objNew metalv1alpha1.Machine) bool { -// labelsChanged := !reflect.DeepEqual(objOld.GetLabels(), objNew.GetLabels()) -// annotationsChanged := !reflect.DeepEqual(objOld.GetAnnotations(), objNew.GetAnnotations()) -// finalizersChanged := !reflect.DeepEqual(objOld.GetFinalizers(), objNew.GetFinalizers()) -// return labelsChanged || annotationsChanged || finalizersChanged -// } - -// func specChanged(objOld, objNew metalv1alpha1.Machine) bool { -// oldSpec, _ := json.Marshal(objOld.Spec) -// newSpec, _ := json.Marshal(objNew.Spec) -// return !reflect.DeepEqual(oldSpec, newSpec) -// } - -// func subresourceChanged(objOld, objNew metalv1alpha1.Machine) bool { -// oldStatus, _ := json.Marshal(objOld.Status) -// newStatus, _ := json.Marshal(objNew.Status) -// return !reflect.DeepEqual(oldStatus, newStatus) -// } - -// func conditionsChanged(oldData, newData []metav1.Condition) bool { -// if len(oldData) != len(newData) { -// return true -// } -// oldConditions, _ := json.Marshal(oldData) -// newConditions, _ := json.Marshal(newData) -// return !reflect.DeepEqual(oldConditions, newConditions) -// } - -// func networkInterfacesChanged(oldData, newData []metalv1alpha1.MachineNetworkInterface) bool { -// if len(oldData) != len(newData) { -// return true -// } -// oldNetworkInterfaces, _ := json.Marshal(oldData) -// newNetworkInterfaces, _ := json.Marshal(newData) -// return !reflect.DeepEqual(oldNetworkInterfaces, newNetworkInterfaces) -// } diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 9f7dca4..b08eb60 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -139,6 +139,12 @@ var _ = BeforeSuite(func() { Expect(oobReconciler).NotTo(BeNil()) Expect(oobReconciler.SetupWithManager(mgr)).To(Succeed()) + var inventoryReconciler *InventoryReconciler + inventoryReconciler, err = NewInventoryReconciler() + Expect(err).NotTo(HaveOccurred()) + Expect(inventoryReconciler).NotTo(BeNil()) + Expect(inventoryReconciler.SetupWithManager(mgr)).To(Succeed()) + mgrCtx, mgrCancel := context.WithCancel(ctx) DeferCleanup(mgrCancel)