Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconcile mode and metrics #44

Merged
merged 2 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions api/proxmox/v1alpha1/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package v1alpha1

const (

// ReconcileModeAnnotation is the annotation key for the reconcile mode
// ReconcileMode is the mode of the reconciliation it could be Normal, WatchOnly, EnsureExists, Disable, DryRun (to be implemented)
ReconcileModeAnnotation = "proxmox.alperen.cloud/reconcile-mode"
ReconcileModeNormal = "Normal"
ReconcileModeWatchOnly = "WatchOnly"
ReconcileModeEnsureExists = "EnsureExists"
ReconcileModeDisable = "Disable"
ReconcileModeDryRun = "DryRun"
)
22 changes: 20 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ limitations under the License.
package main

import (
"context"
"flag"
"os"
"time"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
"sigs.k8s.io/controller-runtime/pkg/client"

"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
Expand All @@ -34,14 +37,16 @@ import (
proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1"
proxmoxcontroller "github.com/alperencelik/kubemox/internal/controller/proxmox"
_ "github.com/alperencelik/kubemox/pkg/kubernetes"
"github.com/alperencelik/kubemox/pkg/metrics"
"github.com/alperencelik/kubemox/pkg/proxmox"
"github.com/alperencelik/kubemox/pkg/utils"
//+kubebuilder:scaffold:imports
)

var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
metricsUpdateInterval = 30 * time.Second
)

func init() { //nolint:gochecknoinits // This is required by kubebuilder
Expand Down Expand Up @@ -180,9 +185,22 @@ func main() {
os.Exit(1)
}

startMetricsUpdater(context.Background(), mgr.GetClient())

setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}

func startMetricsUpdater(ctx context.Context, kubeClient client.Client) {
go func() {
ticker := time.NewTicker(metricsUpdateInterval * time.Second)
defer ticker.Stop()
for range ticker.C {
// Update metrics here
metrics.UpdateProxmoxMetrics(ctx, kubeClient)
}
}()
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"

proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1"
"github.com/alperencelik/kubemox/pkg/metrics"
"github.com/alperencelik/kubemox/pkg/proxmox"
)

