diff --git a/cmd/webhook/app/testing/BUILD.bazel b/cmd/webhook/app/testing/BUILD.bazel index af0f15c3b..cb06308e4 100644 --- a/cmd/webhook/app/testing/BUILD.bazel +++ b/cmd/webhook/app/testing/BUILD.bazel @@ -8,7 +8,7 @@ go_library( deps = [ "//cmd/webhook/app:go_default_library", "//cmd/webhook/app/options:go_default_library", - "//pkg/logs:go_default_library", + "//pkg/logs/testing:go_default_library", "//pkg/util/pki:go_default_library", "//pkg/webhook/server:go_default_library", "@com_github_spf13_pflag//:go_default_library", diff --git a/cmd/webhook/app/testing/testwebhook.go b/cmd/webhook/app/testing/testwebhook.go index 1f116db1c..e0df906e7 100644 --- a/cmd/webhook/app/testing/testwebhook.go +++ b/cmd/webhook/app/testing/testwebhook.go @@ -37,13 +37,11 @@ import ( "github.com/jetstack/cert-manager/cmd/webhook/app" "github.com/jetstack/cert-manager/cmd/webhook/app/options" - logf "github.com/jetstack/cert-manager/pkg/logs" + logtesting "github.com/jetstack/cert-manager/pkg/logs/testing" "github.com/jetstack/cert-manager/pkg/util/pki" "github.com/jetstack/cert-manager/pkg/webhook/server" ) -var log = logf.Log.WithName("webhook-server-test") - type StopFunc func() type ServerOptions struct { @@ -58,7 +56,9 @@ type ServerOptions struct { CAPEM []byte } -func StartWebhookServer(t *testing.T, ctx context.Context, args []string) (ServerOptions, StopFunc) { +func StartWebhookServer(t *testing.T, ctx context.Context, args []string, argumentsForNewServerWithOptions ...app.ServerOption) (ServerOptions, StopFunc) { + log := logtesting.TestLogger{T: t} + fs := pflag.NewFlagSet("testset", pflag.ExitOnError) webhookFlags := options.NewWebhookFlags() webhookConfig, err := options.NewWebhookConfiguration() @@ -100,7 +100,7 @@ func StartWebhookServer(t *testing.T, ctx context.Context, args []string) (Serve stopCh := make(chan struct{}) errCh := make(chan error) - srv, err := app.NewServerWithOptions(log, *webhookFlags, *webhookConfig) + srv, err := app.NewServerWithOptions(log, *webhookFlags, *webhookConfig, argumentsForNewServerWithOptions...) if err != nil { t.Fatal(err) } diff --git a/cmd/webhook/app/webhook.go b/cmd/webhook/app/webhook.go index 696c32f5a..1f2722994 100644 --- a/cmd/webhook/app/webhook.go +++ b/cmd/webhook/app/webhook.go @@ -46,7 +46,17 @@ var validationHook handlers.ValidatingAdmissionHook = handlers.NewRegistryBacked var mutationHook handlers.MutatingAdmissionHook = handlers.NewRegistryBackedMutator(logf.Log, webhook.Scheme, webhook.MutationRegistry) var conversionHook handlers.ConversionHook = handlers.NewSchemeBackedConverter(logf.Log, webhook.Scheme) -func NewServerWithOptions(log logr.Logger, _ options.WebhookFlags, opts config.WebhookConfiguration) (*server.Server, error) { +type ServerOption func(*server.Server) + +// WithConversionHandler allows you to override the handler for the `/convert` +// endpoint in tests. +func WithConversionHandler(handler handlers.ConversionHook) ServerOption { + return func(s *server.Server) { + s.ConversionWebhook = handler + } +} + +func NewServerWithOptions(log logr.Logger, _ options.WebhookFlags, opts config.WebhookConfiguration, optionFunctions ...ServerOption) (*server.Server, error) { restcfg, err := clientcmd.BuildConfigFromFlags(opts.APIServerHost, opts.KubeConfig) if err != nil { return nil, err @@ -88,7 +98,7 @@ func NewServerWithOptions(log logr.Logger, _ options.WebhookFlags, opts config.W log.V(logf.WarnLevel).Info("serving insecurely as tls certificate data not provided") } - return &server.Server{ + s := &server.Server{ ListenAddr: fmt.Sprintf(":%d", *opts.SecurePort), HealthzAddr: fmt.Sprintf(":%d", *opts.HealthzPort), EnablePprof: opts.EnablePprof, @@ -100,7 +110,11 @@ func NewServerWithOptions(log logr.Logger, _ options.WebhookFlags, opts config.W MutationWebhook: mutationHook, ConversionWebhook: conversionHook, Log: log, - }, nil + } + for _, f := range optionFunctions { + f(s) + } + return s, nil } const componentWebhook = "webhook" diff --git a/hack/update-crds.sh b/hack/update-crds.sh index 6b3dc4514..767529d9e 100755 --- a/hack/update-crds.sh +++ b/hack/update-crds.sh @@ -39,6 +39,11 @@ export PATH=$(dirname "$go"):$PATH REPO_ROOT=${BUILD_WORKSPACE_DIRECTORY} cd "${REPO_ROOT}" +"$controllergen" \ + crd \ + paths=./pkg/webhook/handlers/testdata/apis/testgroup/v{1,2}/... \ + output:crd:dir=./pkg/webhook/handlers/testdata/apis/testgroup/crds + "$controllergen" \ schemapatch:manifests=./deploy/crds \ output:dir=./deploy/crds \ diff --git a/internal/apis/config/webhook/types.go b/internal/apis/config/webhook/types.go index 4eac795fb..c08f43b6e 100644 --- a/internal/apis/config/webhook/types.go +++ b/internal/apis/config/webhook/types.go @@ -16,7 +16,9 @@ limitations under the License. package webhook -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/webhook/handlers/testdata/apis/testgroup/BUILD.bazel b/pkg/webhook/handlers/testdata/apis/testgroup/BUILD.bazel index 134d7f9a4..b2560b780 100644 --- a/pkg/webhook/handlers/testdata/apis/testgroup/BUILD.bazel +++ b/pkg/webhook/handlers/testdata/apis/testgroup/BUILD.bazel @@ -28,6 +28,7 @@ filegroup( name = "all-srcs", srcs = [ ":package-srcs", + "//pkg/webhook/handlers/testdata/apis/testgroup/crds:all-srcs", "//pkg/webhook/handlers/testdata/apis/testgroup/fuzzer:all-srcs", "//pkg/webhook/handlers/testdata/apis/testgroup/install:all-srcs", "//pkg/webhook/handlers/testdata/apis/testgroup/v1:all-srcs", diff --git a/pkg/webhook/handlers/testdata/apis/testgroup/crds/BUILD.bazel b/pkg/webhook/handlers/testdata/apis/testgroup/crds/BUILD.bazel new file mode 100644 index 000000000..6df04e38c --- /dev/null +++ b/pkg/webhook/handlers/testdata/apis/testgroup/crds/BUILD.bazel @@ -0,0 +1,13 @@ +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/webhook/handlers/testdata/apis/testgroup/crds/README.md b/pkg/webhook/handlers/testdata/apis/testgroup/crds/README.md new file mode 100644 index 000000000..5190f4928 --- /dev/null +++ b/pkg/webhook/handlers/testdata/apis/testgroup/crds/README.md @@ -0,0 +1,3 @@ +# README + +These CRDs are auto generated by `hack/update-crds.sh`. diff --git a/pkg/webhook/handlers/testdata/apis/testgroup/crds/testgroup.testing.cert-manager.io_testtypes.yaml b/pkg/webhook/handlers/testdata/apis/testgroup/crds/testgroup.testing.cert-manager.io_testtypes.yaml new file mode 100644 index 000000000..3dbcdfabe --- /dev/null +++ b/pkg/webhook/handlers/testdata/apis/testgroup/crds/testgroup.testing.cert-manager.io_testtypes.yaml @@ -0,0 +1,92 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + creationTimestamp: null + name: testtypes.testgroup.testing.cert-manager.io +spec: + group: testgroup.testing.cert-manager.io + names: + kind: TestType + listKind: TestTypeList + plural: testtypes + singular: testtype + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + testField: + description: TestField is used in tests. Validation doesn't allow this + to be set to the value of TestFieldValueNotAllowed. + type: string + testFieldImmutable: + description: TestFieldImmutable cannot be changed after being set to a + non-zero value + type: string + testFieldPtr: + type: string + required: + - metadata + - testField + - testFieldImmutable + type: object + served: true + storage: false + - name: v2 + schema: + openAPIV3Schema: + description: TestType in v2 is identical to v1, except TestFieldPtr has been + renamed to TestFieldPtrAlt + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + testField: + description: TestField is used in tests. Validation doesn't allow this + to be set to the value of TestFieldValueNotAllowed. + type: string + testFieldImmutable: + description: TestFieldImmutable cannot be changed after being set to a + non-zero value + type: string + testFieldPtrAlt: + type: string + required: + - metadata + - testField + - testFieldImmutable + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/pkg/webhook/handlers/testdata/apis/testgroup/v2/types.go b/pkg/webhook/handlers/testdata/apis/testgroup/v2/types.go index 275df8091..b2e3c5017 100644 --- a/pkg/webhook/handlers/testdata/apis/testgroup/v2/types.go +++ b/pkg/webhook/handlers/testdata/apis/testgroup/v2/types.go @@ -22,6 +22,7 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:storageversion // TestType in v2 is identical to v1, except TestFieldPtr has been renamed to TestFieldPtrAlt type TestType struct { metav1.TypeMeta `json:",inline"` diff --git a/test/integration/conversion/BUILD.bazel b/test/integration/conversion/BUILD.bazel index e10b6b41f..bb06b7eb9 100644 --- a/test/integration/conversion/BUILD.bazel +++ b/test/integration/conversion/BUILD.bazel @@ -3,20 +3,23 @@ load("@io_bazel_rules_go//go:def.bzl", "go_test") go_test( name = "go_default_test", srcs = ["conversion_test.go"], + data = [ + "//pkg/webhook/handlers/testdata/apis/testgroup/crds:all-srcs", + ], deps = [ - "//pkg/api:go_default_library", - "//pkg/apis/certmanager/v1:go_default_library", - "//pkg/apis/certmanager/v1alpha2:go_default_library", - "//pkg/apis/certmanager/v1alpha3:go_default_library", - "//pkg/apis/certmanager/v1beta1:go_default_library", - "//pkg/apis/meta/v1:go_default_library", - "//pkg/util/pki:go_default_library", + "//pkg/logs/testing:go_default_library", + "//pkg/webhook/handlers:go_default_library", + "//pkg/webhook/handlers/testdata/apis/testgroup/install:go_default_library", + "//pkg/webhook/handlers/testdata/apis/testgroup/v1:go_default_library", + "//pkg/webhook/handlers/testdata/apis/testgroup/v2:go_default_library", "//test/integration/framework:go_default_library", "@io_k8s_apimachinery//pkg/api/equality:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", + "@io_k8s_apimachinery//pkg/runtime:go_default_library", "@io_k8s_apimachinery//pkg/runtime/schema:go_default_library", "@io_k8s_sigs_controller_runtime//pkg/client:go_default_library", "@io_k8s_utils//diff:go_default_library", + "@io_k8s_utils//pointer:go_default_library", ], ) diff --git a/test/integration/conversion/conversion_test.go b/test/integration/conversion/conversion_test.go index b61a0ebd1..a05b62163 100644 --- a/test/integration/conversion/conversion_test.go +++ b/test/integration/conversion/conversion_test.go @@ -18,231 +18,83 @@ package conversion import ( "context" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/pem" "testing" "time" "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/utils/diff" + "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/jetstack/cert-manager/pkg/api" - v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" - "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2" - "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha3" - "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1beta1" - cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1" - "github.com/jetstack/cert-manager/pkg/util/pki" + logtesting "github.com/jetstack/cert-manager/pkg/logs/testing" + "github.com/jetstack/cert-manager/pkg/webhook/handlers" + testapi "github.com/jetstack/cert-manager/pkg/webhook/handlers/testdata/apis/testgroup/install" + testv1 "github.com/jetstack/cert-manager/pkg/webhook/handlers/testdata/apis/testgroup/v1" + testv2 "github.com/jetstack/cert-manager/pkg/webhook/handlers/testdata/apis/testgroup/v2" "github.com/jetstack/cert-manager/test/integration/framework" ) -func generateCSR(t *testing.T) []byte { - skRSA, err := pki.GenerateRSAPrivateKey(2048) - if err != nil { - t.Fatal(err) - } - asn1Subj, _ := asn1.Marshal(pkix.Name{ - CommonName: "test", - }.ToRDNSequence()) - template := x509.CertificateRequest{ - RawSubject: asn1Subj, - } - - csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, skRSA) - if err != nil { - t.Fatal(err) - } - - csr := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes}) - - return csr -} - func TestConversion(t *testing.T) { - testCSR := generateCSR(t) - tests := map[string]struct { input client.Object targetGVK schema.GroupVersionKind output client.Object }{ - "should convert Certificates from v1alpha2 to v1alpha3": { - input: &v1alpha2.Certificate{ + "should convert from v1 to v2": { + input: &testv1.TestType{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "default", }, - Spec: v1alpha2.CertificateSpec{ - SecretName: "something", - CommonName: "test", - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, + TestFieldPtr: pointer.StringPtr("test1"), }, - targetGVK: v1alpha3.SchemeGroupVersion.WithKind("Certificate"), - output: &v1alpha3.Certificate{ + targetGVK: testv2.SchemeGroupVersion.WithKind("TestType"), + output: &testv2.TestType{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "default", }, - Spec: v1alpha3.CertificateSpec{ - SecretName: "something", - CommonName: "test", - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, + TestFieldPtrAlt: pointer.StringPtr("test1"), }, }, - "should convert CertificateRequest from v1alpha2 to v1alpha3": { - input: &v1alpha2.CertificateRequest{ + "should convert from v2 to v1": { + input: &testv2.TestType{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "default", }, - Spec: v1alpha2.CertificateRequestSpec{ - CSRPEM: testCSR, - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, + TestFieldPtrAlt: pointer.StringPtr("test1"), }, - targetGVK: v1alpha3.SchemeGroupVersion.WithKind("CertificateRequest"), - output: &v1alpha3.CertificateRequest{ + targetGVK: testv1.SchemeGroupVersion.WithKind("TestType"), + output: &testv1.TestType{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "default", }, - Spec: v1alpha3.CertificateRequestSpec{ - CSRPEM: testCSR, - Username: "admin", - Groups: []string{"system:masters", "system:authenticated"}, - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, - }, - }, - "should convert CertificateRequest from v1alpha2 to v1": { - input: &v1alpha2.CertificateRequest{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Spec: v1alpha2.CertificateRequestSpec{ - CSRPEM: testCSR, - Username: "some-user", - Groups: []string{"some-group"}, - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, - }, - targetGVK: v1.SchemeGroupVersion.WithKind("CertificateRequest"), - output: &v1.CertificateRequest{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Spec: v1.CertificateRequestSpec{ - Request: testCSR, - Username: "admin", - Groups: []string{"system:masters", "system:authenticated"}, - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, - }, - }, - "should convert Certificate from v1alpha2 to v1beta1": { - input: &v1alpha2.Certificate{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Spec: v1alpha2.CertificateSpec{ - SecretName: "abc", - CommonName: "test", - Organization: []string{"test"}, - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, - }, - targetGVK: v1beta1.SchemeGroupVersion.WithKind("Certificate"), - output: &v1beta1.Certificate{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Spec: v1beta1.CertificateSpec{ - SecretName: "abc", - CommonName: "test", - Subject: &v1beta1.X509Subject{ - Organizations: []string{"test"}, - }, - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, - }, - }, - "should convert Certificate from v1beta1 to v1": { - input: &v1beta1.Certificate{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Spec: v1beta1.CertificateSpec{ - SecretName: "abc", - CommonName: "test", - Subject: &v1beta1.X509Subject{ - Organizations: []string{"test"}, - }, - URISANs: []string{"spiffe://foo.foo.example.net"}, - EmailSANs: []string{"alice@example.com"}, - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, - }, - targetGVK: v1.SchemeGroupVersion.WithKind("Certificate"), - output: &v1.Certificate{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Spec: v1.CertificateSpec{ - SecretName: "abc", - CommonName: "test", - Subject: &v1.X509Subject{ - Organizations: []string{"test"}, - }, - URIs: []string{"spiffe://foo.foo.example.net"}, - EmailAddresses: []string{"alice@example.com"}, - IssuerRef: cmmeta.ObjectReference{ - Name: "issuername", - }, - }, + TestFieldPtr: pointer.StringPtr("test1"), }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { + log := logtesting.TestLogger{T: t} + + scheme := runtime.NewScheme() + testapi.Install(scheme) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) defer cancel() - config, stop := framework.RunControlPlane(t, ctx) + config, stop := framework.RunControlPlane( + t, ctx, + framework.WithCRDDirectory("../../../pkg/webhook/handlers/testdata/apis/testgroup/crds"), + framework.WithWebhookConversionHandler(handlers.NewSchemeBackedConverter(log, scheme)), + ) defer stop() - - cl, err := client.New(config, client.Options{Scheme: api.Scheme}) + cl, err := client.New(config, client.Options{Scheme: scheme}) if err != nil { t.Fatal(err) } @@ -252,7 +104,7 @@ func TestConversion(t *testing.T) { } meta := test.input.(metav1.ObjectMetaAccessor) - convertedObj, err := api.Scheme.New(test.targetGVK) + convertedObj, err := scheme.New(test.targetGVK) if err != nil { t.Fatal(err) } diff --git a/test/integration/framework/BUILD.bazel b/test/integration/framework/BUILD.bazel index 7e1a9c430..c00dec2b1 100644 --- a/test/integration/framework/BUILD.bazel +++ b/test/integration/framework/BUILD.bazel @@ -9,12 +9,14 @@ go_library( importpath = "github.com/jetstack/cert-manager/test/integration/framework", visibility = ["//visibility:public"], deps = [ + "//cmd/webhook/app:go_default_library", "//cmd/webhook/app/testing:go_default_library", "//pkg/api:go_default_library", "//pkg/api/testing:go_default_library", "//pkg/client/clientset/versioned:go_default_library", "//pkg/client/informers/externalversions:go_default_library", "//pkg/controller:go_default_library", + "//pkg/webhook/handlers:go_default_library", "//test/internal/apiserver:go_default_library", "@io_k8s_api//admissionregistration/v1:go_default_library", "@io_k8s_api//core/v1:go_default_library", @@ -37,6 +39,7 @@ go_library( "@io_k8s_kubectl//pkg/util/openapi:go_default_library", "@io_k8s_sigs_controller_runtime//pkg/client:go_default_library", "@io_k8s_sigs_controller_runtime//pkg/envtest:go_default_library", + "@io_k8s_utils//pointer:go_default_library", ], ) diff --git a/test/integration/framework/apiserver.go b/test/integration/framework/apiserver.go index aabacf931..4ad8cb4fc 100644 --- a/test/integration/framework/apiserver.go +++ b/test/integration/framework/apiserver.go @@ -34,25 +34,63 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer/versioning" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/rest" + "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + "github.com/jetstack/cert-manager/cmd/webhook/app" webhooktesting "github.com/jetstack/cert-manager/cmd/webhook/app/testing" "github.com/jetstack/cert-manager/pkg/api" apitesting "github.com/jetstack/cert-manager/pkg/api/testing" + "github.com/jetstack/cert-manager/pkg/webhook/handlers" "github.com/jetstack/cert-manager/test/internal/apiserver" ) type StopFunc func() -func RunControlPlane(t *testing.T, ctx context.Context) (*rest.Config, StopFunc) { +// controlPlaneOptions has parameters for the control plane of the integration +// test framework which can be overridden in tests. +type controlPlaneOptions struct { + crdsDir *string + webhookConversionHandler handlers.ConversionHook +} + +type RunControlPlaneOption func(*controlPlaneOptions) + +// WithCRDDirectory allows alternative CRDs to be loaded into the test API +// server in tests. +func WithCRDDirectory(directory string) RunControlPlaneOption { + return func(o *controlPlaneOptions) { + o.crdsDir = pointer.StringPtr(directory) + } +} + +// WithWebhookConversionHandler allows the webhook handler for the `/convert` +// endpoint to be overridden in tests. +func WithWebhookConversionHandler(handler handlers.ConversionHook) RunControlPlaneOption { + return func(o *controlPlaneOptions) { + o.webhookConversionHandler = handler + } +} + +func RunControlPlane(t *testing.T, ctx context.Context, optionFunctions ...RunControlPlaneOption) (*rest.Config, StopFunc) { + options := &controlPlaneOptions{ + crdsDir: pointer.StringPtr(apitesting.CRDDirectory(t)), + } + + for _, f := range optionFunctions { + f(options) + } + env, stopControlPlane := apiserver.RunBareControlPlane(t) config := env.Config - webhookOpts, stopWebhook := webhooktesting.StartWebhookServer(t, ctx, []string{"--api-server-host=" + config.Host}) + webhookOpts, stopWebhook := webhooktesting.StartWebhookServer( + t, ctx, []string{"--api-server-host=" + config.Host}, + app.WithConversionHandler(options.webhookConversionHandler), + ) - crdsDir := apitesting.CRDDirectory(t) - crds := readCustomResourcesAtPath(t, crdsDir) + crds := readCustomResourcesAtPath(t, *options.crdsDir) for _, crd := range crds { t.Logf("Found CRD with name %q", crd.Name) } @@ -96,31 +134,22 @@ func init() { apiextensionsinstall.Install(internalScheme) } +// patchCRDConversion overrides the conversion configuration of the CRDs that +// are loaded in to the integration test API server, +// configuring the conversion to be handled by the local webhook server. func patchCRDConversion(crds []apiextensionsv1.CustomResourceDefinition, url string, caPEM []byte) { - for _, crd := range crds { - for i := range crd.Spec.Versions { - crd.Spec.Versions[i].Served = true + for i := range crds { + url := fmt.Sprintf("%s%s", url, "/convert") + crds[i].Spec.Conversion = &apiextensionsv1.CustomResourceConversion{ + Strategy: apiextensionsv1.WebhookConverter, + Webhook: &apiextensionsv1.WebhookConversion{ + ClientConfig: &apiextensionsv1.WebhookClientConfig{ + URL: &url, + CABundle: caPEM, + }, + ConversionReviewVersions: []string{"v1"}, + }, } - if crd.Spec.Conversion == nil { - continue - } - if crd.Spec.Conversion.Webhook == nil { - continue - } - if crd.Spec.Conversion.Webhook.ClientConfig == nil { - continue - } - if crd.Spec.Conversion.Webhook.ClientConfig.Service == nil { - continue - } - path := "" - if crd.Spec.Conversion.Webhook.ClientConfig.Service.Path != nil { - path = *crd.Spec.Conversion.Webhook.ClientConfig.Service.Path - } - url := fmt.Sprintf("%s%s", url, path) - crd.Spec.Conversion.Webhook.ClientConfig.URL = &url - crd.Spec.Conversion.Webhook.ClientConfig.CABundle = caPEM - crd.Spec.Conversion.Webhook.ClientConfig.Service = nil } }