Skip to content

Commit 83fd682

Browse files
committed
feat: Add initial support for reconcile mode and update metrics
1 parent 2ba7ab6 commit 83fd682

File tree

7 files changed

+378
-77
lines changed

7 files changed

+378
-77
lines changed

api/proxmox/v1alpha1/constants.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package v1alpha1
2+
3+
const (
4+
5+
// ReconcileModeAnnotation is the annotation key for the reconcile mode
6+
// ReconcileMode is the mode of the reconciliation it could be Normal, WatchOnly, EnsureExists, Disable, DryRun (to be implemented)
7+
ReconcileModeAnnotation = "proxmox.alperen.cloud/reconcile-mode"
8+
ReconcileModeNormal = "Normal"
9+
ReconcileModeWatchOnly = "WatchOnly"
10+
ReconcileModeEnsureExists = "EnsureExists"
11+
ReconcileModeDisable = "Disable"
12+
ReconcileModeDryRun = "DryRun"
13+
)

cmd/main.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"context"
2021
"flag"
2122
"os"
23+
"time"
2224

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

2730
"k8s.io/apimachinery/pkg/runtime"
2831
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -34,6 +37,7 @@ import (
3437
proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1"
3538
proxmoxcontroller "github.com/alperencelik/kubemox/internal/controller/proxmox"
3639
_ "github.com/alperencelik/kubemox/pkg/kubernetes"
40+
"github.com/alperencelik/kubemox/pkg/metrics"
3741
"github.com/alperencelik/kubemox/pkg/proxmox"
3842
"github.com/alperencelik/kubemox/pkg/utils"
3943
//+kubebuilder:scaffold:imports
@@ -180,9 +184,22 @@ func main() {
180184
os.Exit(1)
181185
}
182186

187+
startMetricsUpdater(context.Background(), mgr.GetClient())
188+
183189
setupLog.Info("starting manager")
184190
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
185191
setupLog.Error(err, "problem running manager")
186192
os.Exit(1)
187193
}
188194
}
195+
196+
func startMetricsUpdater(ctx context.Context, kubeClient client.Client) {
197+
go func() {
198+
ticker := time.NewTicker(15 * time.Second)
199+
defer ticker.Stop()
200+
for range ticker.C {
201+
// Update metrics here
202+
metrics.UpdateProxmoxMetrics(ctx, kubeClient)
203+
}
204+
}()
205+
}

internal/controller/proxmox/managedvirtualmachine_controller.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import (
3535
"sigs.k8s.io/controller-runtime/pkg/predicate"
3636

3737
proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1"
38-
"github.com/alperencelik/kubemox/pkg/metrics"
3938
"github.com/alperencelik/kubemox/pkg/proxmox"
4039
)
4140

