sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/model/resource/resource_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes 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 resource 18 19 import ( 20 . "github.com/onsi/ginkgo/v2" 21 . "github.com/onsi/gomega" 22 ) 23 24 //nolint:dupl 25 var _ = Describe("Resource", func() { 26 const ( 27 group = "group" 28 domain = "test.io" 29 version = "v1" 30 kind = "Kind" 31 plural = "kinds" 32 v1beta1 = "v1beta1" 33 ) 34 35 var ( 36 gvk = GVK{ 37 Group: group, 38 Domain: domain, 39 Version: version, 40 Kind: kind, 41 } 42 res = Resource{ 43 GVK: gvk, 44 Plural: plural, 45 } 46 ) 47 48 Context("Validate", func() { 49 It("should succeed for a valid Resource", func() { 50 Expect(res.Validate()).To(Succeed()) 51 }) 52 53 DescribeTable("should fail for invalid Resources", 54 func(res Resource) { Expect(res.Validate()).NotTo(Succeed()) }, 55 // Ensure that the rest of the fields are valid to check each part 56 Entry("invalid GVK", Resource{GVK: GVK{}, Plural: "plural"}), 57 Entry("invalid Plural", Resource{GVK: gvk, Plural: "Plural"}), 58 Entry("invalid API", Resource{GVK: gvk, Plural: "plural", API: &API{CRDVersion: "1"}}), 59 Entry("invalid Webhooks", Resource{GVK: gvk, Plural: "plural", Webhooks: &Webhooks{WebhookVersion: "1"}}), 60 ) 61 }) 62 63 Context("compound field", func() { 64 const ( 65 safeDomain = "testio" 66 groupVersion = group + version 67 domainVersion = safeDomain + version 68 safeGroup = "mygroup" 69 safeAlias = safeGroup + version 70 ) 71 72 var ( 73 resNoGroup = Resource{ 74 GVK: GVK{ 75 // Empty group 76 Domain: domain, 77 Version: version, 78 Kind: kind, 79 }, 80 } 81 resNoDomain = Resource{ 82 GVK: GVK{ 83 Group: group, 84 // Empty domain 85 Version: version, 86 Kind: kind, 87 }, 88 } 89 resHyphenGroup = Resource{ 90 GVK: GVK{ 91 Group: "my-group", 92 Domain: domain, 93 Version: version, 94 Kind: kind, 95 }, 96 } 97 resDotGroup = Resource{ 98 GVK: GVK{ 99 Group: "my.group", 100 Domain: domain, 101 Version: version, 102 Kind: kind, 103 }, 104 } 105 ) 106 107 DescribeTable("PackageName should return the correct string", 108 func(res Resource, packageName string) { Expect(res.PackageName()).To(Equal(packageName)) }, 109 Entry("fully qualified resource", res, group), 110 Entry("empty group name", resNoGroup, safeDomain), 111 Entry("empty domain", resNoDomain, group), 112 Entry("hyphen-containing group", resHyphenGroup, safeGroup), 113 Entry("dot-containing group", resDotGroup, safeGroup), 114 ) 115 116 DescribeTable("ImportAlias", 117 func(res Resource, importAlias string) { Expect(res.ImportAlias()).To(Equal(importAlias)) }, 118 Entry("fully qualified resource", res, groupVersion), 119 Entry("empty group name", resNoGroup, domainVersion), 120 Entry("empty domain", resNoDomain, groupVersion), 121 Entry("hyphen-containing group", resHyphenGroup, safeAlias), 122 Entry("dot-containing group", resDotGroup, safeAlias), 123 ) 124 }) 125 126 Context("part check", func() { 127 Context("HasAPI", func() { 128 It("should return true if the API is scaffolded", func() { 129 Expect(Resource{API: &API{CRDVersion: "v1"}}.HasAPI()).To(BeTrue()) 130 }) 131 132 DescribeTable("should return false if the API is not scaffolded", 133 func(res Resource) { Expect(res.HasAPI()).To(BeFalse()) }, 134 Entry("nil API", Resource{API: nil}), 135 Entry("empty CRD version", Resource{API: &API{}}), 136 ) 137 }) 138 139 Context("HasController", func() { 140 It("should return true if the controller is scaffolded", func() { 141 Expect(Resource{Controller: true}.HasController()).To(BeTrue()) 142 }) 143 144 It("should return false if the controller is not scaffolded", func() { 145 Expect(Resource{Controller: false}.HasController()).To(BeFalse()) 146 }) 147 }) 148 149 Context("HasDefaultingWebhook", func() { 150 It("should return true if the defaulting webhook is scaffolded", func() { 151 Expect(Resource{Webhooks: &Webhooks{Defaulting: true}}.HasDefaultingWebhook()).To(BeTrue()) 152 }) 153 154 DescribeTable("should return false if the defaulting webhook is not scaffolded", 155 func(res Resource) { Expect(res.HasDefaultingWebhook()).To(BeFalse()) }, 156 Entry("nil webhooks", Resource{Webhooks: nil}), 157 Entry("no defaulting", Resource{Webhooks: &Webhooks{Defaulting: false}}), 158 ) 159 }) 160 161 Context("HasValidationWebhook", func() { 162 It("should return true if the validation webhook is scaffolded", func() { 163 Expect(Resource{Webhooks: &Webhooks{Validation: true}}.HasValidationWebhook()).To(BeTrue()) 164 }) 165 166 DescribeTable("should return false if the validation webhook is not scaffolded", 167 func(res Resource) { Expect(res.HasValidationWebhook()).To(BeFalse()) }, 168 Entry("nil webhooks", Resource{Webhooks: nil}), 169 Entry("no validation", Resource{Webhooks: &Webhooks{Validation: false}}), 170 ) 171 }) 172 173 Context("HasConversionWebhook", func() { 174 It("should return true if the conversion webhook is scaffolded", func() { 175 Expect(Resource{Webhooks: &Webhooks{Conversion: true}}.HasConversionWebhook()).To(BeTrue()) 176 }) 177 178 DescribeTable("should return false if the conversion webhook is not scaffolded", 179 func(res Resource) { Expect(res.HasConversionWebhook()).To(BeFalse()) }, 180 Entry("nil webhooks", Resource{Webhooks: nil}), 181 Entry("no conversion", Resource{Webhooks: &Webhooks{Conversion: false}}), 182 ) 183 }) 184 185 Context("IsRegularPlural", func() { 186 It("should return true if the regular plural form is used", func() { 187 Expect(res.IsRegularPlural()).To(BeTrue()) 188 }) 189 190 It("should return false if an irregular plural form is used", func() { 191 Expect(Resource{GVK: gvk, Plural: "types"}.IsRegularPlural()).To(BeFalse()) 192 }) 193 }) 194 }) 195 196 Context("Copy", func() { 197 const ( 198 path = "api/v1" 199 crdVersion = "v1" 200 webhookVersion = "v1" 201 ) 202 203 res := Resource{ 204 GVK: gvk, 205 Plural: plural, 206 Path: path, 207 API: &API{ 208 CRDVersion: crdVersion, 209 Namespaced: true, 210 }, 211 Controller: true, 212 Webhooks: &Webhooks{ 213 WebhookVersion: webhookVersion, 214 Defaulting: true, 215 Validation: true, 216 Conversion: true, 217 }, 218 } 219 220 It("should return an exact copy", func() { 221 other := res.Copy() 222 Expect(other.Group).To(Equal(res.Group)) 223 Expect(other.Domain).To(Equal(res.Domain)) 224 Expect(other.Version).To(Equal(res.Version)) 225 Expect(other.Kind).To(Equal(res.Kind)) 226 Expect(other.Plural).To(Equal(res.Plural)) 227 Expect(other.Path).To(Equal(res.Path)) 228 Expect(other.API).NotTo(BeNil()) 229 Expect(other.API.CRDVersion).To(Equal(res.API.CRDVersion)) 230 Expect(other.API.Namespaced).To(Equal(res.API.Namespaced)) 231 Expect(other.Controller).To(Equal(res.Controller)) 232 Expect(other.Webhooks).NotTo(BeNil()) 233 Expect(other.Webhooks.WebhookVersion).To(Equal(res.Webhooks.WebhookVersion)) 234 Expect(other.Webhooks.Defaulting).To(Equal(res.Webhooks.Defaulting)) 235 Expect(other.Webhooks.Validation).To(Equal(res.Webhooks.Validation)) 236 Expect(other.Webhooks.Conversion).To(Equal(res.Webhooks.Conversion)) 237 }) 238 239 It("modifying the copy should not affect the original", func() { 240 other := res.Copy() 241 other.Group = "group2" 242 other.Domain = "other.domain" 243 other.Version = "v2" 244 other.Kind = "kind2" 245 other.Plural = "kind2s" 246 other.Path = "api/v2" 247 other.API.CRDVersion = v1beta1 248 other.API.Namespaced = false 249 other.API = nil // Change fields before changing pointer 250 other.Controller = false 251 other.Webhooks.WebhookVersion = v1beta1 252 other.Webhooks.Defaulting = false 253 other.Webhooks.Validation = false 254 other.Webhooks.Conversion = false 255 other.Webhooks = nil // Change fields before changing pointer 256 257 Expect(res.Group).To(Equal(group)) 258 Expect(res.Domain).To(Equal(domain)) 259 Expect(res.Version).To(Equal(version)) 260 Expect(res.Kind).To(Equal(kind)) 261 Expect(res.Plural).To(Equal(plural)) 262 Expect(res.Path).To(Equal(path)) 263 Expect(res.API).NotTo(BeNil()) 264 Expect(res.API.CRDVersion).To(Equal(crdVersion)) 265 Expect(res.API.Namespaced).To(BeTrue()) 266 Expect(res.Controller).To(BeTrue()) 267 Expect(res.Webhooks).NotTo(BeNil()) 268 Expect(res.Webhooks.WebhookVersion).To(Equal(webhookVersion)) 269 Expect(res.Webhooks.Defaulting).To(BeTrue()) 270 Expect(res.Webhooks.Validation).To(BeTrue()) 271 Expect(res.Webhooks.Conversion).To(BeTrue()) 272 }) 273 }) 274 275 Context("Update", func() { 276 var r, other Resource 277 278 It("should fail for nil objects", func() { 279 var nilResource *Resource 280 Expect(nilResource.Update(other)).NotTo(Succeed()) 281 }) 282 283 It("should fail for different GVKs", func() { 284 r = Resource{GVK: gvk} 285 other = Resource{ 286 GVK: GVK{ 287 Group: group, 288 Domain: domain, 289 Version: version, 290 Kind: "OtherKind", 291 }, 292 } 293 Expect(r.Update(other)).NotTo(Succeed()) 294 }) 295 296 It("should fail for different Plurals", func() { 297 r = Resource{ 298 GVK: gvk, 299 Plural: plural, 300 } 301 other = Resource{ 302 GVK: gvk, 303 Plural: "types", 304 } 305 Expect(r.Update(other)).NotTo(Succeed()) 306 }) 307 308 It("should work for a new path", func() { 309 const path = "api/v1" 310 r = Resource{GVK: gvk} 311 other = Resource{ 312 GVK: gvk, 313 Path: path, 314 } 315 Expect(r.Update(other)).To(Succeed()) 316 Expect(r.Path).To(Equal(path)) 317 }) 318 319 It("should fail for different Paths", func() { 320 r = Resource{ 321 GVK: gvk, 322 Path: "api/v1", 323 } 324 other = Resource{ 325 GVK: gvk, 326 Path: "apis/group/v1", 327 } 328 Expect(r.Update(other)).NotTo(Succeed()) 329 }) 330 331 Context("API", func() { 332 It("should work with nil APIs", func() { 333 r = Resource{GVK: gvk} 334 other = Resource{ 335 GVK: gvk, 336 API: &API{CRDVersion: v1}, 337 } 338 Expect(r.Update(other)).To(Succeed()) 339 Expect(r.API).NotTo(BeNil()) 340 Expect(r.API.CRDVersion).To(Equal(v1)) 341 }) 342 343 It("should fail if API.Update fails", func() { 344 r = Resource{ 345 GVK: gvk, 346 API: &API{CRDVersion: v1}, 347 } 348 other = Resource{ 349 GVK: gvk, 350 API: &API{CRDVersion: v1beta1}, 351 } 352 Expect(r.Update(other)).NotTo(Succeed()) 353 }) 354 355 // The rest of the cases are tested in API.Update 356 }) 357 358 Context("Controller", func() { 359 It("should set the controller flag if provided and not previously set", func() { 360 r = Resource{GVK: gvk} 361 other = Resource{ 362 GVK: gvk, 363 Controller: true, 364 } 365 Expect(r.Update(other)).To(Succeed()) 366 Expect(r.Controller).To(BeTrue()) 367 }) 368 369 It("should keep the controller flag if previously set", func() { 370 r = Resource{ 371 GVK: gvk, 372 Controller: true, 373 } 374 375 By("not providing it") 376 other = Resource{GVK: gvk} 377 Expect(r.Update(other)).To(Succeed()) 378 Expect(r.Controller).To(BeTrue()) 379 380 By("providing it") 381 other = Resource{ 382 GVK: gvk, 383 Controller: true, 384 } 385 Expect(r.Update(other)).To(Succeed()) 386 Expect(r.Controller).To(BeTrue()) 387 }) 388 389 It("should not set the controller flag if not provided and not previously set", func() { 390 r = Resource{GVK: gvk} 391 other = Resource{GVK: gvk} 392 Expect(r.Update(other)).To(Succeed()) 393 Expect(r.Controller).To(BeFalse()) 394 }) 395 }) 396 397 Context("Webhooks", func() { 398 It("should work with nil Webhooks", func() { 399 r = Resource{GVK: gvk} 400 other = Resource{ 401 GVK: gvk, 402 Webhooks: &Webhooks{WebhookVersion: v1}, 403 } 404 Expect(r.Update(other)).To(Succeed()) 405 Expect(r.Webhooks).NotTo(BeNil()) 406 Expect(r.Webhooks.WebhookVersion).To(Equal(v1)) 407 }) 408 409 It("should fail if Webhooks.Update fails", func() { 410 r = Resource{ 411 GVK: gvk, 412 Webhooks: &Webhooks{WebhookVersion: v1}, 413 } 414 other = Resource{ 415 GVK: gvk, 416 Webhooks: &Webhooks{WebhookVersion: v1beta1}, 417 } 418 Expect(r.Update(other)).NotTo(Succeed()) 419 }) 420 421 // The rest of the cases are tested in Webhooks.Update 422 }) 423 }) 424 425 Context("Replacer", func() { 426 replacer := res.Replacer() 427 428 DescribeTable("should replace the following strings", 429 func(pattern, result string) { Expect(replacer.Replace(pattern)).To(Equal(result)) }, 430 Entry("no pattern", "version", "version"), 431 Entry("pattern `%[group]`", "%[group]", res.Group), 432 Entry("pattern `%[version]`", "%[version]", res.Version), 433 Entry("pattern `%[kind]`", "%[kind]", "kind"), 434 Entry("pattern `%[plural]`", "%[plural]", res.Plural), 435 Entry("pattern `%[package-name]`", "%[package-name]", res.PackageName()), 436 ) 437 }) 438 })