Skip to content

Commit e552aee

Browse files
authored
Merge pull request #1525 from vincepri/envtest-conversion-webhook
⚠️ Envtest should modify CRDs appropriately when using webhooks
2 parents ae50d4c + 2bfe5cf commit e552aee

File tree

3 files changed

+74
-18
lines changed

3 files changed

+74
-18
lines changed

pkg/envtest/crd.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ import (
2020
"bufio"
2121
"bytes"
2222
"context"
23+
"fmt"
2324
"io"
2425
"io/ioutil"
2526
"os"
2627
"path/filepath"
2728
"time"
2829

30+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
31+
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
2932
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
3033
apierrors "k8s.io/apimachinery/pkg/api/errors"
3134
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -35,6 +38,7 @@ import (
3538
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
3639
"k8s.io/client-go/rest"
3740
"k8s.io/client-go/util/retry"
41+
"k8s.io/utils/pointer"
3842
"sigs.k8s.io/controller-runtime/pkg/client"
3943
"sigs.k8s.io/yaml"
4044
)
@@ -60,6 +64,13 @@ type CRDInstallOptions struct {
6064
// uninstalled when terminating the test environment.
6165
// Defaults to false.
6266
CleanUpAfterUse bool
67+
68+
// WebhookOptions contains the conversion webhook information to install
69+
// on the CRDs. This field is usually inherited by the EnvTest options.
70+
//
71+
// If you're passing this field manually, you need to make sure that
72+
// the CA information and host port is filled in properly.
73+
WebhookOptions WebhookInstallOptions
6374
}
6475

6576
const defaultPollInterval = 100 * time.Millisecond
@@ -74,6 +85,10 @@ func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]client.Objec
7485
return nil, err
7586
}
7687

