Skip to content

Commit 70108bf

Browse files
authored
Merge pull request #1710 from prafull01/webhook-test
✨ Add scaffolding for the webhook test suite (go/v3-alpha)
2 parents 30d9998 + c09442c commit 70108bf

File tree

9 files changed

+818
-0
lines changed

9 files changed

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

pkg/plugin/v3/scaffolds/webhook.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,15 @@ You need to implement the conversion.Hub and conversion.Convertible interfaces f
8888
return err
8989
}
9090

91+
// TODO: Add test suite for conversion webhook after #1664 has been merged & conversion tests supported in envtest.
92+
if s.defaulting || s.validation {
93+
if err := machinery.NewScaffold().Execute(
94+
s.newUniverse(),
95+
&api.WebhookSuite{},
96+
); err != nil {
97+
return err
98+
}
99+
}
100+
91101
return nil
92102
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package v1
2+
3+
import (
4+
"crypto/tls"
5+
"fmt"
6+
"net"
7+
"path/filepath"
8+
"testing"
9+
"time"
10+
11+
. "github.com/onsi/ginkgo"
12+
. "github.com/onsi/gomega"
13+
14+
admissionv1beta1 "k8s.io/api/admission/v1beta1"
15+
// +kubebuilder:scaffold:imports
16+
"k8s.io/apimachinery/pkg/runtime"
17+
"k8s.io/client-go/rest"
18+
ctrl "sigs.k8s.io/controller-runtime"
19+
"sigs.k8s.io/controller-runtime/pkg/client"
20+
"sigs.k8s.io/controller-runtime/pkg/envtest"
21+
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
22+
logf "sigs.k8s.io/controller-runtime/pkg/log"
23+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
24+
)
25+
26+
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
27+
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
28+
29+
var cfg *rest.Config
30+
var k8sClient client.Client
31+
var testEnv *envtest.Environment
32+
var stopCh = make(chan struct{})
33+
34+
func TestAPIs(t *testing.T) {
35+
RegisterFailHandler(Fail)
36+
37+
RunSpecsWithDefaultAndCustomReporters(t,
38+
"Webhook Suite",
39+
[]Reporter{printer.NewlineReporter{}})
40+
}
41+
42+
var _ = BeforeSuite(func(done Done) {
43+
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
44+
45+
By("bootstrapping test environment")
46+
testEnv = &envtest.Environment{
47+
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
48+
WebhookInstallOptions: envtest.WebhookInstallOptions{
49+
DirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
50+
},
51+
}
52+
53+
var err error
54+
cfg, err = testEnv.Start()
55+
Expect(err).NotTo(HaveOccurred())
56+
Expect(cfg).NotTo(BeNil())
57+
58+
scheme := runtime.NewScheme()
59+
err = AddToScheme(scheme)
60+
Expect(err).NotTo(HaveOccurred())
61+
62+
err = admissionv1beta1.AddToScheme(scheme)
63+
Expect(err).NotTo(HaveOccurred())
64+
65+
// +kubebuilder:scaffold:scheme
66+
67+
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
68+
Expect(err).NotTo(HaveOccurred())
69+
Expect(k8sClient).NotTo(BeNil())
70+
71+
// start webhook server using Manager
72+
webhookInstallOptions := &testEnv.WebhookInstallOptions
73+
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
74+
Scheme: scheme,
75+
Host: webhookInstallOptions.LocalServingHost,
76+
Port: webhookInstallOptions.LocalServingPort,
77+
CertDir: webhookInstallOptions.LocalServingCertDir,
78+
LeaderElection: false,
79+
MetricsBindAddress: "0",
80+
})
81+
Expect(err).NotTo(HaveOccurred())
82+
83+
err = (&Captain{}).SetupWebhookWithManager(mgr)
84+
Expect(err).NotTo(HaveOccurred())
85+
86+
// +kubebuilder:scaffold:webhook
87+
88+
go func() {
89+
err = mgr.Start(stopCh)
90+
if err != nil {
91+
Expect(err).NotTo(HaveOccurred())
92+
}
93+
}()
94+
95+
// wait for the webhook server to get ready
96+
dialer := &net.Dialer{Timeout: time.Second}
97+
addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
98+
Eventually(func() error {
99+
conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
100+
if err != nil {
101+
return err
102+
}
103+
conn.Close()
104+
return nil
105+
}).Should(Succeed())
106+
107+
close(done)
108+
}, 60)
109+
110+
var _ = AfterSuite(func() {
111+
close(stopCh)
112+
By("tearing down the test environment")
113+
err := testEnv.Stop()
114+
Expect(err).NotTo(HaveOccurred())
115+
})

0 commit comments

Comments
 (0)