@@ -131,7 +130,6 @@ func (r *ManagedVirtualMachineReconciler) Reconcile(ctx context.Context, req ctr
131130
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
132131
}
133132
}
134-
metrics.DecManagedVirtualMachineCount()
135133
}
136134
logger.Info("Removing finalizer from ManagedVirtualMachine", "name", managedVM.Name)
137135
// Remove finalizer
@@ -207,9 +205,6 @@ func (r *ManagedVirtualMachineReconciler) handleManagedVMCreation(ctx context.Co
207205
return err
208206
}
209207
// Add metrics and events
210-
metrics.SetManagedVirtualMachineCPUCores(managedVM.Name, managedVM.Namespace, float64(managedVM.Spec.Cores))
211-
metrics.SetManagedVirtualMachineMemory(managedVM.Name, managedVM.Namespace, float64(managedVM.Spec.Memory))
212-
metrics.IncManagedVirtualMachineCount()
213208
r.Recorder.Event(managedVM, "Normal", "Created", fmt.Sprintf("ManagedVirtualMachine %s created", managedVM.Name))
214209
} else {
215210
logger.Info(fmt.Sprintf("ManagedVM %v already exists", managedVM))

internal/controller/proxmox/virtualmachine_controller.go

Lines changed: 84 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import (
3636

3737
proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1"
3838
"github.com/alperencelik/kubemox/pkg/kubernetes"
39-
"github.com/alperencelik/kubemox/pkg/metrics"
4039
"github.com/alperencelik/kubemox/pkg/proxmox"
4140
)
4241

@@ -81,6 +80,33 @@ func (r *VirtualMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reque
8180
if err != nil {
8281
return ctrl.Result{}, r.handleResourceNotFound(ctx, err)
8382
}
83+
reconcileMode := r.getReconcileMode(vm)
84+
85+
switch reconcileMode {
86+
case proxmoxv1alpha1.ReconcileModeWatchOnly:
87+
logger.Info(fmt.Sprintf("Reconciliation is watch only for VirtualMachine %s", vm.Name))
88+
r.handleWatcher(ctx, req, vm)
89+
return ctrl.Result{}, nil
90+
case proxmoxv1alpha1.ReconcileModeEnsureExists:
91+
logger.Info(fmt.Sprintf("Reconciliation is ensure exists for VirtualMachine %s", vm.Name))
92+
vmExists := proxmox.CheckVM(vm.Spec.Name, vm.Spec.NodeName)
93+
if !vmExists {
94+
// If not exists, create the VM
95+
logger.Info("Creating VirtualMachine", "name", vm.Spec.Name)
96+
err = r.CreateVirtualMachine(ctx, vm)
97+
if err != nil {
98+
logger.Error(err, "Error creating VirtualMachine")
99+
}
100+
}
101+
return ctrl.Result{}, nil
102+
case proxmoxv1alpha1.ReconcileModeDisable:
103+
// Disable the reconciliation
104+
logger.Info(fmt.Sprintf("Reconciliation is disabled for VirtualMachine %s", vm.Name))
105+
return ctrl.Result{}, nil
106+
default:
107+
// Normal mode
108+
break
109+
}
84110

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

@@ -98,37 +124,10 @@ func (r *VirtualMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reque
98124
// The object is being deleted
99125
if controllerutil.ContainsFinalizer(vm, virtualMachineFinalizerName) {
100126
// Delete the VM
101-
logger.Info(fmt.Sprintf("Deleting VirtualMachine %s", vm.Spec.Name))
102-
103-
// Update the condition for the VirtualMachine if it is not already deleting
104-
if !meta.IsStatusConditionPresentAndEqual(vm.Status.Conditions, typeDeletingVirtualMachine, metav1.ConditionUnknown) {
105-
meta.SetStatusCondition(&vm.Status.Conditions, metav1.Condition{
106-
Type: typeDeletingVirtualMachine,
107-
Status: metav1.ConditionUnknown,
108-
Reason: "Deleting",
109-
Message: "Deleting VirtualMachine",
110-
})
111-
if err = r.Status().Update(ctx, vm); err != nil {
112-
logger.Error(err, "Error updating VirtualMachine status")
113-
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
114-
}
115-
} else {
116-
return ctrl.Result{}, nil
117-
}
118-
// Stop the watcher if resource is being deleted
119-
if stopChan, exists := r.Watchers.Watchers[req.Name]; exists {
120-
close(stopChan)
121-
delete(r.Watchers.Watchers, req.Name)
122-
}
123-
// Perform all operations to delete the VM if the VM is not marked as deleting
124-
// TODO: Evaluate the requirement of check mechanism for VM whether it's already deleting
125-
r.DeleteVirtualMachine(ctx, vm)
126-
127-
// Remove finalizer
128-
logger.Info("Removing finalizer from VirtualMachine", "name", vm.Spec.Name)
129-
controllerutil.RemoveFinalizer(vm, virtualMachineFinalizerName)
130-
if err = r.Update(ctx, vm); err != nil {
131-
return ctrl.Result{}, nil
127+
res, delErr := r.handleDelete(ctx, req, vm)
128+
if delErr != nil {
129+
logger.Error(err, "Error handling VirtualMachine deletion")
130+
return res, client.IgnoreNotFound(delErr)
132131
}
133132
}
134133
// Stop reconciliation as the item is being deleted
@@ -166,7 +165,8 @@ func (r *VirtualMachineReconciler) SetupWithManager(mgr ctrl.Manager) error {
166165
newVM := e.ObjectNew.(*proxmoxv1alpha1.VirtualMachine)
167166
condition1 := !reflect.DeepEqual(oldVM.Spec, newVM.Spec)
168167
condition2 := newVM.ObjectMeta.GetDeletionTimestamp().IsZero()
169-
return condition1 || !condition2
168+
condition3 := !reflect.DeepEqual(oldVM.GetAnnotations(), newVM.GetAnnotations())
169+
return condition1 || !condition2 || condition3
170170
},
171171
}).
172172
WithOptions(controller.Options{MaxConcurrentReconciles: VMmaxConcurrentReconciles}).
@@ -197,7 +197,6 @@ func (r *VirtualMachineReconciler) handleVirtualMachineOperations(ctx context.Co
197197
}
198198
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
199199
}
200-
metrics.IncVirtualMachineCount()
201200
} else {
202201
// Check if auto start is enabled
203202
_, err = r.handleAutoStart(ctx, vm)
@@ -273,12 +272,10 @@ func (r *VirtualMachineReconciler) DeleteVirtualMachine(ctx context.Context, vm
273272
// Delete the VM
274273
r.Recorder.Event(vm, "Normal", "Deleting", fmt.Sprintf("VirtualMachine %s is being deleted", vm.Spec.Name))
275274
if vm.Spec.DeletionProtection {
276-
metrics.DecVirtualMachineCount()
277275
logger.Info(fmt.Sprintf("VirtualMachine %s is protected from deletion", vm.Spec.Name))
278276
return
279277
} else {
280278
proxmox.DeleteVM(vm.Spec.Name, vm.Spec.NodeName)
281-
metrics.DecVirtualMachineCount()
282279
}
283280
}
284281

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

365+
func (r *VirtualMachineReconciler) handleDelete(ctx context.Context, req ctrl.Request,
366+
vm *proxmoxv1alpha1.VirtualMachine) (ctrl.Result, error) {
367+
logger := log.FromContext(ctx)
368+
// Delete the VM
369+
logger.Info(fmt.Sprintf("Deleting VirtualMachine %s", vm.Spec.Name))
370+
371+
// Update the condition for the VirtualMachine if it is not already deleting
372+
if !meta.IsStatusConditionPresentAndEqual(vm.Status.Conditions, typeDeletingVirtualMachine, metav1.ConditionUnknown) {
373+
meta.SetStatusCondition(&vm.Status.Conditions, metav1.Condition{
374+
Type: typeDeletingVirtualMachine,
375+
Status: metav1.ConditionUnknown,
376+
Reason: "Deleting",
377+
Message: "Deleting VirtualMachine",
378+
})
379+
if err := r.Status().Update(ctx, vm); err != nil {
380+
logger.Error(err, "Error updating VirtualMachine status")
381+
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
382+
}
383+
} else {
384+
return ctrl.Result{}, nil
385+
}
386+
// Stop the watcher if resource is being deleted
387+
if stopChan, exists := r.Watchers.Watchers[req.Name]; exists {
388+
close(stopChan)
389+
delete(r.Watchers.Watchers, req.Name)
390+
}
391+
// Perform all operations to delete the VM if the VM is not marked as deleting
392+
// TODO: Evaluate the requirement of check mechanism for VM whether it's already deleting
393+
r.DeleteVirtualMachine(ctx, vm)
394+
395+
// Remove finalizer
396+
logger.Info("Removing finalizer from VirtualMachine", "name", vm.Spec.Name)
397+
controllerutil.RemoveFinalizer(vm, virtualMachineFinalizerName)
398+
if err := r.Update(ctx, vm); err != nil {
399+
return ctrl.Result{}, nil
400+
}
401+
return ctrl.Result{}, nil
402+
}
403+
368404
func (r *VirtualMachineReconciler) handleCloudInitOperations(ctx context.Context,
369405
vm *proxmoxv1alpha1.VirtualMachine) error {
370406
if proxmox.CheckVMType(vm) == proxmox.VirtualMachineTemplateType {
@@ -416,6 +452,18 @@ func (r *VirtualMachineReconciler) handleAdditionalConfig(ctx context.Context, v
416452
return nil
417453
}
418454

455+
func (r *VirtualMachineReconciler) getReconcileMode(vm *proxmoxv1alpha1.VirtualMachine) string {
456+
// Get the annotations and find out the reconcile mode
457+
annotations := vm.GetAnnotations()
458+
if annotations == nil {
459+
return "Normal"
460+
}
461+
if mode, ok := annotations[proxmoxv1alpha1.ReconcileModeAnnotation]; ok {
462+
return mode
463+
}
464+
return "Normal"
465+
}
466+
419467
func (r *VirtualMachineReconciler) handleWatcher(ctx context.Context, req ctrl.Request, vm *proxmoxv1alpha1.VirtualMachine) {
420468
r.Watchers.HandleWatcher(ctx, req, func(ctx context.Context, stopChan chan struct{}) (ctrl.Result, error) {
421469
return proxmox.StartWatcher(ctx, vm, stopChan, r.fetchResource, r.updateStatus,

0 commit comments

Comments
 (0)