sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/plugins/golang/v3/scaffolds/internal/templates/api/webhook_suitetest.go (about) 1 /* 2 Copyright 2022 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 api 18 19 import ( 20 "fmt" 21 "path/filepath" 22 23 "sigs.k8s.io/kubebuilder/v3/pkg/machinery" 24 ) 25 26 var _ machinery.Template = &WebhookSuite{} 27 var _ machinery.Inserter = &WebhookSuite{} 28 29 // WebhookSuite scaffolds the file that sets up the webhook tests 30 type WebhookSuite struct { //nolint:maligned 31 machinery.TemplateMixin 32 machinery.MultiGroupMixin 33 machinery.BoilerplateMixin 34 machinery.ResourceMixin 35 36 // todo: currently is not possible to know if an API was or not scaffolded. We can fix it when #1826 be addressed 37 WireResource bool 38 39 // BaseDirectoryRelativePath define the Path for the base directory when it is multigroup 40 BaseDirectoryRelativePath string 41 } 42 43 // SetTemplateDefaults implements file.Template 44 func (f *WebhookSuite) SetTemplateDefaults() error { 45 if f.Path == "" { 46 if f.MultiGroup { 47 if f.Resource.Group != "" { 48 f.Path = filepath.Join("apis", "%[group]", "%[version]", "webhook_suite_test.go") 49 } else { 50 f.Path = filepath.Join("apis", "%[version]", "webhook_suite_test.go") 51 } 52 } else { 53 f.Path = filepath.Join("api", "%[version]", "webhook_suite_test.go") 54 } 55 } 56 f.Path = f.Resource.Replacer().Replace(f.Path) 57 58 f.TemplateBody = fmt.Sprintf(webhookTestSuiteTemplate, 59 machinery.NewMarkerFor(f.Path, importMarker), 60 admissionImportAlias, 61 machinery.NewMarkerFor(f.Path, addSchemeMarker), 62 machinery.NewMarkerFor(f.Path, addWebhookManagerMarker), 63 "%s", 64 "%d", 65 ) 66 67 // If is multigroup the path needs to be ../../.. since it has the group dir. 68 f.BaseDirectoryRelativePath = `"..", ".."` 69 if f.MultiGroup && f.Resource.Group != "" { 70 f.BaseDirectoryRelativePath = `"..", "..",".."` 71 } 72 73 return nil 74 } 75 76 const ( 77 // TODO: admission webhook versions should be based on the input of the user. For More Info #1664 78 admissionImportAlias = "admissionv1beta1" 79 admissionPath = "k8s.io/api/admission/v1beta1" 80 importMarker = "imports" 81 addWebhookManagerMarker = "webhook" 82 addSchemeMarker = "scheme" 83 ) 84 85 // GetMarkers implements file.Inserter 86 func (f *WebhookSuite) GetMarkers() []machinery.Marker { 87 return []machinery.Marker{ 88 machinery.NewMarkerFor(f.Path, importMarker), 89 machinery.NewMarkerFor(f.Path, addSchemeMarker), 90 machinery.NewMarkerFor(f.Path, addWebhookManagerMarker), 91 } 92 } 93 94 const ( 95 apiImportCodeFragment = `%s "%s" 96 ` 97 98 addWebhookManagerCodeFragment = `err = (&%s{}).SetupWebhookWithManager(mgr) 99 Expect(err).NotTo(HaveOccurred()) 100 101 ` 102 ) 103 104 // GetCodeFragments implements file.Inserter 105 func (f *WebhookSuite) GetCodeFragments() machinery.CodeFragmentsMap { 106 fragments := make(machinery.CodeFragmentsMap, 3) 107 108 // Generate import code fragments 109 imports := make([]string, 0) 110 imports = append(imports, fmt.Sprintf(apiImportCodeFragment, admissionImportAlias, admissionPath)) 111 112 // Generate add scheme code fragments 113 addScheme := make([]string, 0) 114 115 // Generate add webhookManager code fragments 116 addWebhookManager := make([]string, 0) 117 addWebhookManager = append(addWebhookManager, fmt.Sprintf(addWebhookManagerCodeFragment, f.Resource.Kind)) 118 119 // Only store code fragments in the map if the slices are non-empty 120 if len(addWebhookManager) != 0 { 121 fragments[machinery.NewMarkerFor(f.Path, addWebhookManagerMarker)] = addWebhookManager 122 } 123 if len(imports) != 0 { 124 fragments[machinery.NewMarkerFor(f.Path, importMarker)] = imports 125 } 126 if len(addScheme) != 0 { 127 fragments[machinery.NewMarkerFor(f.Path, addSchemeMarker)] = addScheme 128 } 129 130 return fragments 131 } 132 133 const webhookTestSuiteTemplate = `{{ .Boilerplate }} 134 135 package {{ .Resource.Version }} 136 137 import ( 138 "context" 139 "path/filepath" 140 "testing" 141 "fmt" 142 143 . "github.com/onsi/ginkgo/v2" 144 . "github.com/onsi/gomega" 145 %s 146 "k8s.io/client-go/kubernetes/scheme" 147 "k8s.io/client-go/rest" 148 "k8s.io/apimachinery/pkg/runtime" 149 ctrl "sigs.k8s.io/controller-runtime" 150 "sigs.k8s.io/controller-runtime/pkg/client" 151 "sigs.k8s.io/controller-runtime/pkg/envtest" 152 logf "sigs.k8s.io/controller-runtime/pkg/log" 153 "sigs.k8s.io/controller-runtime/pkg/log/zap" 154 ) 155 156 // These tests use Ginkgo (BDD-style Go testing framework). Refer to 157 // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 158 159 var cfg *rest.Config 160 var k8sClient client.Client 161 var testEnv *envtest.Environment 162 var ctx context.Context 163 var cancel context.CancelFunc 164 165 func TestAPIs(t *testing.T) { 166 RegisterFailHandler(Fail) 167 168 RunSpecs(t, "Webhook Suite") 169 } 170 171 var _ = BeforeSuite(func() { 172 logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 173 174 ctx, cancel = context.WithCancel(context.TODO()) 175 176 By("bootstrapping test environment") 177 testEnv = &envtest.Environment{ 178 CRDDirectoryPaths: []string{filepath.Join({{ .BaseDirectoryRelativePath }}, "config", "crd", "bases")}, 179 ErrorIfCRDPathMissing: {{ .WireResource }}, 180 WebhookInstallOptions: envtest.WebhookInstallOptions{ 181 Paths: []string{filepath.Join({{ .BaseDirectoryRelativePath }}, "config", "webhook")}, 182 }, 183 } 184 185 var err error 186 // cfg is defined in this file globally. 187 cfg, err = testEnv.Start() 188 Expect(err).NotTo(HaveOccurred()) 189 Expect(cfg).NotTo(BeNil()) 190 191 scheme := runtime.NewScheme() 192 err = AddToScheme(scheme) 193 Expect(err).NotTo(HaveOccurred()) 194 195 err = %s.AddToScheme(scheme) 196 Expect(err).NotTo(HaveOccurred()) 197 198 %s 199 200 k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) 201 Expect(err).NotTo(HaveOccurred()) 202 Expect(k8sClient).NotTo(BeNil()) 203 204 // start webhook server using Manager 205 webhookInstallOptions := &testEnv.WebhookInstallOptions 206 mgr, err := ctrl.NewManager(cfg, ctrl.Options{ 207 Scheme: scheme, 208 Host: webhookInstallOptions.LocalServingHost, 209 Port: webhookInstallOptions.LocalServingPort, 210 CertDir: webhookInstallOptions.LocalServingCertDir, 211 LeaderElection: false, 212 MetricsBindAddress: "0", 213 }) 214 Expect(err).NotTo(HaveOccurred()) 215 216 %s 217 218 go func() { 219 defer GinkgoRecover() 220 err = mgr.Start(ctx) 221 Expect(err).NotTo(HaveOccurred()) 222 }() 223 224 // wait for the webhook server to get ready 225 dialer := &net.Dialer{Timeout: time.Second} 226 addrPort := fmt.Sprintf("%s:%s", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) 227 Eventually(func() error { 228 conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) 229 if err != nil { 230 return err 231 } 232 conn.Close() 233 return nil 234 }).Should(Succeed()) 235 236 }) 237 238 var _ = AfterSuite(func() { 239 cancel() 240 By("tearing down the test environment") 241 err := testEnv.Stop() 242 Expect(err).NotTo(HaveOccurred()) 243 }) 244 `