88+
if err := modifyConversionWebhooks(options.CRDs, options.WebhookOptions); err != nil {
89+
return nil, err
90+
}
91+
7792
// Create the CRDs in the apiserver
7893
if err := CreateCRDs(config, options.CRDs); err != nil {
7994
return options.CRDs, err
@@ -340,6 +355,37 @@ func renderCRDs(options *CRDInstallOptions) ([]client.Object, error) {
340355
return res, nil
341356
}
342357

358+
func modifyConversionWebhooks(crds []client.Object, webhookOptions WebhookInstallOptions) error {
359+
if len(webhookOptions.LocalServingCAData) == 0 {
360+
return nil
361+
}
362+
363+
// generate host port.
364+
hostPort, err := webhookOptions.generateHostPort()
365+
if err != nil {
366+
return err
367+
}
368+
url := pointer.StringPtr(fmt.Sprintf("https://%s/convert", hostPort))
369+
370+
for _, crd := range crds {
371+
switch c := crd.(type) {
372+
case *apiextensionsv1beta1.CustomResourceDefinition:
373+
c.Spec.Conversion.WebhookClientConfig.Service = nil
374+
c.Spec.Conversion.WebhookClientConfig = &apiextensionsv1beta1.WebhookClientConfig{
375+
CABundle: webhookOptions.LocalServingCAData,
376+
}
377+
case *apiextensionsv1.CustomResourceDefinition:
378+
c.Spec.Conversion.Webhook.ClientConfig.Service = nil
379+
c.Spec.Conversion.Webhook.ClientConfig = &apiextensionsv1.WebhookClientConfig{
380+
URL: url,
381+
CABundle: webhookOptions.LocalServingCAData,
382+
}
383+
}
384+
}
385+
386+
return nil
387+
}
388+
343389
// readCRDs reads the CRDs from files and Unmarshals them into structs
344390
func readCRDs(basePath string, files []os.FileInfo) ([]*unstructured.Unstructured, error) {
345391
var crds []*unstructured.Unstructured

pkg/envtest/server.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,12 @@ func (te *Environment) Start() (*rest.Config, error) {
263263
}
264264
}
265265

266+
// Call PrepWithoutInstalling to setup certificates first
267+
// and have them available to patch CRD conversion webhook as well.
268+
if err := te.WebhookInstallOptions.PrepWithoutInstalling(); err != nil {
269+
return nil, err
270+
}
271+
266272
log.V(1).Info("installing CRDs")
267273
te.CRDInstallOptions.CRDs = mergeCRDs(te.CRDInstallOptions.CRDs, te.CRDs)
268274
te.CRDInstallOptions.Paths = mergePaths(te.CRDInstallOptions.Paths, te.CRDDirectoryPaths)
@@ -274,9 +280,10 @@ func (te *Environment) Start() (*rest.Config, error) {
274280
te.CRDs = crds
275281

276282
log.V(1).Info("installing webhooks")
277-
err = te.WebhookInstallOptions.Install(te.Config)
278-
279-
return te.Config, err
283+
if err := te.WebhookInstallOptions.Install(te.Config); err != nil {
284+
return nil, err
285+
}
286+
return te.Config, nil
280287
}
281288

282289
func (te *Environment) startControlPlane() error {

pkg/envtest/webhook.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ type WebhookInstallOptions struct {
8080
// ModifyWebhookDefinitions modifies webhook definitions by:
8181
// - applying CABundle based on the provided tinyca
8282
// - if webhook client config uses service spec, it's removed and replaced with direct url
83-
func (o *WebhookInstallOptions) ModifyWebhookDefinitions(caData []byte) error {
83+
func (o *WebhookInstallOptions) ModifyWebhookDefinitions() error {
84+
caData := o.LocalServingCAData
85+
86+
// generate host port.
8487
hostPort, err := o.generateHostPort()
8588
if err != nil {
8689
return err
@@ -161,16 +164,14 @@ func (o *WebhookInstallOptions) generateHostPort() (string, error) {
161164
// controller-runtime, where we need a random host-port & caData for webhook
162165
// tests, but may be useful in similar scenarios.
163166
func (o *WebhookInstallOptions) PrepWithoutInstalling() error {
164-
hookCA, err := o.setupCA()
165-
if err != nil {
167+
if err := o.setupCA(); err != nil {
166168
return err
167169
}
168170
if err := parseWebhook(o); err != nil {
169171
return err
170172
}
171173

172-
err = o.ModifyWebhookDefinitions(hookCA)
173-
if err != nil {
174+
if err := o.ModifyWebhookDefinitions(); err != nil {
174175
return err
175176
}
176177

@@ -179,8 +180,10 @@ func (o *WebhookInstallOptions) PrepWithoutInstalling() error {
179180

180181
// Install installs specified webhooks to the API server
181182
func (o *WebhookInstallOptions) Install(config *rest.Config) error {
182-
if err := o.PrepWithoutInstalling(); err != nil {
183-
return err
183+
if len(o.LocalServingCAData) == 0 {
184+
if err := o.PrepWithoutInstalling(); err != nil {
185+
return err
186+
}
184187
}
185188

186189
if err := createWebhooks(config, o.MutatingWebhooks, o.ValidatingWebhooks); err != nil {
@@ -269,38 +272,38 @@ func (p *webhookPoller) poll() (done bool, err error) {
269272
}
270273

271274
// setupCA creates CA for testing and writes them to disk
272-
func (o *WebhookInstallOptions) setupCA() ([]byte, error) {
275+
func (o *WebhookInstallOptions) setupCA() error {
273276
hookCA, err := integration.NewTinyCA()
274277
if err != nil {
275-
return nil, fmt.Errorf("unable to set up webhook CA: %v", err)
278+
return fmt.Errorf("unable to set up webhook CA: %v", err)
276279
}
277280

278281
names := []string{"localhost", o.LocalServingHost, o.LocalServingHostExternalName}
279282
hookCert, err := hookCA.NewServingCert(names...)
280283
if err != nil {
281-
return nil, fmt.Errorf("unable to set up webhook serving certs: %v", err)
284+
return fmt.Errorf("unable to set up webhook serving certs: %v", err)
282285
}
283286

284287
localServingCertsDir, err := ioutil.TempDir("", "envtest-serving-certs-")
285288
o.LocalServingCertDir = localServingCertsDir
286289
if err != nil {
287-
return nil, fmt.Errorf("unable to create directory for webhook serving certs: %v", err)
290+
return fmt.Errorf("unable to create directory for webhook serving certs: %v", err)
288291
}
289292

290293
certData, keyData, err := hookCert.AsBytes()
291294
if err != nil {
292-
return nil, fmt.Errorf("unable to marshal webhook serving certs: %v", err)
295+
return fmt.Errorf("unable to marshal webhook serving certs: %v", err)
293296
}
294297

295298
if err := ioutil.WriteFile(filepath.Join(localServingCertsDir, "tls.crt"), certData, 0640); err != nil {
296-
return nil, fmt.Errorf("unable to write webhook serving cert to disk: %v", err)
299+
return fmt.Errorf("unable to write webhook serving cert to disk: %v", err)
297300
}
298301
if err := ioutil.WriteFile(filepath.Join(localServingCertsDir, "tls.key"), keyData, 0640); err != nil {
299-
return nil, fmt.Errorf("unable to write webhook serving key to disk: %v", err)
302+
return fmt.Errorf("unable to write webhook serving key to disk: %v", err)
300303
}
301304

302305
o.LocalServingCAData = certData
303-
return certData, nil
306+
return err
304307
}
305308

306309
func createWebhooks(config *rest.Config, mutHooks []client.Object, valHooks []client.Object) error {

0 commit comments

Comments
 (0)