Skip to content

Commit

Permalink
Create Machines from OOBs
Browse files Browse the repository at this point in the history
  • Loading branch information
Gchbg committed May 7, 2024
1 parent 61cace4 commit 54011b1
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 15 deletions.
3 changes: 2 additions & 1 deletion api/v1alpha1/machine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type MachineSpec struct {

OOBRef v1.LocalObjectReference `json:"oobRef"`

// +optional
InventoryRef *v1.LocalObjectReference `json:"inventoryRef,omitempty"`

// +optional
Expand Down Expand Up @@ -116,7 +117,7 @@ const (
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:printcolumn:name="UUID",type=string,JSONPath=`.status.uuid`
// +kubebuilder:printcolumn:name="UUID",type=string,JSONPath=`.spec.uuid`
// +kubebuilder:printcolumn:name="Manufacturer",type=string,JSONPath=`.status.manufacturer`
// +kubebuilder:printcolumn:name="SKU",type=string,JSONPath=`.status.sku`,priority=100
// +kubebuilder:printcolumn:name="SerialNumber",type=string,JSONPath=`.status.serialNumber`,priority=100
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/metal.ironcore.dev_machines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ spec:
scope: Cluster
versions:
- additionalPrinterColumns:
- jsonPath: .status.uuid
- jsonPath: .spec.uuid
name: UUID
type: string
- jsonPath: .status.manufacturer
Expand Down
16 changes: 14 additions & 2 deletions internal/controller/indexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,28 @@ import (

func CreateIndexes(ctx context.Context, mgr manager.Manager) error {
indexer := mgr.GetFieldIndexer()
var err error

err := indexer.IndexField(ctx, &metalv1alpha1.MachineClaim{}, MachineClaimSpecMachineRef, func(obj client.Object) []string {
err = indexer.IndexField(ctx, &metalv1alpha1.Machine{}, MachineSpecOOBRefName, func(obj client.Object) []string {
machine := obj.(*metalv1alpha1.Machine)
if machine.Spec.OOBRef.Name == "" {
return nil
}
return []string{machine.Spec.OOBRef.Name}
})
if err != nil {
return fmt.Errorf("cannot index field %s: %w", MachineSpecOOBRefName, err)
}

err = indexer.IndexField(ctx, &metalv1alpha1.MachineClaim{}, MachineClaimSpecMachineRefName, func(obj client.Object) []string {
claim := obj.(*metalv1alpha1.MachineClaim)
if claim.Spec.MachineRef == nil || claim.Spec.MachineRef.Name == "" {
return nil
}
return []string{claim.Spec.MachineRef.Name}
})
if err != nil {
return fmt.Errorf("cannot index field %s: %w", MachineClaimSpecMachineRef, err)
return fmt.Errorf("cannot index field %s: %w", MachineClaimSpecMachineRefName, err)
}

err = indexer.IndexField(ctx, &metalv1alpha1.OOB{}, OOBSpecMACAddress, func(obj client.Object) []string {
Expand Down
6 changes: 6 additions & 0 deletions internal/controller/machine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ import (
// +kubebuilder:rbac:groups=metal.ironcore.dev,resources=machines/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=metal.ironcore.dev,resources=machines/finalizers,verbs=update

const (
MachineFieldManager = "metal.ironcore.dev/machine"
MachineFinalizer = "metal.ironcore.dev/machine"
MachineSpecOOBRefName = ".spec.oobRef.Name"
)

func NewMachineReconciler() (*MachineReconciler, error) {
return &MachineReconciler{}, nil
}
Expand Down
10 changes: 6 additions & 4 deletions internal/controller/machineclaim_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ import (
// +kubebuilder:rbac:groups=metal.ironcore.dev,resources=machines/finalizers,verbs=update

const (
MachineClaimFieldManager = "metal.ironcore.dev/machineclaim"
MachineClaimFinalizer = "metal.ironcore.dev/machineclaim"
MachineClaimSpecMachineRef = ".spec.machineRef.Name"
MachineClaimFieldManager = "metal.ironcore.dev/machineclaim"
MachineClaimFinalizer = "metal.ironcore.dev/machineclaim"
MachineClaimSpecMachineRefName = ".spec.machineRef.Name"
)

func NewMachineClaimReconciler() (*MachineClaimReconciler, error) {
Expand Down Expand Up @@ -393,7 +393,9 @@ func (r *MachineClaimReconciler) enqueueMachineClaimsFromMachine(ctx context.Con
machine := obj.(*metalv1alpha1.Machine)

claimList := metalv1alpha1.MachineClaimList{}
err := r.List(ctx, &claimList, client.MatchingFields{MachineClaimSpecMachineRef: machine.Name})
err := r.List(ctx, &claimList, client.MatchingFields{
MachineClaimSpecMachineRefName: machine.Name,
})
if err != nil {
log.Error(ctx, fmt.Errorf("cannot list MachineClaims: %w", err))
return nil
Expand Down
167 changes: 160 additions & 7 deletions internal/controller/oob_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const (
OOBErrorBadEndpoint = "BadEndpoint"
OOBErrorBadCredentials = "BadCredentials"
OOBErrorBadInfo = "BadInfo"
OOBErrorBadMachines = "BadMachines"
)

func NewOOBReconciler(systemNamespace, ipLabelSelector, macDB string, credsRenewalBeforeExpiry time.Duration, usernamePrefix, temporaryPasswordSecret string) (*OOBReconciler, error) {
Expand Down Expand Up @@ -132,6 +133,7 @@ type access struct {

type ctxkOOBHost struct{}
type ctxkBMC struct{}
type ctxkInfo struct{}

func (r *OOBReconciler) PreStart(ctx context.Context) error {
return r.ensureTemporaryPassword(ctx)
Expand Down Expand Up @@ -168,6 +170,16 @@ func (r *OOBReconciler) finalize(ctx context.Context, oob *metalv1alpha1.OOB) er
return err
}

err = r.finalizeSecret(ctx, oob)
if err != nil {
return err
}

err = r.finalizeMachines(ctx, oob)
if err != nil {
return err
}

log.Debug(ctx, "Removing finalizer")
var apply *metalv1alpha1apply.OOBApplyConfiguration
apply, err = metalv1alpha1apply.ExtractOOB(oob, OOBFieldManager)
Expand Down Expand Up @@ -250,6 +262,25 @@ func (r *OOBReconciler) finalizeSecret(ctx context.Context, oob *metalv1alpha1.O
return nil
}

func (r *OOBReconciler) finalizeMachines(ctx context.Context, oob *metalv1alpha1.OOB) error {
var machineList metalv1alpha1.MachineList
err := r.List(ctx, &machineList, client.MatchingFields{
MachineSpecOOBRefName: oob.Name,
})
if err != nil {
return fmt.Errorf("cannot list Machines: %w", err)
}
for _, m := range machineList.Items {
log.Info(ctx, "Deleting machine", "machine", m.Name)
err = r.Delete(ctx, &m)
if err != nil {
return fmt.Errorf("cannot delete Machine: %w", err)
}
}

return nil
}

func (r *OOBReconciler) reconcile(ctx context.Context, oob *metalv1alpha1.OOB) (ctrl.Result, error) {
log.Debug(ctx, "Reconciling")

Expand Down Expand Up @@ -301,6 +332,15 @@ func (r *OOBReconciler) reconcile(ctx context.Context, oob *metalv1alpha1.OOB) (
return ctrl.Result{}, err
}

ctx, advance, err = r.runPhase(ctx, oob, oobRecPhase{
name: "Machines",
run: r.processMachines,
errType: OOBErrorBadMachines,
})
if !advance {
return ctrl.Result{}, err
}

ctx, advance, err = r.runPhase(ctx, oob, oobRecPhase{
name: "Ready",
run: r.processReady,
Expand Down Expand Up @@ -496,7 +536,7 @@ func (r *OOBReconciler) processEndpoint(ctx context.Context, oob *metalv1alpha1.
var ipList ipamv1alpha1.IPList
err := r.List(ctx, &ipList, client.MatchingLabelsSelector{Selector: r.ipLabelSelector}, client.MatchingLabels{OOBIPMacLabel: oob.Spec.MACAddress})
if err != nil {
return ctx, nil, nil, fmt.Errorf("cannot list OOBs: %w", err)
return ctx, nil, nil, fmt.Errorf("cannot list IPs: %w", err)
}

found := false
Expand Down Expand Up @@ -632,7 +672,9 @@ func (r *OOBReconciler) processCredentials(ctx context.Context, oob *metalv1alph
}
if oob.Spec.SecretRef == nil {
var secretList metalv1alpha1.OOBSecretList
err := r.List(ctx, &secretList, client.MatchingFields{OOBSecretSpecMACAddress: oob.Spec.MACAddress})
err := r.List(ctx, &secretList, client.MatchingFields{
OOBSecretSpecMACAddress: oob.Spec.MACAddress,
})
if err != nil {
return ctx, nil, nil, fmt.Errorf("cannot list OOBSecrets: %w", err)
}
Expand Down Expand Up @@ -866,7 +908,97 @@ func (r *OOBReconciler) processInfo(ctx context.Context, oob *metalv1alpha1.OOB)
WithFirmwareVersion(info.FirmwareVersion)
}

return ctx, nil, status, nil
return context.WithValue(ctx, ctxkInfo{}, info), nil, status, nil
}

func (r *OOBReconciler) processMachines(ctx context.Context, oob *metalv1alpha1.OOB) (context.Context, *metalv1alpha1apply.OOBApplyConfiguration, *metalv1alpha1apply.OOBStatusApplyConfiguration, error) {
info := ctx.Value(ctxkInfo{}).(bmc.Info)

type minfo struct {
m *metalv1alpha1.Machine
i bmc.Machine
}
machines := make(map[string]minfo, len(info.Machines))
if oob.Status.Type == metalv1alpha1.OOBTypeMachine {
for _, i := range info.Machines {
machines[i.UUID] = minfo{
m: nil,
i: i,
}
}
}

var machineList metalv1alpha1.MachineList
err := r.List(ctx, &machineList, client.MatchingFields{
MachineSpecOOBRefName: oob.Name,
})
if err != nil {
return ctx, nil, nil, fmt.Errorf("cannot list Machines: %w", err)
}
for _, m := range machineList.Items {
mi, ok := machines[m.Spec.UUID]
if !ok {
log.Info(ctx, "Deleting orphaned machine", "machine", m.Name)
err = r.Delete(ctx, &m)
if err != nil {
return ctx, nil, nil, fmt.Errorf("cannot delete Machine: %w", err)
}
continue
}

machine := &m
machines[m.Spec.UUID] = minfo{
m: machine,
i: mi.i,
}
}

oobRef := v1.LocalObjectReference{
Name: oob.Name,
}

for uuid, mi := range machines {
if mi.m == nil {
mi.m = &metalv1alpha1.Machine{
ObjectMeta: metav1.ObjectMeta{
Name: uuid,
},
}
machineApply := metalv1alpha1apply.Machine(mi.m.Name, "").WithSpec(metalv1alpha1apply.MachineSpec().
WithUUID(uuid).
WithOOBRef(oobRef))
log.Info(ctx, "Applying Machine", "machine", mi.m.Name)
err = r.Patch(ctx, mi.m, ssa.Apply(machineApply), client.FieldOwner(OOBFieldManager), client.ForceOwnership)
if err != nil {
return ctx, nil, nil, fmt.Errorf("cannot apply Machine: %w", err)
}
}

if mi.m.Status.Manufacturer != mi.i.Manufacturer ||
mi.m.Status.SKU != mi.i.SKU ||
mi.m.Status.SerialNumber != mi.i.SerialNumber ||
mi.m.Status.Power != metalv1alpha1.Power(mi.i.Power) ||
mi.m.Status.LocatorLED != metalv1alpha1.LED(mi.i.LocatorLED) {
var machineApply *metalv1alpha1apply.MachineApplyConfiguration
machineApply, err = metalv1alpha1apply.ExtractMachineStatus(mi.m, OOBFieldManager)
if err != nil {
return ctx, nil, nil, fmt.Errorf("cannot extract Machine status: %w", err)
}
machineApply = machineApply.WithStatus(util.Ensure(machineApply.Status).
WithManufacturer(mi.i.Manufacturer).
WithSKU(mi.i.SKU).
WithSerialNumber(mi.i.SerialNumber).
WithPower(metalv1alpha1.Power(mi.i.Power)).
WithLocatorLED(metalv1alpha1.LED(mi.i.LocatorLED)))
log.Info(ctx, "Applying Machine status", "machine", mi.m.Name)
err = r.Status().Patch(ctx, mi.m, ssa.Apply(machineApply), client.FieldOwner(OOBFieldManager), client.ForceOwnership)
if err != nil {
return ctx, nil, nil, fmt.Errorf("cannot apply Machine status: %w", err)
}
}
}

return ctx, nil, nil, nil
}

func (r *OOBReconciler) processReady(ctx context.Context, oob *metalv1alpha1.OOB) (context.Context, *metalv1alpha1apply.OOBApplyConfiguration, *metalv1alpha1apply.OOBStatusApplyConfiguration, error) {
Expand Down Expand Up @@ -896,6 +1028,11 @@ func (r *OOBReconciler) SetupWithManager(mgr ctrl.Manager) error {
return err
}

err = c.Watch(source.Kind(mgr.GetCache(), &metalv1alpha1.Machine{}), handler.EnqueueRequestsFromMapFunc(r.enqueueOOBFromMachine))
if err != nil {
return err
}

return mgr.Add(c)
}

Expand All @@ -916,8 +1053,10 @@ func (r *OOBReconciler) enqueueOOBFromIP(ctx context.Context, obj client.Object)
}
ctx = log.WithValues(ctx, "mac", mac)

oobList := metalv1alpha1.OOBList{}
err := r.List(ctx, &oobList, client.MatchingFields{OOBSpecMACAddress: mac})
var oobList metalv1alpha1.OOBList
err := r.List(ctx, &oobList, client.MatchingFields{
OOBSpecMACAddress: mac,
})
if err != nil {
log.Error(ctx, fmt.Errorf("cannot list OOBs: %w", err))
return nil
Expand Down Expand Up @@ -994,8 +1133,10 @@ func (r *OOBReconciler) enqueueOOBFromIP(ctx context.Context, obj client.Object)
func (r *OOBReconciler) enqueueOOBFromOOBSecret(ctx context.Context, obj client.Object) []reconcile.Request {
secret := obj.(*metalv1alpha1.OOBSecret)

oobList := metalv1alpha1.OOBList{}
err := r.List(ctx, &oobList, client.MatchingFields{OOBSpecMACAddress: secret.Spec.MACAddress})
var oobList metalv1alpha1.OOBList
err := r.List(ctx, &oobList, client.MatchingFields{
OOBSpecMACAddress: secret.Spec.MACAddress,
})
if err != nil {
log.Error(ctx, fmt.Errorf("cannot list OOBs: %w", err))
return nil
Expand All @@ -1015,6 +1156,18 @@ func (r *OOBReconciler) enqueueOOBFromOOBSecret(ctx context.Context, obj client.
return reqs
}

func (r *OOBReconciler) enqueueOOBFromMachine(_ context.Context, obj client.Object) []reconcile.Request {
machine := obj.(*metalv1alpha1.Machine)

return []reconcile.Request{
{
NamespacedName: types.NamespacedName{
Name: machine.Spec.OOBRef.Name,
},
},
}
}

func (r *OOBReconciler) ensureTemporaryPassword(ctx context.Context) error {
secret := v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Expand Down

0 comments on commit 54011b1

Please sign in to comment.