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 }