knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/resourcesemantics/conversion/internal/types.go (about)

     1  /*
     2  Copyright 2020 The Knative Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package internal
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"strings"
    25  
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"knative.dev/pkg/apis"
    28  )
    29  
    30  const (
    31  	// Group specifies the group of the test resource
    32  	Group = "webhook.pkg.knative.dev"
    33  
    34  	// Kind specifies the kind of the test resource
    35  	Kind = "Resource"
    36  
    37  	// ErrorMarshal when assigned to the Spec.Property of the ErrorResource
    38  	// will cause json marshalling of the resource to fail
    39  	ErrorMarshal = "marshal"
    40  
    41  	// ErrorUnmarshal when assigned to the Spec.Property of the ErrorResource
    42  	// will cause json unmarshalling of the resource to fail
    43  	ErrorUnmarshal = "unmarshal"
    44  
    45  	// ErrorConvertTo when assigned to the Spec.Property of the ErrorResource
    46  	// will cause ConvertTo to fail
    47  	ErrorConvertTo = "convertTo"
    48  
    49  	// ErrorConvertFrom when assigned to the Spec.Property of the ErrorResource
    50  	// will cause ConvertFrom to fail
    51  	ErrorConvertFrom = "convertFrom"
    52  )
    53  
    54  type (
    55  	// V1Resource will never has a prefix or suffix on Spec.Property
    56  	// This type is used for testing conversion webhooks
    57  	//
    58  	// +k8s:deepcopy-gen=true
    59  	// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
    60  	V1Resource struct {
    61  		metav1.TypeMeta   `json:",inline"`
    62  		metav1.ObjectMeta `json:"metadata,omitempty"`
    63  		Spec              Spec `json:"spec"`
    64  	}
    65  
    66  	// V2Resource will always have a 'prefix/' in front of it's property
    67  	// This type is used for testing conversion webhooks
    68  	//
    69  	// +k8s:deepcopy-gen=true
    70  	// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
    71  	V2Resource struct {
    72  		metav1.TypeMeta   `json:",inline"`
    73  		metav1.ObjectMeta `json:"metadata,omitempty"`
    74  		Spec              Spec `json:"spec"`
    75  	}
    76  
    77  	// V3Resource will always have a '/suffix' in front of it's property
    78  	// This type is used for testing conversion webhooks
    79  	//
    80  	// +k8s:deepcopy-gen=true
    81  	// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
    82  	V3Resource struct {
    83  		metav1.TypeMeta   `json:",inline"`
    84  		metav1.ObjectMeta `json:"metadata,omitempty"`
    85  		Spec              SpecWithDefault `json:"spec"`
    86  	}
    87  
    88  	// ErrorResource explodes in various settings depending on the property
    89  	// set. Use the Error* constants
    90  	//
    91  	// This type is used for testing conversion webhooks
    92  	//
    93  	// +k8s:deepcopy-gen=true
    94  	// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
    95  	ErrorResource struct {
    96  		// We embed the V1Resource as an easy way to still marshal & unmarshal
    97  		// this type without infinite loops - since we override the methods
    98  		// in order to induce failures
    99  		V1Resource `json:",inline"`
   100  	}
   101  
   102  	// Spec holds our fancy string property
   103  	Spec struct {
   104  		Property string `json:"prop"`
   105  	}
   106  
   107  	// SpecWithDefault holds two fancy string properties
   108  	SpecWithDefault struct {
   109  		Property    string `json:"prop"`
   110  		NewProperty string `json:"defaulted_prop"`
   111  	}
   112  )
   113  
   114  var (
   115  	_ apis.Convertible = (*V1Resource)(nil)
   116  	_ apis.Convertible = (*V2Resource)(nil)
   117  	_ apis.Convertible = (*V3Resource)(nil)
   118  	_ apis.Convertible = (*ErrorResource)(nil)
   119  
   120  	_ apis.Defaultable = (*V3Resource)(nil)
   121  )
   122  
   123  // NewV1 returns a V1Resource with Spec.Property set
   124  // to prop
   125  func NewV1(prop string) *V1Resource {
   126  	return &V1Resource{
   127  		TypeMeta: metav1.TypeMeta{
   128  			Kind:       Kind,
   129  			APIVersion: Group + "/v1",
   130  		},
   131  		Spec: Spec{
   132  			Property: prop,
   133  		},
   134  	}
   135  }
   136  
   137  // NewV2 returns a V2Resource with Spec.Property set
   138  // to 'prefix/' + prop
   139  func NewV2(prop string) *V2Resource {
   140  	return &V2Resource{
   141  		TypeMeta: metav1.TypeMeta{
   142  			Kind:       Kind,
   143  			APIVersion: Group + "/v2",
   144  		},
   145  		Spec: Spec{
   146  			Property: "prefix/" + prop,
   147  		},
   148  	}
   149  }
   150  
   151  // NewV3 returns a V3Resource with Spec.Property set
   152  // to prop + '/suffix'
   153  func NewV3(prop string) *V3Resource {
   154  	v3 := &V3Resource{
   155  		TypeMeta: metav1.TypeMeta{
   156  			Kind:       Kind,
   157  			APIVersion: Group + "/v3",
   158  		},
   159  		Spec: SpecWithDefault{
   160  			Property: prop + "/suffix",
   161  		},
   162  	}
   163  	v3.SetDefaults(context.Background())
   164  	return v3
   165  }
   166  
   167  // NewErrorResource returns an ErrorResource with Spec.Property set
   168  // to failure
   169  func NewErrorResource(failure string) *ErrorResource {
   170  	return &ErrorResource{
   171  		V1Resource: V1Resource{
   172  			TypeMeta: metav1.TypeMeta{
   173  				Kind:       Kind,
   174  				APIVersion: Group + "/error",
   175  			},
   176  			Spec: Spec{
   177  				Property: failure,
   178  			},
   179  		},
   180  	}
   181  }
   182  
   183  // ConvertTo implements apis.Convertible
   184  func (r *V1Resource) ConvertTo(ctx context.Context, to apis.Convertible) error {
   185  	switch sink := to.(type) {
   186  	case *V2Resource:
   187  		sink.Spec.Property = "prefix/" + r.Spec.Property
   188  	case *V3Resource:
   189  		sink.Spec.Property = r.Spec.Property + "/suffix"
   190  	case *ErrorResource:
   191  		sink.Spec.Property = r.Spec.Property
   192  	case *V1Resource:
   193  		sink.Spec.Property = r.Spec.Property
   194  	default:
   195  		return fmt.Errorf("unsupported type %T", sink)
   196  	}
   197  	return nil
   198  }
   199  
   200  // ConvertFrom implements apis.Convertible
   201  func (r *V1Resource) ConvertFrom(ctx context.Context, from apis.Convertible) error {
   202  	switch source := from.(type) {
   203  	case *V2Resource:
   204  		r.Spec.Property = strings.TrimPrefix(source.Spec.Property, "prefix/")
   205  	case *V3Resource:
   206  		r.Spec.Property = strings.TrimSuffix(source.Spec.Property, "/suffix")
   207  	case *ErrorResource:
   208  		r.Spec.Property = source.Spec.Property
   209  	case *V1Resource:
   210  		r.Spec.Property = source.Spec.Property
   211  	default:
   212  		return fmt.Errorf("unsupported type %T", source)
   213  	}
   214  	return nil
   215  }
   216  
   217  // SetDefaults implements apis.Defaultable
   218  func (r *V3Resource) SetDefaults(ctx context.Context) {
   219  	if r.Spec.NewProperty == "" {
   220  		r.Spec.NewProperty = "defaulted"
   221  	}
   222  }
   223  
   224  // ConvertTo implements apis.Convertible
   225  func (*V2Resource) ConvertTo(ctx context.Context, to apis.Convertible) error {
   226  	panic("unimplemented")
   227  }
   228  
   229  // ConvertFrom implements apis.Convertible
   230  func (*V2Resource) ConvertFrom(ctx context.Context, from apis.Convertible) error {
   231  	panic("unimplemented")
   232  }
   233  
   234  // ConvertTo implements apis.Convertible
   235  func (*V3Resource) ConvertTo(ctx context.Context, to apis.Convertible) error {
   236  	panic("unimplemented")
   237  }
   238  
   239  // ConvertFrom implements apis.Convertible
   240  func (*V3Resource) ConvertFrom(ctx context.Context, from apis.Convertible) error {
   241  	panic("unimplemented")
   242  }
   243  
   244  // ConvertTo implements apis.Convertible
   245  func (e *ErrorResource) ConvertTo(ctx context.Context, to apis.Convertible) error {
   246  	if e.Spec.Property == ErrorConvertTo {
   247  		return errors.New("boooom - convert up")
   248  	}
   249  
   250  	return e.V1Resource.ConvertTo(ctx, to)
   251  }
   252  
   253  // ConvertFrom implements apis.Convertible
   254  func (e *ErrorResource) ConvertFrom(ctx context.Context, from apis.Convertible) error {
   255  	err := e.V1Resource.ConvertFrom(ctx, from)
   256  
   257  	if err == nil && e.Spec.Property == ErrorConvertFrom {
   258  		err = errors.New("boooom - convert down")
   259  	}
   260  	return err
   261  }
   262  
   263  // UnmarshalJSON implements json.Unmarshaler
   264  func (e *ErrorResource) UnmarshalJSON(data []byte) (err error) {
   265  	err = json.Unmarshal(data, &e.V1Resource)
   266  	if err == nil && e.Spec.Property == ErrorUnmarshal {
   267  		err = errors.New("boooom - unmarshal json")
   268  	}
   269  	return err
   270  }
   271  
   272  // MarshalJSON implements json.Marshaler
   273  func (e *ErrorResource) MarshalJSON() ([]byte, error) {
   274  	if e.Spec.Property == ErrorMarshal {
   275  		return nil, errors.New("boooom - marshal json")
   276  	}
   277  	return json.Marshal(e.V1Resource)
   278  }