Expand Down Expand Up @@ -131,7 +130,6 @@ func (r *ManagedVirtualMachineReconciler) Reconcile(ctx context.Context, req ctr
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
}
}
metrics.DecManagedVirtualMachineCount()
}
logger.Info("Removing finalizer from ManagedVirtualMachine", "name", managedVM.Name)
// Remove finalizer
Expand Down Expand Up @@ -207,9 +205,6 @@ func (r *ManagedVirtualMachineReconciler) handleManagedVMCreation(ctx context.Co
return err
}
// Add metrics and events
metrics.SetManagedVirtualMachineCPUCores(managedVM.Name, managedVM.Namespace, float64(managedVM.Spec.Cores))
metrics.SetManagedVirtualMachineMemory(managedVM.Name, managedVM.Namespace, float64(managedVM.Spec.Memory))
metrics.IncManagedVirtualMachineCount()
r.Recorder.Event(managedVM, "Normal", "Created", fmt.Sprintf("ManagedVirtualMachine %s created", managedVM.Name))
} else {
logger.Info(fmt.Sprintf("ManagedVM %v already exists", managedVM))
Expand Down
120 changes: 84 additions & 36 deletions internal/controller/proxmox/virtualmachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import (

proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1"
"github.com/alperencelik/kubemox/pkg/kubernetes"
"github.com/alperencelik/kubemox/pkg/metrics"
"github.com/alperencelik/kubemox/pkg/proxmox"
)

Expand Down Expand Up @@ -81,6 +80,33 @@ func (r *VirtualMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reque
if err != nil {
return ctrl.Result{}, r.handleResourceNotFound(ctx, err)
}
reconcileMode := r.getReconcileMode(vm)

switch reconcileMode {
case proxmoxv1alpha1.ReconcileModeWatchOnly:
logger.Info(fmt.Sprintf("Reconciliation is watch only for VirtualMachine %s", vm.Name))
r.handleWatcher(ctx, req, vm)
return ctrl.Result{}, nil
case proxmoxv1alpha1.ReconcileModeEnsureExists:
logger.Info(fmt.Sprintf("Reconciliation is ensure exists for VirtualMachine %s", vm.Name))
vmExists := proxmox.CheckVM(vm.Spec.Name, vm.Spec.NodeName)
if !vmExists {
// If not exists, create the VM
logger.Info("Creating VirtualMachine", "name", vm.Spec.Name)
err = r.CreateVirtualMachine(ctx, vm)
if err != nil {
logger.Error(err, "Error creating VirtualMachine")
}
}
return ctrl.Result{}, nil
case proxmoxv1alpha1.ReconcileModeDisable:
// Disable the reconciliation
logger.Info(fmt.Sprintf("Reconciliation is disabled for VirtualMachine %s", vm.Name))
return ctrl.Result{}, nil
default:
// Normal mode
break
}

logger.Info(fmt.Sprintf("Reconciling VirtualMachine %s", vm.Name))

Expand All @@ -98,37 +124,10 @@ func (r *VirtualMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reque
// The object is being deleted
if controllerutil.ContainsFinalizer(vm, virtualMachineFinalizerName) {
// Delete the VM
logger.Info(fmt.Sprintf("Deleting VirtualMachine %s", vm.Spec.Name))

// Update the condition for the VirtualMachine if it is not already deleting
if !meta.IsStatusConditionPresentAndEqual(vm.Status.Conditions, typeDeletingVirtualMachine, metav1.ConditionUnknown) {
meta.SetStatusCondition(&vm.Status.Conditions, metav1.Condition{
Type: typeDeletingVirtualMachine,
Status: metav1.ConditionUnknown,
Reason: "Deleting",
Message: "Deleting VirtualMachine",
})
if err = r.Status().Update(ctx, vm); err != nil {
logger.Error(err, "Error updating VirtualMachine status")
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
}
} else {
return ctrl.Result{}, nil
}
// Stop the watcher if resource is being deleted
if stopChan, exists := r.Watchers.Watchers[req.Name]; exists {
close(stopChan)
delete(r.Watchers.Watchers, req.Name)
}
// Perform all operations to delete the VM if the VM is not marked as deleting
// TODO: Evaluate the requirement of check mechanism for VM whether it's already deleting
r.DeleteVirtualMachine(ctx, vm)

// Remove finalizer
logger.Info("Removing finalizer from VirtualMachine", "name", vm.Spec.Name)
controllerutil.RemoveFinalizer(vm, virtualMachineFinalizerName)
if err = r.Update(ctx, vm); err != nil {
return ctrl.Result{}, nil
res, delErr := r.handleDelete(ctx, req, vm)
if delErr != nil {
logger.Error(err, "Error handling VirtualMachine deletion")
return res, client.IgnoreNotFound(delErr)
}
}
// Stop reconciliation as the item is being deleted
Expand Down Expand Up @@ -166,7 +165,8 @@ func (r *VirtualMachineReconciler) SetupWithManager(mgr ctrl.Manager) error {
newVM := e.ObjectNew.(*proxmoxv1alpha1.VirtualMachine)
condition1 := !reflect.DeepEqual(oldVM.Spec, newVM.Spec)
condition2 := newVM.ObjectMeta.GetDeletionTimestamp().IsZero()
return condition1 || !condition2
condition3 := !reflect.DeepEqual(oldVM.GetAnnotations(), newVM.GetAnnotations())
return condition1 || !condition2 || condition3
},
}).
WithOptions(controller.Options{MaxConcurrentReconciles: VMmaxConcurrentReconciles}).
Expand Down Expand Up @@ -197,7 +197,6 @@ func (r *VirtualMachineReconciler) handleVirtualMachineOperations(ctx context.Co
}
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
}
metrics.IncVirtualMachineCount()
} else {
// Check if auto start is enabled
_, err = r.handleAutoStart(ctx, vm)
Expand Down Expand Up @@ -273,12 +272,10 @@ func (r *VirtualMachineReconciler) DeleteVirtualMachine(ctx context.Context, vm
// Delete the VM
r.Recorder.Event(vm, "Normal", "Deleting", fmt.Sprintf("VirtualMachine %s is being deleted", vm.Spec.Name))
if vm.Spec.DeletionProtection {
metrics.DecVirtualMachineCount()
logger.Info(fmt.Sprintf("VirtualMachine %s is protected from deletion", vm.Spec.Name))
return
} else {
proxmox.DeleteVM(vm.Spec.Name, vm.Spec.NodeName)
metrics.DecVirtualMachineCount()
}
}

Expand Down Expand Up @@ -365,6 +362,45 @@ func (r *VirtualMachineReconciler) handleFinalizer(ctx context.Context, vm *prox
return nil
}

func (r *VirtualMachineReconciler) handleDelete(ctx context.Context, req ctrl.Request,
vm *proxmoxv1alpha1.VirtualMachine) (ctrl.Result, error) {
logger := log.FromContext(ctx)
// Delete the VM
logger.Info(fmt.Sprintf("Deleting VirtualMachine %s", vm.Spec.Name))

// Update the condition for the VirtualMachine if it is not already deleting
if !meta.IsStatusConditionPresentAndEqual(vm.Status.Conditions, typeDeletingVirtualMachine, metav1.ConditionUnknown) {
meta.SetStatusCondition(&vm.Status.Conditions, metav1.Condition{
Type: typeDeletingVirtualMachine,
Status: metav1.ConditionUnknown,
Reason: "Deleting",
Message: "Deleting VirtualMachine",
})
if err := r.Status().Update(ctx, vm); err != nil {
logger.Error(err, "Error updating VirtualMachine status")
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
}
} else {
return ctrl.Result{}, nil
}
// Stop the watcher if resource is being deleted
if stopChan, exists := r.Watchers.Watchers[req.Name]; exists {
close(stopChan)
delete(r.Watchers.Watchers, req.Name)
}
// Perform all operations to delete the VM if the VM is not marked as deleting
// TODO: Evaluate the requirement of check mechanism for VM whether it's already deleting
r.DeleteVirtualMachine(ctx, vm)

// Remove finalizer
logger.Info("Removing finalizer from VirtualMachine", "name", vm.Spec.Name)
controllerutil.RemoveFinalizer(vm, virtualMachineFinalizerName)
if err := r.Update(ctx, vm); err != nil {
return ctrl.Result{}, nil
}
return ctrl.Result{}, nil
}

func (r *VirtualMachineReconciler) handleCloudInitOperations(ctx context.Context,
vm *proxmoxv1alpha1.VirtualMachine) error {
if proxmox.CheckVMType(vm) == proxmox.VirtualMachineTemplateType {
Expand Down Expand Up @@ -416,6 +452,18 @@ func (r *VirtualMachineReconciler) handleAdditionalConfig(ctx context.Context, v
return nil
}

func (r *VirtualMachineReconciler) getReconcileMode(vm *proxmoxv1alpha1.VirtualMachine) string {
// Get the annotations and find out the reconcile mode
annotations := vm.GetAnnotations()
if annotations == nil {
return "Normal"
}
if mode, ok := annotations[proxmoxv1alpha1.ReconcileModeAnnotation]; ok {
return mode
}
return "Normal"
}

func (r *VirtualMachineReconciler) handleWatcher(ctx context.Context, req ctrl.Request, vm *proxmoxv1alpha1.VirtualMachine) {
r.Watchers.HandleWatcher(ctx, req, func(ctx context.Context, stopChan chan struct{}) (ctrl.Result, error) {
return proxmox.StartWatcher(ctx, vm, stopChan, r.fetchResource, r.updateStatus,
Expand Down
Loading
Loading