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