Skip to content

Commit

Permalink
Merge pull request #540 from scothis/custom-defaulter
Browse files Browse the repository at this point in the history
Apply defaults to objects implementing CustomDefaulter
  • Loading branch information
scothis authored Aug 15, 2024
2 parents 20db0da + bc2cb08 commit 5c96b7e
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 34 deletions.
46 changes: 36 additions & 10 deletions internal/resources/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ import (
)

var (
_ webhook.Defaulter = &TestResource{}
_ webhook.Validator = &TestResource{}
_ client.Object = &TestResource{}
_ webhook.CustomDefaulter = &TestResource{}
_ webhook.CustomValidator = &TestResource{}
_ client.Object = &TestResource{}
)

// +kubebuilder:object:root=true
Expand All @@ -50,26 +50,52 @@ type TestResource struct {
Status TestResourceStatus `json:"status"`
}

func (r *TestResource) Default() {
func (*TestResource) Default(ctx context.Context, obj runtime.Object) error {
r, ok := obj.(*TestResource)
if !ok {
return fmt.Errorf("expected object to be TestResource")
}

if r.Spec.Fields == nil {
r.Spec.Fields = map[string]string{}
}
r.Spec.Fields["Defaulter"] = "ran"

return nil
}

func (r *TestResource) ValidateCreate() (admission.Warnings, error) {
return nil, r.validate().ToAggregate()
func (*TestResource) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
r, ok := obj.(*TestResource)
if !ok {
return nil, fmt.Errorf("expected obj to be TestResource")
}

return nil, r.validate(ctx).ToAggregate()
}

func (r *TestResource) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
return nil, r.validate().ToAggregate()
func (*TestResource) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
_, ok := oldObj.(*TestResource)
if !ok {
return nil, fmt.Errorf("expected oldObj to be TestResource")
}
r, ok := newObj.(*TestResource)
if !ok {
return nil, fmt.Errorf("expected newObj to be TestResource")
}

return nil, r.validate(ctx).ToAggregate()
}

func (r *TestResource) ValidateDelete() (admission.Warnings, error) {
func (*TestResource) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
_, ok := obj.(*TestResource)
if !ok {
return nil, fmt.Errorf("expected obj to be TestResource")
}

return nil, nil
}

