Skip to content

Commit

Permalink
Implement httpbootconfig controller
Browse files Browse the repository at this point in the history
  • Loading branch information
hardikdr committed May 6, 2024
1 parent 9b4c864 commit 104b77f
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 24 deletions.
12 changes: 7 additions & 5 deletions api/v1alpha1/httpbootconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@ type HTTPBootConfigSpec struct {

// HTTPBootConfigStatus defines the observed state of HTTPBootConfig
type HTTPBootConfigStatus struct {
State HTTPConfigState `json:"state,omitempty"`
State HTTPBootConfigState `json:"state,omitempty"`
}

type HTTPConfigState string
type HTTPBootConfigState string

const (
HTTPConfigStateReady HTTPConfigState = "Ready"
HTTPConfigStatePending HTTPConfigState = "Pending"
HTTPConfigStateError HTTPConfigState = "Error"
HTTPBootConfigStateReady HTTPBootConfigState = "Ready"
HTTPBootConfigStatePending HTTPBootConfigState = "Pending"
HTTPBootConfigStateError HTTPBootConfigState = "Error"
)

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="State",type=string,JSONPath=`.status.state`
//+kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// HTTPBootConfig is the Schema for the httpbootconfigs API
type HTTPBootConfig struct {
Expand Down
20 changes: 13 additions & 7 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (
// core controllers
ipxeBootConfigController = "ipxebootconfig"
serverBootConfigController = "serverbootconfig"
httpBootConfigController = "httpbootconfig"
)

func init() {
Expand Down Expand Up @@ -92,6 +93,7 @@ func main() {
// core controllers
ipxeBootConfigController,
serverBootConfigController,
httpBootConfigController,
)

flag.Var(controllers, "controllers",
Expand Down Expand Up @@ -184,17 +186,21 @@ func main() {
Scheme: mgr.GetScheme(),
IPXEServiceURL: ipxeServiceURL,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ServerBootConfiguration")
setupLog.Error(err, "unable to create controller", "controller", "ServerBootConfig")
os.Exit(1)
}
}
if err = (&controller.HTTPBootConfigReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "HTTPBootConfig")
os.Exit(1)

if controllers.Enabled(httpBootConfigController) {
if err = (&controller.HTTPBootConfigReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "HTTPBootConfig")
os.Exit(1)
}
}

//+kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand Down
9 changes: 8 additions & 1 deletion config/crd/bases/boot.ironcore.dev_httpbootconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ spec:
singular: httpbootconfig
scope: Namespaced
versions:
- name: v1alpha1
- additionalPrinterColumns:
- jsonPath: .status.state
name: State
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: HTTPBootConfig is the Schema for the httpbootconfigs API
Expand Down
123 changes: 112 additions & 11 deletions internal/controller/httpbootconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ package controller

import (
"context"
"fmt"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/go-logr/logr"
bootv1alpha1 "github.com/ironcore-dev/ipxe-operator/api/v1alpha1"
)

Expand All @@ -23,20 +29,69 @@ type HTTPBootConfigReconciler struct {
//+kubebuilder:rbac:groups=boot.ironcore.dev,resources=httpbootconfigs,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=boot.ironcore.dev,resources=httpbootconfigs/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=boot.ironcore.dev,resources=httpbootconfigs/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 HTTPBootConfig 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
func (r *HTTPBootConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
log := log.FromContext(ctx)

// TODO(user): your logic here
HTTPBootConfig := &bootv1alpha1.HTTPBootConfig{}
if err := r.Get(ctx, req.NamespacedName, HTTPBootConfig); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}

return r.reconcileExists(ctx, log, HTTPBootConfig)
}

func (r *HTTPBootConfigReconciler) reconcileExists(ctx context.Context, log logr.Logger, HTTPBootConfig *bootv1alpha1.HTTPBootConfig) (ctrl.Result, error) {
if !HTTPBootConfig.DeletionTimestamp.IsZero() {
return r.delete(ctx, log, HTTPBootConfig)
}

return r.reconcile(ctx, log, HTTPBootConfig)
}

func (r *HTTPBootConfigReconciler) reconcile(ctx context.Context, log logr.Logger, HTTPBootConfig *bootv1alpha1.HTTPBootConfig) (ctrl.Result, error) {
log.V(1).Info("Ensuring Ignition")
state, ignitionErr := r.ensureIgnition(ctx, log, HTTPBootConfig)
if ignitionErr != nil {
patchError := r.patchStatus(ctx, HTTPBootConfig, state)
if patchError != nil {
return ctrl.Result{}, fmt.Errorf("failed to patch status %w %w", ignitionErr, patchError)
}

log.V(1).Info("Failed to Ensure Ignition", "Error", ignitionErr)
return ctrl.Result{}, nil
}

patchErr := r.patchStatus(ctx, HTTPBootConfig, state)
if patchErr != nil {
return ctrl.Result{}, fmt.Errorf("failed to patch status %w", patchErr)
}

return ctrl.Result{}, nil
}

func (r *HTTPBootConfigReconciler) ensureIgnition(ctx context.Context, _ logr.Logger, HTTPBootConfig *bootv1alpha1.HTTPBootConfig) (bootv1alpha1.HTTPBootConfigState, error) {
// Verify if the IgnitionRef is set, and it has the intended data key.
if HTTPBootConfig.Spec.IgnitionSecretRef != nil {
IgnitionSecret := &corev1.Secret{}
if err := r.Get(ctx, client.ObjectKey{Name: HTTPBootConfig.Spec.IgnitionSecretRef.Name, Namespace: HTTPBootConfig.Namespace}, IgnitionSecret); err != nil {
return bootv1alpha1.HTTPBootConfigStateError, err
// TODO: Add some validation steps to ensure that the IgntionData is compliant, if necessary.
// Assume for now, that it's going to json format.
}
if IgnitionSecret.Data[bootv1alpha1.DefaultIgnitionKey] == nil {
return bootv1alpha1.HTTPBootConfigStateError, fmt.Errorf("ignition data is missing")
}
}

return bootv1alpha1.HTTPBootConfigStateReady, nil
}

func (r *HTTPBootConfigReconciler) delete(_ context.Context, log logr.Logger, HTTPBootConfig *bootv1alpha1.HTTPBootConfig) (ctrl.Result, error) {
log.V(1).Info("Deleting HTTPBootConfig")

// TODO

return ctrl.Result{}, nil
}
Expand All @@ -45,5 +100,51 @@ func (r *HTTPBootConfigReconciler) Reconcile(ctx context.Context, req ctrl.Reque
func (r *HTTPBootConfigReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&bootv1alpha1.HTTPBootConfig{}).
Watches(
&corev1.Secret{},
handler.EnqueueRequestsFromMapFunc(r.enqueueHTTPBootConfigReferencingIgnitionSecret),
).
Complete(r)
}

func (r *HTTPBootConfigReconciler) patchStatus(
ctx context.Context,
HTTPBootConfig *bootv1alpha1.HTTPBootConfig,
state bootv1alpha1.HTTPBootConfigState,
) error {
base := HTTPBootConfig.DeepCopy()
HTTPBootConfig.Status.State = state

if err := r.Status().Patch(ctx, HTTPBootConfig, client.MergeFrom(base)); err != nil {
return fmt.Errorf("error patching HTTPBootConfig: %w", err)
}
return nil
}

func (r *HTTPBootConfigReconciler) enqueueHTTPBootConfigReferencingIgnitionSecret(ctx context.Context, secret client.Object) []reconcile.Request {
log := log.Log.WithValues("secret", secret.GetName())
secretObj, ok := secret.(*corev1.Secret)
if !ok {
log.Error(nil, "cant decode object into Secret", secret)
return nil
}

list := &bootv1alpha1.HTTPBootConfigList{}
if err := r.Client.List(ctx, list, client.InNamespace(secretObj.Namespace)); err != nil {
log.Error(err, "failed to list HTTPBootConfig for secret", secret)
return nil
}

var requests []reconcile.Request
for _, HTTPBootConfig := range list.Items {
if HTTPBootConfig.Spec.IgnitionSecretRef != nil && HTTPBootConfig.Spec.IgnitionSecretRef.Name == secretObj.Name {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: HTTPBootConfig.Name,
Namespace: HTTPBootConfig.Namespace,
},
})
}
}
return requests
}

0 comments on commit 104b77f

Please sign in to comment.