-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding reconciler logic for baseboardmanagement controller
- Loading branch information
Showing
1 changed file
with
163 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,42 +18,193 @@ package controllers | |
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
utilerrors "k8s.io/apimachinery/pkg/util/errors" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/log" | ||
|
||
bmcv1alpha1 "github.com/tinkerbell/rufio/api/v1alpha1" | ||
bmcclient "github.com/tinkerbell/rufio/pkg/bmc/client" | ||
) | ||
|
||
// BaseboardManagementReconciler reconciles a BaseboardManagement object | ||
type BaseboardManagementReconciler struct { | ||
client.Client | ||
Scheme *runtime.Scheme | ||
Scheme *runtime.Scheme | ||
BMCClient bmcclient.BMCClient | ||
} | ||
|
||
//+kubebuilder:rbac:groups=bmc.tinkerbell.org,resources=baseboardmanagements,verbs=get;list;watch;create;update;patch;delete | ||
//+kubebuilder:rbac:groups=bmc.tinkerbell.org,resources=baseboardmanagements/status,verbs=get;update;patch | ||
//+kubebuilder:rbac:groups=bmc.tinkerbell.org,resources=baseboardmanagements/finalizers,verbs=update | ||
// +kubebuilder:rbac:groups="",resources=secrets;,verbs=get;list;watch | ||
|
||
// Reconcile is part of the main kubernetes reconciliation loop which aims to | ||
// move the current state of the cluster closer to the desired state. | ||
// TODO(user): Modify the Reconcile function to compare the state specified by | ||
// the BaseboardManagement object against the actual cluster state, and then | ||
// perform operations to make the cluster state reflect the state specified by | ||
// the user. | ||
// | ||
// For more details, check Reconcile and its Result here: | ||
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile | ||
// Reconcile ensures the state of a BaseboardManagement. | ||
// Gets the BaseboardManagement object and uses the SecretReference to initialize a BMC Client. | ||
// Ensures the BMC power is set to the desired state. | ||
// Updates the status and conditions accordingly. | ||
func (r *BaseboardManagementReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
_ = log.FromContext(ctx) | ||
logger := log.FromContext(ctx).WithValues("BaseboardManagement", req.NamespacedName) | ||
logger.Info("Reconciling BaseboardManagement", "name", req.NamespacedName) | ||
|
||
// TODO(user): your logic here | ||
// Fetch the BaseboardManagement object | ||
baseboardManagement := &bmcv1alpha1.BaseboardManagement{} | ||
err := r.Client.Get(ctx, req.NamespacedName, baseboardManagement) | ||
if err != nil { | ||
if apierrors.IsNotFound(err) { | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
logger.Error(err, "Failed to get BaseboardManagement", "name", req.NamespacedName) | ||
return ctrl.Result{}, fmt.Errorf("failed to get BaseboardManagement: %v", err) | ||
} | ||
|
||
// Deletion is a noop. | ||
if !baseboardManagement.DeletionTimestamp.IsZero() { | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
return r.reconcile(ctx, baseboardManagement) | ||
} | ||
|
||
func (r *BaseboardManagementReconciler) reconcile(ctx context.Context, bm *bmcv1alpha1.BaseboardManagement) (ctrl.Result, error) { | ||
logger := log.FromContext(ctx).WithValues("BaseboardManagement", bm.Name) | ||
|
||
// Fetching username, password from SecretReference | ||
username, password, err := r.resolveAuthSecretRef(ctx, bm.Spec.Connection.AuthSecretRef) | ||
if err != nil { | ||
return ctrl.Result{}, fmt.Errorf("resolving authentication from SecretReference: %v", err) | ||
} | ||
|
||
// Initializing BMC Client connection | ||
r.BMCClient.InitClient(bm.Spec.Connection.Host, "623", username, password) | ||
err = r.BMCClient.OpenConnection(ctx) | ||
if err != nil { | ||
logger.Error(err, "BMC connection failed", "host", bm.Spec.Connection.Host) | ||
result, setConditionErr := r.setCondition(ctx, bm, bmcv1alpha1.ConnectionError, err.Error()) | ||
if setConditionErr != nil { | ||
return result, utilerrors.NewAggregate([]error{fmt.Errorf("failed to set conditions: %v", setConditionErr), err}) | ||
} | ||
return result, err | ||
} | ||
|
||
// Close BMC connection after reconcilation | ||
defer func() { | ||
err = r.BMCClient.CloseConnection(ctx) | ||
if err != nil { | ||
logger.Error(err, "BMC close connection failed", "host", bm.Spec.Connection.Host) | ||
} | ||
}() | ||
|
||
logger.Info("Checking power status for baseboard management") | ||
powerStatus, err := r.BMCClient.GetPowerStatus(ctx) | ||
if err != nil { | ||
logger.Error(err, "Failed to reconcile BaseboardManagement power", "host", bm.Spec.Connection.Host) | ||
return ctrl.Result{}, err | ||
} | ||
|
||
if bm.Spec.Power == bmcv1alpha1.PowerState(strings.ToLower(powerStatus)) { | ||
logger.Info("Baseboard management in desired power state") | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
// Setting baseboard management to desired power state | ||
err = r.BMCClient.SetPowerState(ctx, string(bm.Spec.Power)) | ||
if err != nil { | ||
logger.Error(err, "Failed to reconcile BaseboardManagement power", "host", bm.Spec.Connection.Host) | ||
return ctrl.Result{}, err | ||
} | ||
|
||
return r.reconcileStatus(ctx, bm) | ||
} | ||
|
||
// setCondition updates the status.Condition if the condition type is present. | ||
// Appends if new condition is found. | ||
// Patches the BaseboardManagement status. | ||
func (r *BaseboardManagementReconciler) setCondition(ctx context.Context, bm *bmcv1alpha1.BaseboardManagement, cType bmcv1alpha1.BaseboardManagementConditionType, message string) (ctrl.Result, error) { | ||
patch := client.MergeFrom(bm.DeepCopy()) | ||
|
||
currentConditions := bm.Status.Conditions | ||
for i := range currentConditions { | ||
// If condition exists, update the message if different | ||
if currentConditions[i].Type == cType { | ||
if currentConditions[i].Message != message { | ||
bm.Status.Conditions[i].Message = message | ||
return r.patchStatus(ctx, bm, patch) | ||
} | ||
return ctrl.Result{}, nil | ||
} | ||
} | ||
|
||
// Append new condition to Conditions | ||
condition := bmcv1alpha1.BaseboardManagementCondition{ | ||
Type: cType, | ||
Message: message, | ||
} | ||
bm.Status.Conditions = append(bm.Status.Conditions, condition) | ||
|
||
return r.patchStatus(ctx, bm, patch) | ||
} | ||
|
||
// reconcileStatus updates the Power and Conditions and patches BaseboardManagement status. | ||
func (r *BaseboardManagementReconciler) reconcileStatus(ctx context.Context, bm *bmcv1alpha1.BaseboardManagement) (ctrl.Result, error) { | ||
patch := client.MergeFrom(bm.DeepCopy()) | ||
|
||
// Update the power status of the BaseboardManagement | ||
bm.Status.Power = bm.Spec.Power | ||
// Clear conditions | ||
bm.Status.Conditions = []bmcv1alpha1.BaseboardManagementCondition{} | ||
|
||
return r.patchStatus(ctx, bm, patch) | ||
} | ||
|
||
// patchStatus patches the specifies patch on the BaseboardManagement. | ||
func (r *BaseboardManagementReconciler) patchStatus(ctx context.Context, bm *bmcv1alpha1.BaseboardManagement, patch client.Patch) (ctrl.Result, error) { | ||
logger := log.FromContext(ctx).WithValues("BaseboardManagement", bm.Name) | ||
|
||
err := r.Client.Status().Patch(ctx, bm, patch) | ||
if err != nil { | ||
logger.Error(err, "Failed to patch BaseboardManagement status") | ||
return ctrl.Result{}, fmt.Errorf("failed to patch BaseboardManagement status: %v", err) | ||
} | ||
|
||
return ctrl.Result{}, nil | ||
} | ||
|
||
// resolveAuthSecretRef Gets the Secret from the SecretReference. | ||
// Returns the username and password encoded in the Secret. | ||
func (r *BaseboardManagementReconciler) resolveAuthSecretRef(ctx context.Context, secretRef corev1.SecretReference) (string, string, error) { | ||
secret := &corev1.Secret{} | ||
key := types.NamespacedName{Namespace: secretRef.Namespace, Name: secretRef.Name} | ||
|
||
if err := r.Client.Get(ctx, key, secret); err != nil { | ||
if apierrors.IsNotFound(err) { | ||
return "", "", fmt.Errorf("secret %s not found: %v", key, err) | ||
} | ||
|
||
return "", "", fmt.Errorf("failed to retrieve secret %s : %v", secretRef, err) | ||
} | ||
|
||
username, ok := secret.Data["username"] | ||
if !ok { | ||
return "", "", fmt.Errorf("required secret key username not present") | ||
} | ||
|
||
password, ok := secret.Data["password"] | ||
if !ok { | ||
return "", "", fmt.Errorf("required secret key password not present") | ||
} | ||
|
||
return string(username), string(password), nil | ||
} | ||
|
||
// SetupWithManager sets up the controller with the Manager. | ||
func (r *BaseboardManagementReconciler) SetupWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewControllerManagedBy(mgr). | ||
|