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  `