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  `