func (r *TestResource) validate() field.ErrorList {
func (r *TestResource) validate(ctx context.Context) field.ErrorList {
errs := field.ErrorList{}

if r.Spec.Fields != nil {
Expand Down
15 changes: 13 additions & 2 deletions internal/resources/resource_with_empty_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ limitations under the License.
package resources

import (
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

var _ webhook.Defaulter = &TestResourceEmptyStatus{}
var _ webhook.CustomDefaulter = &TestResourceEmptyStatus{}

// +kubebuilder:object:root=true
// +genclient
Expand All @@ -34,11 +38,18 @@ type TestResourceEmptyStatus struct {
Status TestResourceEmptyStatusStatus `json:"status"`
}

func (r *TestResourceEmptyStatus) Default() {
func (*TestResourceEmptyStatus) Default(ctx context.Context, obj runtime.Object) error {
r, ok := obj.(*TestResourceEmptyStatus)
if !ok {
return fmt.Errorf("expected obj to be TestResourceEmptyStatus")
}

if r.Spec.Fields == nil {
r.Spec.Fields = map[string]string{}
}
r.Spec.Fields["Defaulter"] = "ran"

return nil
}

// +kubebuilder:object:generate=true
Expand Down
48 changes: 38 additions & 10 deletions internal/resources/resource_with_nilable_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ limitations under the License.
package resources

import (
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
Expand All @@ -26,9 +29,9 @@ import (
)

var (
_ webhook.Defaulter = &TestResourceNilableStatus{}
_ webhook.Validator = &TestResourceNilableStatus{}
_ client.Object = &TestResourceNilableStatus{}
_ webhook.CustomDefaulter = &TestResourceNilableStatus{}
_ webhook.CustomValidator = &TestResourceNilableStatus{}
_ client.Object = &TestResourceNilableStatus{}
)

// +kubebuilder:object:root=true
Expand All @@ -42,26 +45,51 @@ type TestResourceNilableStatus struct {
Status *TestResourceStatus `json:"status"`
}

func (r *TestResourceNilableStatus) Default() {
func (TestResourceNilableStatus) Default(ctx context.Context, obj runtime.Object) error {
r, ok := obj.(*TestResourceNilableStatus)
if !ok {
return fmt.Errorf("expected obj to be TestResourceNilableStatus")
}
if r.Spec.Fields == nil {
r.Spec.Fields = map[string]string{}
}
r.Spec.Fields["Defaulter"] = "ran"

return nil
}

func (r *TestResourceNilableStatus) ValidateCreate() (admission.Warnings, error) {
return nil, r.validate().ToAggregate()
func (*TestResourceNilableStatus) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
r, ok := obj.(*TestResourceNilableStatus)
if !ok {
return nil, fmt.Errorf("expected obj to be TestResourceNilableStatus")
}

return nil, r.validate(ctx).ToAggregate()
}

func (r *TestResourceNilableStatus) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
return nil, r.validate().ToAggregate()
func (*TestResourceNilableStatus) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
_, ok := oldObj.(*TestResourceNilableStatus)
if !ok {
return nil, fmt.Errorf("expected oldObj to be TestResourceNilableStatus")
}
r, ok := newObj.(*TestResourceNilableStatus)
if !ok {
return nil, fmt.Errorf("expected newObj to be TestResourceNilableStatus")
}

return nil, r.validate(ctx).ToAggregate()
}

func (r *TestResourceNilableStatus) ValidateDelete() (admission.Warnings, error) {
func (*TestResourceNilableStatus) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
_, ok := obj.(*TestResourceNilableStatus)
if !ok {
return nil, fmt.Errorf("expected obj to be TestResourceNilableStatus")
}

return nil, nil
}

func (r *TestResourceNilableStatus) validate() field.ErrorList {
func (r *TestResourceNilableStatus) validate(ctx context.Context) field.ErrorList {
errs := field.ErrorList{}

if r.Spec.Fields != nil {
Expand Down
46 changes: 36 additions & 10 deletions internal/resources/resource_with_unexported_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package resources

import (
"context"
"encoding/json"
"fmt"

Expand All @@ -32,9 +33,9 @@ import (
)

var (
_ webhook.Defaulter = &TestResourceUnexportedFields{}
_ webhook.Validator = &TestResourceUnexportedFields{}
_ client.Object = &TestResourceUnexportedFields{}
_ webhook.CustomDefaulter = &TestResourceUnexportedFields{}
_ webhook.CustomValidator = &TestResourceUnexportedFields{}
_ client.Object = &TestResourceUnexportedFields{}
)

// +kubebuilder:object:root=true
Expand All @@ -48,26 +49,51 @@ type TestResourceUnexportedFields struct {
Status TestResourceUnexportedFieldsStatus `json:"status"`
}

func (r *TestResourceUnexportedFields) Default() {
func (*TestResourceUnexportedFields) Default(ctx context.Context, obj runtime.Object) error {
r, ok := obj.(*TestResourceUnexportedFields)
if !ok {
return fmt.Errorf("expected obj to be TestResourceUnexportedFields")
}
if r.Spec.Fields == nil {
r.Spec.Fields = map[string]string{}
}
r.Spec.Fields["Defaulter"] = "ran"

return nil
}

func (r *TestResourceUnexportedFields) ValidateCreate() (admission.Warnings, error) {
return nil, r.validate().ToAggregate()
func (*TestResourceUnexportedFields) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
r, ok := obj.(*TestResourceUnexportedFields)
if !ok {
return nil, fmt.Errorf("expected obj to be TestResourceUnexportedFields")
}

return nil, r.validate(ctx).ToAggregate()
}

func (r *TestResourceUnexportedFields) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
return nil, r.validate().ToAggregate()
func (*TestResourceUnexportedFields) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
_, ok := oldObj.(*TestResourceUnexportedFields)
if !ok {
return nil, fmt.Errorf("expected oldObj to be TestResourceUnexportedFields")
}
r, ok := newObj.(*TestResourceUnexportedFields)
if !ok {
return nil, fmt.Errorf("expected newObj to be TestResourceUnexportedFields")
}

return nil, r.validate(ctx).ToAggregate()
}

func (r *TestResourceUnexportedFields) ValidateDelete() (admission.Warnings, error) {
func (*TestResourceUnexportedFields) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
_, ok := obj.(*TestResourceUnexportedFields)
if !ok {
return nil, fmt.Errorf("expected obj to be TestResourceUnexportedFields")
}

return nil, nil
}

func (r *TestResourceUnexportedFields) validate() field.ErrorList {
func (r *TestResourceUnexportedFields) validate(ctx context.Context) field.ErrorList {
errs := field.ErrorList{}

if r.Spec.Fields != nil {
Expand Down
7 changes: 6 additions & 1 deletion reconcilers/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,12 @@ func (r *ResourceReconciler[T]) reconcileOuter(ctx context.Context, req Request)
}
resource := originalResource.DeepCopyObject().(T)

if defaulter, ok := client.Object(resource).(webhook.Defaulter); ok {
if defaulter, ok := client.Object(resource).(webhook.CustomDefaulter); ok {
// resource.Default(ctx, resource)
if err := defaulter.Default(ctx, resource); err != nil {
return Result{}, err
}
} else if defaulter, ok := client.Object(resource).(webhook.Defaulter); ok {
// resource.Default()
defaulter.Default()
}
Expand Down
7 changes: 6 additions & 1 deletion reconcilers/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,12 @@ func (r *AdmissionWebhookAdapter[T]) reconcile(ctx context.Context, req admissio
return err
}

if defaulter, ok := client.Object(resource).(webhook.Defaulter); ok {
if defaulter, ok := client.Object(resource).(webhook.CustomDefaulter); ok {
// resource.Default(ctx, resource)
if err := defaulter.Default(ctx, resource); err != nil {
return err
}
} else if defaulter, ok := client.Object(resource).(webhook.Defaulter); ok {
// resource.Default()
defaulter.Default()
}
Expand Down

0 comments on commit 5c96b7e

Please sign in to comment.