Test conversion code using sample CRDs and remove conversion configuration from cert-manager CRDs
* Generate CRDs for the sample API types * Allow alternative CRDs to be loaded into the envtest API server * Override the conversion configuration of the CRDs * Show webhook server logs in tests * Simplify the loading of the test API CRDs * Allow the ConversionHandler to be overridden in tests Signed-off-by: Richard Wall <richard.wall@jetstack.io>
This commit is contained in:
parent
5894ed989a
commit
4eedf4fcfd
@ -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",
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
13
pkg/webhook/handlers/testdata/apis/testgroup/crds/BUILD.bazel
vendored
Normal file
13
pkg/webhook/handlers/testdata/apis/testgroup/crds/BUILD.bazel
vendored
Normal file
@ -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"],
|
||||
)
|
||||
3
pkg/webhook/handlers/testdata/apis/testgroup/crds/README.md
vendored
Normal file
3
pkg/webhook/handlers/testdata/apis/testgroup/crds/README.md
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# README
|
||||
|
||||
These CRDs are auto generated by `hack/update-crds.sh`.
|
||||
@ -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: []
|
||||
@ -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"`
|
||||
|
||||
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user