diff --git a/api/pie/v1alpha1/pieprobe_types.go b/api/pie/v1alpha1/pieprobe_types.go index 93f3ac7..6afcf6f 100644 --- a/api/pie/v1alpha1/pieprobe_types.go +++ b/api/pie/v1alpha1/pieprobe_types.go @@ -32,6 +32,16 @@ type PieProbeSpec struct { //+kubebuilder:validation:Optional //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="pvcCapacity is immutable" PVCCapacity *resource.Quantity `json:"pvcCapacity"` + + //+kubebuilder:default:=false + //+kubebuilder:validation:Optional + //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="disableProvisionProbe is immutable" + DisableProvisionProbe bool `json:"disableProvisionProbe"` + + //+kubebuilder:default:=false + //+kubebuilder:validation:Optional + //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="disableMountProbes is immutable" + DisableMountProbes bool `json:"disableMountProbes"` } // PieProbeStatus defines the observed state of PieProbe diff --git a/charts/pie/templates/pie.topolvm.io_pieprobes.yaml b/charts/pie/templates/pie.topolvm.io_pieprobes.yaml index 6e0f5be..2dbc38c 100644 --- a/charts/pie/templates/pie.topolvm.io_pieprobes.yaml +++ b/charts/pie/templates/pie.topolvm.io_pieprobes.yaml @@ -39,6 +39,18 @@ spec: spec: description: PieProbeSpec defines the desired state of PieProbe properties: + disableMountProbes: + default: false + type: boolean + x-kubernetes-validations: + - message: disableMountProbes is immutable + rule: self == oldSelf + disableProvisionProbe: + default: false + type: boolean + x-kubernetes-validations: + - message: disableProvisionProbe is immutable + rule: self == oldSelf monitoringStorageClass: type: string x-kubernetes-validations: diff --git a/config/crd/bases/pie.topolvm.io_pieprobes.yaml b/config/crd/bases/pie.topolvm.io_pieprobes.yaml index 6e0f5be..2dbc38c 100644 --- a/config/crd/bases/pie.topolvm.io_pieprobes.yaml +++ b/config/crd/bases/pie.topolvm.io_pieprobes.yaml @@ -39,6 +39,18 @@ spec: spec: description: PieProbeSpec defines the desired state of PieProbe properties: + disableMountProbes: + default: false + type: boolean + x-kubernetes-validations: + - message: disableMountProbes is immutable + rule: self == oldSelf + disableProvisionProbe: + default: false + type: boolean + x-kubernetes-validations: + - message: disableProvisionProbe is immutable + rule: self == oldSelf monitoringStorageClass: type: string x-kubernetes-validations: diff --git a/internal/controller/pie/pieprobe_controller.go b/internal/controller/pie/pieprobe_controller.go index eaedef5..f7a3448 100644 --- a/internal/controller/pie/pieprobe_controller.go +++ b/internal/controller/pie/pieprobe_controller.go @@ -87,21 +87,31 @@ func (r *PieProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return ctrl.Result{}, nil } - // Create a provision-probe CronJob for each sc - err = r.createOrUpdateJob( - ctx, - ProvisionProbe, - &pieProbe, - nil, - ) - if err != nil { - return ctrl.Result{}, err + if !pieProbe.Spec.DisableProvisionProbe { + if err := r.reconcileProvisionProbe(ctx, &pieProbe); err != nil { + return ctrl.Result{}, err + } + } + + if !pieProbe.Spec.DisableMountProbes { + if err := r.reconcileMountProbes(ctx, &pieProbe); err != nil { + return ctrl.Result{}, err + } } + return ctrl.Result{}, nil +} + +func (r *PieProbeReconciler) reconcileProvisionProbe(ctx context.Context, pieProbe *piev1alpha1.PieProbe) error { + // Create a provision-probe CronJob for each sc + return r.createOrUpdateJob(ctx, ProvisionProbe, pieProbe, nil) +} + +func (r *PieProbeReconciler) reconcileMountProbes(ctx context.Context, pieProbe *piev1alpha1.PieProbe) error { // Get a node list and create a PVC and a mount-probe CronJob for each node and sc. nodeSelector, err := nodeaffinity.NewNodeSelector(&pieProbe.Spec.NodeSelector) if err != nil { - return ctrl.Result{}, err + return err } allNodeList := corev1.NodeList{} r.client.List(ctx, &allNodeList) @@ -112,22 +122,13 @@ func (r *PieProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } availableNodeList = append(availableNodeList, node) - err = r.createOrUpdatePVC( - ctx, - node.Name, - &pieProbe, - ) + err = r.createOrUpdatePVC(ctx, node.Name, pieProbe) if err != nil { - return ctrl.Result{}, err + return err } - err = r.createOrUpdateJob( - ctx, - MountProbe, - &pieProbe, - &node.Name, - ) + err = r.createOrUpdateJob(ctx, MountProbe, pieProbe, &node.Name) if err != nil { - return ctrl.Result{}, err + return err } } @@ -144,7 +145,7 @@ func (r *PieProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c }), }) if err != nil { - return ctrl.Result{}, err + return err } for _, cronJob := range cronJobList.Items { nodeName := cronJob.GetLabels()[constants.ProbeNodeLabelKey] @@ -156,7 +157,7 @@ func (r *PieProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } err := r.deleteCronJob(ctx, &cronJob) if client.IgnoreNotFound(err) != nil { - return ctrl.Result{}, err + return err } } @@ -168,7 +169,7 @@ func (r *PieProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c }), }) if err != nil { - return ctrl.Result{}, err + return err } for _, pvc := range pvcList.Items { nodeName := pvc.GetLabels()[constants.ProbeNodeLabelKey] @@ -177,11 +178,11 @@ func (r *PieProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } err = r.deletePVC(ctx, &pvc) if client.IgnoreNotFound(err) != nil { - return ctrl.Result{}, err + return err } } - return ctrl.Result{}, nil + return nil } func (r *PieProbeReconciler) deletePVC(ctx context.Context, pvc *corev1.PersistentVolumeClaim) error { diff --git a/internal/controller/pie/pieprobe_controller_test.go b/internal/controller/pie/pieprobe_controller_test.go index 4de3853..237fc5e 100644 --- a/internal/controller/pie/pieprobe_controller_test.go +++ b/internal/controller/pie/pieprobe_controller_test.go @@ -95,6 +95,44 @@ func prepareObjects(ctx context.Context) error { return nil } +func deletePieProbeAndReferencingResources(ctx context.Context, pieProbe *piev1alpha1.PieProbe) error { + if err := k8sClient.Delete(ctx, pieProbe); err != nil { + return err + } + + var pvcList corev1.PersistentVolumeClaimList + if err := k8sClient.List(ctx, &pvcList, client.MatchingLabels(map[string]string{ + "storage-class": pieProbe.Spec.MonitoringStorageClass, + })); err != nil { + return err + } + + for _, pvc := range pvcList.Items { + pvc.ObjectMeta.Finalizers = []string{} + if err := k8sClient.Update(ctx, &pvc); err != nil { + return err + } + if err := k8sClient.Delete(ctx, &pvc); err != nil { + return err + } + } + + var cronjobList batchv1.CronJobList + if err := k8sClient.List(ctx, &cronjobList, client.MatchingLabels(map[string]string{ + "storage-class": pieProbe.Spec.MonitoringStorageClass, + })); err != nil { + return err + } + + for _, cronjob := range cronjobList.Items { + if err := k8sClient.Delete(ctx, &cronjob); err != nil { + return err + } + } + + return nil +} + var _ = Describe("PieProbe controller", func() { ctx := context.Background() var stopFunc func() @@ -237,30 +275,75 @@ var _ = Describe("PieProbe controller", func() { }).Should(Succeed()) By("cleaning up PVCs and CronJobs for sc2") - err = k8sClient.Delete(ctx, pieProbe2) - Expect(err).NotTo(HaveOccurred()) - var pvcList corev1.PersistentVolumeClaimList - err = k8sClient.List(ctx, &pvcList, client.MatchingLabels(map[string]string{ - "storage-class": "sc2", - })) - Expect(err).NotTo(HaveOccurred()) - for _, pvc := range pvcList.Items { - pvc.ObjectMeta.Finalizers = []string{} - err = k8sClient.Update(ctx, &pvc) - Expect(err).NotTo(HaveOccurred()) - err = k8sClient.Delete(ctx, &pvc) - Expect(err).NotTo(HaveOccurred()) + deletePieProbeAndReferencingResources(ctx, pieProbe2) + }) + + It("should create only mount probes if .spec.disableProvisionProbes is true", func() { + By("creating a new PieProbe with .spec.disableProvisionProbes true") + pieProbe2 := &piev1alpha1.PieProbe{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "pie-probe-sc2", + }, + Spec: piev1alpha1.PieProbeSpec{ + MonitoringStorageClass: "sc2", + NodeSelector: nodeSelector, + ProbePeriod: 1, + DisableProvisionProbe: true, + }, } - var cronjobList batchv1.CronJobList - err = k8sClient.List(ctx, &cronjobList) + _, err := ctrl.CreateOrUpdate(ctx, k8sClient, pieProbe2, func() error { return nil }) Expect(err).NotTo(HaveOccurred()) - for _, cronjob := range cronjobList.Items { - if !strings.Contains(cronjob.GetName(), "-sc2-") { - continue + + By("checking mount probes exist and provision probes DO NOT exist") + Eventually(func(g Gomega) { + var cronjobList batchv1.CronJobList + err = k8sClient.List(context.Background(), &cronjobList, client.MatchingLabels(map[string]string{ + "storage-class": "sc2", + })) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(cronjobList.Items).To(HaveLen(2)) + for _, cronjob := range cronjobList.Items { + g.Expect(cronjob.GetName()).To(HavePrefix("mount-")) } - err = k8sClient.Delete(ctx, &cronjob) - Expect(err).NotTo(HaveOccurred()) + }).Should(Succeed()) + + By("cleaning up PVCs and CronJobs for sc2") + deletePieProbeAndReferencingResources(ctx, pieProbe2) + }) + + It("should create only provision probes if .spec.disableMountProbes is true", func() { + By("creating a new PieProbe with .spec.disableMountProbes true") + pieProbe2 := &piev1alpha1.PieProbe{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "pie-probe-sc2", + }, + Spec: piev1alpha1.PieProbeSpec{ + MonitoringStorageClass: "sc2", + NodeSelector: nodeSelector, + ProbePeriod: 1, + DisableMountProbes: true, + }, } + _, err := ctrl.CreateOrUpdate(ctx, k8sClient, pieProbe2, func() error { return nil }) + Expect(err).NotTo(HaveOccurred()) + + By("checking provision probes exist and mount probes DO NOT exist") + Eventually(func(g Gomega) { + var cronjobList batchv1.CronJobList + err = k8sClient.List(ctx, &cronjobList, client.MatchingLabels(map[string]string{ + "storage-class": "sc2", + })) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(cronjobList.Items).To(HaveLen(1)) + for _, cronjob := range cronjobList.Items { + g.Expect(cronjob.GetName()).To(HavePrefix("provision-")) + } + }).Should(Succeed()) + + By("cleaning up PVCs and CronJobs for sc2") + deletePieProbeAndReferencingResources(ctx, pieProbe2) }) It("should reject to edit monitoringStorageClass", func() {