diff --git a/api/proxmox/v1alpha1/constants.go b/api/proxmox/v1alpha1/constants.go new file mode 100644 index 0000000..8505a8c --- /dev/null +++ b/api/proxmox/v1alpha1/constants.go @@ -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" +) diff --git a/cmd/main.go b/cmd/main.go index 1df8ab7..fa03b33 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -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" @@ -34,6 +37,7 @@ 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 @@ -180,9 +184,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(15 * time.Second) + defer ticker.Stop() + for range ticker.C { + // Update metrics here + metrics.UpdateProxmoxMetrics(ctx, kubeClient) + } + }() +} diff --git a/internal/controller/proxmox/managedvirtualmachine_controller.go b/internal/controller/proxmox/managedvirtualmachine_controller.go index 8068604..c4893f9 100644 --- a/internal/controller/proxmox/managedvirtualmachine_controller.go +++ b/internal/controller/proxmox/managedvirtualmachine_controller.go @@ -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" ) @@ -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 @@ -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)) diff --git a/internal/controller/proxmox/virtualmachine_controller.go b/internal/controller/proxmox/virtualmachine_controller.go index a03b82f..06a7f22 100644 --- a/internal/controller/proxmox/virtualmachine_controller.go +++ b/internal/controller/proxmox/virtualmachine_controller.go @@ -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" ) @@ -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)) @@ -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 @@ -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}). @@ -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) @@ -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() } } @@ -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 { @@ -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, diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index d532e83..448165f 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -1,11 +1,18 @@ package metrics import ( + "context" + + proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1" + "github.com/alperencelik/kubemox/pkg/proxmox" "github.com/prometheus/client_golang/prometheus" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/metrics" ) var ( + // VirtualMachine virtualMachineCount = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "kubemox_virtual_machine_count", Help: "Number of virtualMachines registered in the cluster", @@ -18,6 +25,16 @@ var ( Name: "kubemox_virtual_machine_memory", Help: "Memory of virtualMachine as MB", }, []string{"name", "namespace"}) + virtualMachineRunningCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_running_count", + Help: "Number of running virtualMachines", + }) + virtualMachineStoppedCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_stopped_count", + Help: "Number of stopped virtualMachines", + }) + + // ManagedVirtualMachine ManagedVirtualMachineCount = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "kubemox_managed_virtual_machine_count", Help: "Number of managedVirtualMachines exists in Proxmox", @@ -30,45 +47,259 @@ var ( Name: "kubemox_managed_virtual_machine_memory", Help: "Memory of managedVirtualMachine as MB", }, []string{"name", "namespace"}) + ManagedVirtualMachineRunningCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_managed_virtual_machine_running_count", + Help: "Number of running managedVirtualMachines", + }) + ManagedVirtualMachineStoppedCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_managed_virtual_machine_stopped_count", + Help: "Number of stopped managedVirtualMachines", + }) + + // Container + containerCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_container_count", + Help: "Number of containers registered in the cluster", + }) + containerCPUCores = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "kubemox_container_cpu_cores", + Help: "Number of CPU cores of container", + }, []string{"name", "namespace"}) + containerMemory = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "kubemox_container_memory", + Help: "Memory of container as MB", + }, []string{"name", "namespace"}) + + // VirtualMachineTemplate + virtualMachineTemplateCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_template_count", + Help: "Number of virtualMachineTemplates registered in the cluster", + }) + virtualMachineTemplateCPUCores = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_template_cpu_cores", + Help: "Number of CPU cores of virtualMachineTemplate", + }, []string{"name", "namespace"}) + virtualMachineTemplateMemory = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_template_memory", + Help: "Memory of virtualMachineTemplate as MB", + }, []string{"name", "namespace"}) + + // VirtualMachineSet + virtualMachineSetCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_set_count", + Help: "Number of virtualMachineSets registered in the cluster", + }) + virtualMachineSetCPUCores = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_set_cpu_cores", + Help: "Number of CPU cores of virtualMachineSet", + }, []string{"name", "namespace"}) + virtualMachineSetMemory = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_set_memory", + Help: "Memory of virtualMachineSet as MB", + }, []string{"name", "namespace"}) + virtualMachineSetReplicas = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_set_replicas", + Help: "Number of replicas of virtualMachineSet", + }, []string{"name", "namespace"}) + + // VirtualMachineSnapshot + virtualMachineSnapshotCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_snapshot_count", + Help: "Number of virtualMachineSnapshots registered in the cluster", + }) + virtualMachineSnapshotPerVirtualMachineCount = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_snapshots", + Help: "Number of snapshots of virtualMachine", + }, []string{"name", "namespace"}) + + // VirtualMachineSnapshotPolicy + virtualMachineSnapshotPolicyCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_virtual_machine_snapshot_policy_count", + Help: "Number of virtualMachineSnapshotPolicies registered in the cluster", + }) + + // StorageDownloadURL + storageDownloadURLCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_storage_download_url_count", + Help: "Number of storageDownloadURLs registered in the cluster", + }) + + // CustomCertificate + customCertificateCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "kubemox_custom_certificate_count", + Help: "Number of customCertificates registered in the cluster", + }) ) -func init() { //nolint:gochecknoinits // This is required by kubebuilder - metrics.Registry.MustRegister(virtualMachineCount) - metrics.Registry.MustRegister(virtualMachineCPUCores) - metrics.Registry.MustRegister(virtualMachineMemory) - metrics.Registry.MustRegister(ManagedVirtualMachineCount) - metrics.Registry.MustRegister(ManagedVirtualMachineCPUCores) - metrics.Registry.MustRegister(ManagedVirtualMachineMemory) +var KubemoxMetrics = []prometheus.Collector{ + // VirtualMachine + virtualMachineCount, + virtualMachineCPUCores, + virtualMachineMemory, + virtualMachineRunningCount, + virtualMachineStoppedCount, + // ManagedVirtualMachine + ManagedVirtualMachineCount, + ManagedVirtualMachineCPUCores, + ManagedVirtualMachineMemory, + ManagedVirtualMachineRunningCount, + ManagedVirtualMachineStoppedCount, + // Container + containerCount, + containerCPUCores, + containerMemory, + // VirtualMachineTemplate + virtualMachineTemplateCount, + virtualMachineTemplateCPUCores, + virtualMachineTemplateMemory, + // VirtualMachineSet + virtualMachineSetCount, + virtualMachineSetCPUCores, + virtualMachineSetMemory, + virtualMachineSetReplicas, + // VirtualMachineSnapshot + virtualMachineSnapshotCount, + virtualMachineSnapshotPerVirtualMachineCount, + // VirtualMachineSnapshotPolicy + virtualMachineSnapshotPolicyCount, + // StorageDownloadURL + storageDownloadURLCount, + // CustomCertificate + customCertificateCount, } -func IncVirtualMachineCount() { - virtualMachineCount.Inc() +func init() { //nolint:gochecknoinits // This is required by kubebuilder + metrics.Registry.MustRegister(KubemoxMetrics...) } -func DecVirtualMachineCount() { - virtualMachineCount.Dec() +func UpdateProxmoxMetrics(ctx context.Context, kubeClient client.Client) { + logger := log.FromContext(ctx) + + // Get all virtual machines + vmList := &proxmoxv1alpha1.VirtualMachineList{} + if err := kubeClient.List(ctx, vmList); err != nil { + logger.Error(err, "unable to list virtual machines") + return + } + updateVirtualMachineMetrics(vmList) + + // Get all managed virtual machines + managedVMList := &proxmoxv1alpha1.ManagedVirtualMachineList{} + if err := kubeClient.List(ctx, managedVMList); err != nil { + logger.Error(err, "unable to list managed virtual machines") + return + } + updateManagedVirtualMachineMetrics(managedVMList) + + // Get all containers + containerList := &proxmoxv1alpha1.ContainerList{} + if err := kubeClient.List(ctx, containerList); err != nil { + logger.Error(err, "unable to list containers") + return + } + updateContainerMetrics(containerList) + + // Get all virtual machine templates + virtualMachineTemplateList := &proxmoxv1alpha1.VirtualMachineTemplateList{} + if err := kubeClient.List(ctx, virtualMachineTemplateList); err != nil { + logger.Error(err, "unable to list virtual machine templates") + return + } + updateVirtualMachineTemplateMetrics(virtualMachineTemplateList) + + // Get all virtual machine sets + vmSetList := &proxmoxv1alpha1.VirtualMachineSetList{} + if err := kubeClient.List(ctx, vmSetList); err != nil { + logger.Error(err, "unable to list virtual machine sets") + return + } + updateVirtualMachineSetMetrics(vmSetList) + + // Get all virtual machine snapshots + vmSnapshotList := &proxmoxv1alpha1.VirtualMachineSnapshotList{} + if err := kubeClient.List(ctx, vmSnapshotList); err != nil { + logger.Error(err, "unable to list virtual machine snapshots") + return + } + updateVirtualMachineSnapshotMetrics(vmSnapshotList) + + // Get all virtual machine snapshot policies + vmSnapshotPolicyList := &proxmoxv1alpha1.VirtualMachineSnapshotPolicyList{} + if err := kubeClient.List(ctx, vmSnapshotPolicyList); err != nil { + logger.Error(err, "unable to list virtual machine snapshot policies") + return + } + virtualMachineSnapshotPolicyCount.Set(float64(len(vmSnapshotPolicyList.Items))) + + // Get all storage download URLs + storageDownloadURLList := &proxmoxv1alpha1.StorageDownloadURLList{} + if err := kubeClient.List(ctx, storageDownloadURLList); err != nil { + logger.Error(err, "unable to list storage download URLs") + return + } + storageDownloadURLCount.Set(float64(len(storageDownloadURLList.Items))) + + // Get all custom certificates + customCertificateList := &proxmoxv1alpha1.CustomCertificateList{} + if err := kubeClient.List(ctx, customCertificateList); err != nil { + logger.Error(err, "unable to list custom certificates") + return + } + customCertificateCount.Set(float64(len(customCertificateList.Items))) } -func SetVirtualMachineCPUCores(name, namespace string, cores float64) { - virtualMachineCPUCores.WithLabelValues(name, namespace).Set(cores) +func updateVirtualMachineMetrics(vmList *proxmoxv1alpha1.VirtualMachineList) { + virtualMachineCount.Set(float64(len(vmList.Items))) + for i := range vmList.Items { + vm := &vmList.Items[i] + virtualMachineCPUCores.WithLabelValues(vm.Name, vm.Namespace).Set(float64(proxmox.GetCores(vm))) + virtualMachineMemory.WithLabelValues(vm.Name, vm.Namespace).Set(float64(proxmox.GetMemory(vm))) + } } -func SetVirtualMachineMemory(name, namespace string, memory float64) { - virtualMachineMemory.WithLabelValues(name, namespace).Set(memory) +func updateManagedVirtualMachineMetrics(managedVMList *proxmoxv1alpha1.ManagedVirtualMachineList) { + ManagedVirtualMachineCount.Set(float64(len(managedVMList.Items))) + + for i := range managedVMList.Items { + managedVM := &managedVMList.Items[i] + ManagedVirtualMachineCPUCores.WithLabelValues(managedVM.Name, managedVM.Namespace).Set(float64(managedVM.Spec.Cores)) + ManagedVirtualMachineMemory.WithLabelValues(managedVM.Name, managedVM.Namespace).Set(float64(managedVM.Spec.Memory)) + } } -func IncManagedVirtualMachineCount() { - ManagedVirtualMachineCount.Inc() +func updateContainerMetrics(containerList *proxmoxv1alpha1.ContainerList) { + containerCount.Set(float64(len(containerList.Items))) + for i := range containerList.Items { + container := &containerList.Items[i] + containerCPUCores.WithLabelValues(container.Name, container.Namespace).Set(float64(container.Spec.Template.Cores)) + containerMemory.WithLabelValues(container.Name, container.Namespace).Set(float64(container.Spec.Template.Memory)) + } } -func DecManagedVirtualMachineCount() { - ManagedVirtualMachineCount.Dec() +func updateVirtualMachineTemplateMetrics(virtualMachineTemplateList *proxmoxv1alpha1.VirtualMachineTemplateList) { + virtualMachineTemplateCount.Set(float64(len(virtualMachineTemplateList.Items))) + for i := range virtualMachineTemplateList.Items { + vmTemplate := &virtualMachineTemplateList.Items[i] + virtualMachineTemplateCPUCores.WithLabelValues(vmTemplate.Name, vmTemplate.Namespace). + Set(float64(vmTemplate.Spec.VirtualMachineConfig.Cores)) + virtualMachineTemplateMemory.WithLabelValues(vmTemplate.Name, vmTemplate.Namespace). + Set(float64(vmTemplate.Spec.VirtualMachineConfig.Memory)) + } } -func SetManagedVirtualMachineCPUCores(name, namespace string, cores float64) { - ManagedVirtualMachineCPUCores.WithLabelValues(name, namespace).Set(cores) +func updateVirtualMachineSetMetrics(vmSetList *proxmoxv1alpha1.VirtualMachineSetList) { + virtualMachineSetCount.Set(float64(len(vmSetList.Items))) + for i := range vmSetList.Items { + vmSet := &vmSetList.Items[i] + virtualMachineSetCPUCores.WithLabelValues(vmSet.Name, vmSet.Namespace). + Set(float64(vmSet.Spec.Template.Cores)) + virtualMachineSetMemory.WithLabelValues(vmSet.Name, vmSet.Namespace). + Set(float64(vmSet.Spec.Template.Memory)) + virtualMachineSetReplicas.WithLabelValues(vmSet.Name, vmSet.Namespace). + Set(float64(vmSet.Spec.Replicas)) + } } -func SetManagedVirtualMachineMemory(name, namespace string, memory float64) { - ManagedVirtualMachineMemory.WithLabelValues(name, namespace).Set(memory) +func updateVirtualMachineSnapshotMetrics(vmSnapshotList *proxmoxv1alpha1.VirtualMachineSnapshotList) { + virtualMachineSnapshotCount.Set(float64(len(vmSnapshotList.Items))) } diff --git a/pkg/proxmox/virtualmachine.go b/pkg/proxmox/virtualmachine.go index a08ce64..bc8bda0 100644 --- a/pkg/proxmox/virtualmachine.go +++ b/pkg/proxmox/virtualmachine.go @@ -12,7 +12,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/utils" proxmox "github.com/luthermonson/go-proxmox" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -535,13 +534,9 @@ func UpdateVM(vm *proxmoxv1alpha1.VirtualMachine) bool { case VirtualMachineTemplateType: cpuOption.Value = vm.Spec.Template.Cores memoryOption.Value = uint64(vm.Spec.Template.Memory) - metrics.SetVirtualMachineCPUCores(vmName, vm.Namespace, float64(vm.Spec.Template.Cores)) - metrics.SetVirtualMachineMemory(vmName, vm.Namespace, float64(vm.Spec.Template.Memory)) case VirtualMachineScratchType: cpuOption.Value = vm.Spec.VMSpec.Cores memoryOption.Value = uint64(vm.Spec.VMSpec.Memory) - metrics.SetVirtualMachineCPUCores(vmName, vm.Namespace, float64(vm.Spec.VMSpec.Cores)) - metrics.SetVirtualMachineMemory(vmName, vm.Namespace, float64(vm.Spec.VMSpec.Memory)) default: log.Log.Info(fmt.Sprintf("VM %s doesn't have any template or vmSpec defined", vmName)) } @@ -671,9 +666,6 @@ func UpdateManagedVM(ctx context.Context, managedVM *proxmoxv1alpha1.ManagedVirt // Revert the update since it's not possible to shrink disk managedVM.Spec.Disk = int(VirtualMachineMaxDisk) } - // Add metrics - metrics.SetManagedVirtualMachineCPUCores(managedVMName, managedVM.Namespace, float64(managedVM.Spec.Cores)) - metrics.SetManagedVirtualMachineMemory(managedVMName, managedVM.Namespace, float64(managedVM.Spec.Memory)) if VirtualMachine.CPUs != managedVM.Spec.Cores || VirtualMachineMem != uint64(managedVM.Spec.Memory) { // Update VM @@ -1139,9 +1131,9 @@ func CheckVirtualMachineDelta(vm *proxmoxv1alpha1.VirtualMachine) (bool, error) } // Desired VM desiredVM := VirtualMachineComparison{ - Cores: getCores(vm), + Cores: GetCores(vm), Sockets: getSockets(vm), - Memory: getMemory(vm), + Memory: GetMemory(vm), Networks: *getNetworks(vm), Disks: sortDisks(getDisks(vm)), } @@ -1498,14 +1490,14 @@ func IsVirtualMachineReady(obj Resource) (bool, error) { // Helper functions -func getCores(vm *proxmoxv1alpha1.VirtualMachine) int { +func GetCores(vm *proxmoxv1alpha1.VirtualMachine) int { if CheckVMType(vm) == VirtualMachineTemplateType { return vm.Spec.Template.Cores } return vm.Spec.VMSpec.Cores } -func getMemory(vm *proxmoxv1alpha1.VirtualMachine) int { +func GetMemory(vm *proxmoxv1alpha1.VirtualMachine) int { if CheckVMType(vm) == VirtualMachineTemplateType { return vm.Spec.Template.Memory } diff --git a/pkg/proxmox/watcher.go b/pkg/proxmox/watcher.go index 0852e84..ddd69fc 100644 --- a/pkg/proxmox/watcher.go +++ b/pkg/proxmox/watcher.go @@ -6,6 +6,7 @@ import ( "sync" "time" + proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -110,7 +111,11 @@ func StartWatcher(ctx context.Context, resource Resource, logger.Error(err, "Error updating resource status") return ctrl.Result{}, err } - + // If the reconcileMode is WatchOnly then we don't need to check for configuration drift + val := resource.GetAnnotations()[proxmoxv1alpha1.ReconcileModeAnnotation] + if val == proxmoxv1alpha1.ReconcileModeWatchOnly || val == proxmoxv1alpha1.ReconcileModeEnsureExists { + continue + } triggerReconcile, err := checkDelta(resource) if err != nil { logger.Error(err, "Error comparing resource state")