Skip to content

Commit

Permalink
Use CustomValidator interface for webhook implementation (#97)
Browse files Browse the repository at this point in the history
* Use CustomValidator interface for webhook implementation

Signed-off-by: Atanas Dinov <atanas.dinov@suse.com>

* Unify webhook register

Signed-off-by: Atanas Dinov <atanas.dinov@suse.com>

---------

Signed-off-by: Atanas Dinov <atanas.dinov@suse.com>
  • Loading branch information
atanasdinov authored Oct 3, 2024
1 parent bac33b4 commit fbb06fb
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 17 deletions.
40 changes: 25 additions & 15 deletions api/v1alpha1/upgradeplan_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1alpha1

import (
"context"
"fmt"
"slices"

Expand All @@ -27,47 +28,57 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// SetupWebhookWithManager will setup the manager to manage the webhooks
func (r *UpgradePlan) SetupWebhookWithManager(mgr ctrl.Manager) error {
func SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
WithValidator(&UpgradePlanValidator{}).
For(&UpgradePlan{}).
Complete()
}

// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
// +kubebuilder:webhook:path=/validate-lifecycle-suse-com-v1alpha1-upgradeplan,mutating=false,failurePolicy=fail,sideEffects=None,groups=lifecycle.suse.com,resources=upgradeplans,verbs=create;update,versions=v1alpha1,name=vupgradeplan.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &UpgradePlan{}
var _ webhook.CustomValidator = &UpgradePlanValidator{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *UpgradePlan) ValidateCreate() (admission.Warnings, error) {
_, err := validateReleaseVersion(r.Spec.ReleaseVersion)
type UpgradePlanValidator struct{}

func (*UpgradePlanValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
upgradePlan, ok := obj.(*UpgradePlan)
if !ok {
return nil, fmt.Errorf("unexpected object type: %T", obj)
}

_, err := validateReleaseVersion(upgradePlan.Spec.ReleaseVersion)
return nil, err
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *UpgradePlan) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
func (*UpgradePlanValidator) ValidateUpdate(ctx context.Context, old, new runtime.Object) (admission.Warnings, error) {
oldPlan, ok := old.(*UpgradePlan)
if !ok {
return nil, fmt.Errorf("unexpected object type: %T", old)
}

newPlan, ok := new.(*UpgradePlan)
if !ok {
return nil, fmt.Errorf("unexpected object type: %T", new)
}

// deletion is scheduled, but not yet finished and controller has updated the plan
// with the removed finalizers
if !r.ObjectMeta.DeletionTimestamp.IsZero() && len(r.Finalizers) < len(oldPlan.Finalizers) {
if !newPlan.ObjectMeta.DeletionTimestamp.IsZero() && len(newPlan.Finalizers) < len(oldPlan.Finalizers) {
return nil, nil
}

disallowingUpdateStates := []string{UpgradeInProgress, UpgradePending, UpgradeError}

for _, condition := range r.Status.Conditions {
for _, condition := range newPlan.Status.Conditions {
if slices.Contains(disallowingUpdateStates, condition.Reason) {
return nil, fmt.Errorf("upgrade plan cannot be edited while condition '%s' is in '%s' state", condition.Type, condition.Reason)
}
}

newReleaseVersion, err := validateReleaseVersion(r.Spec.ReleaseVersion)
newReleaseVersion, err := validateReleaseVersion(newPlan.Spec.ReleaseVersion)
if err != nil {
return nil, err
}
Expand All @@ -80,7 +91,7 @@ func (r *UpgradePlan) ValidateUpdate(old runtime.Object) (admission.Warnings, er

switch indicator {
case 0:
return nil, fmt.Errorf("any edits over '%s' must come with an increment of the releaseVersion", r.Name)
return nil, fmt.Errorf("any edits over '%s' must come with an increment of the releaseVersion", newPlan.Name)
case -1:
return nil, fmt.Errorf("new releaseVersion must be greater than the currently applied one ('%s')", oldPlan.Status.LastSuccessfulReleaseVersion)
}
Expand All @@ -89,8 +100,7 @@ func (r *UpgradePlan) ValidateUpdate(old runtime.Object) (admission.Warnings, er
return nil, nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *UpgradePlan) ValidateDelete() (admission.Warnings, error) {
func (*UpgradePlanValidator) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) {
return nil, nil
}

Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha1/webhook_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ var _ = BeforeSuite(func() {
})
Expect(err).NotTo(HaveOccurred())

err = (&UpgradePlan{}).SetupWebhookWithManager(mgr)
err = SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

// +kubebuilder:scaffold:webhook
Expand Down
15 changes: 15 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func main() {
os.Exit(1)
}
if os.Getenv("ENABLE_WEBHOOKS") != "false" {
if err = (&lifecyclev1alpha1.UpgradePlan{}).SetupWebhookWithManager(mgr); err != nil {
if err = lifecyclev1alpha1.SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "UpgradePlan")
os.Exit(1)
}
Expand Down

0 comments on commit fbb06fb

Please sign in to comment.