diff --git a/BUILD.bazel b/BUILD.bazel index 14ee04418..f4cedb180 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -93,6 +93,7 @@ filegroup( "//test/integration:all-srcs", "//test/internal/apiserver:all-srcs", "//test/unit/coreclients:all-srcs", + "//test/unit/discovery:all-srcs", "//test/unit/gen:all-srcs", "//test/unit/listers:all-srcs", "//tools/cobra:all-srcs", diff --git a/cmd/controller/app/controller.go b/cmd/controller/app/controller.go index 1563b6363..609df0c94 100644 --- a/cmd/controller/app/controller.go +++ b/cmd/controller/app/controller.go @@ -243,6 +243,7 @@ func buildControllerContext(ctx context.Context, stopCh <-chan struct{}, opts *o Client: cl, CMClient: intcl, GWClient: gwcl, + DiscoveryClient: cl.Discovery(), Recorder: recorder, KubeSharedInformerFactory: kubeSharedInformerFactory, SharedInformerFactory: sharedInformerFactory, diff --git a/pkg/controller/BUILD.bazel b/pkg/controller/BUILD.bazel index 89685c385..c0846e7b8 100644 --- a/pkg/controller/BUILD.bazel +++ b/pkg/controller/BUILD.bazel @@ -26,6 +26,7 @@ go_library( "@io_k8s_apimachinery//pkg/util/runtime:go_default_library", "@io_k8s_apimachinery//pkg/util/wait:go_default_library", "@io_k8s_apiserver//pkg/registry/generic/registry:go_default_library", + "@io_k8s_client_go//discovery:go_default_library", "@io_k8s_client_go//informers:go_default_library", "@io_k8s_client_go//kubernetes:go_default_library", "@io_k8s_client_go//rest:go_default_library", diff --git a/pkg/controller/acmechallenges/BUILD.bazel b/pkg/controller/acmechallenges/BUILD.bazel index e24244f84..a1ec67d85 100644 --- a/pkg/controller/acmechallenges/BUILD.bazel +++ b/pkg/controller/acmechallenges/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "//pkg/controller:go_default_library", "//pkg/controller/acmechallenges/scheduler:go_default_library", "//pkg/feature:go_default_library", + "//pkg/internal/ingress:go_default_library", "//pkg/issuer:go_default_library", "//pkg/issuer/acme/dns:go_default_library", "//pkg/issuer/acme/dns/util:go_default_library", diff --git a/pkg/controller/acmechallenges/controller.go b/pkg/controller/acmechallenges/controller.go index 9359a3cb8..a62bc6cf0 100644 --- a/pkg/controller/acmechallenges/controller.go +++ b/pkg/controller/acmechallenges/controller.go @@ -35,6 +35,7 @@ import ( cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1" controllerpkg "github.com/jetstack/cert-manager/pkg/controller" "github.com/jetstack/cert-manager/pkg/controller/acmechallenges/scheduler" + "github.com/jetstack/cert-manager/pkg/internal/ingress" "github.com/jetstack/cert-manager/pkg/issuer" "github.com/jetstack/cert-manager/pkg/issuer/acme/dns" "github.com/jetstack/cert-manager/pkg/issuer/acme/http" @@ -96,7 +97,12 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin // cache when managing pod/service/ingress resources podInformer := ctx.KubeSharedInformerFactory.Core().V1().Pods() serviceInformer := ctx.KubeSharedInformerFactory.Core().V1().Services() - ingressInformer := ctx.KubeSharedInformerFactory.Networking().V1beta1().Ingresses() + + _, ingressInformer, err := ingress.NewListerInformer(ctx) + if err != nil { + return nil, nil, err + } + // build a list of InformerSynced functions that will be returned by the Register method. // the controller will only begin processing items once all of these informers have synced. mustSync := []cache.InformerSynced{ @@ -105,7 +111,7 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin secretInformer.Informer().HasSynced, podInformer.Informer().HasSynced, serviceInformer.Informer().HasSynced, - ingressInformer.Informer().HasSynced, + ingressInformer.HasSynced, } // set all the references to the listers for used by the Sync function @@ -128,10 +134,12 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin c.scheduler = scheduler.New(logf.NewContext(ctx.RootContext, c.log), c.challengeLister, ctx.SchedulerOptions.MaxConcurrentChallenges) c.recorder = ctx.Recorder c.cmClient = ctx.CMClient - c.httpSolver = http.NewSolver(ctx) c.accountRegistry = ctx.ACMEOptions.AccountRegistry - var err error + c.httpSolver, err = http.NewSolver(ctx) + if err != nil { + return nil, nil, err + } c.dnsSolver, err = dns.NewSolver(ctx) if err != nil { return nil, nil, err diff --git a/pkg/controller/certificate-shim/BUILD.bazel b/pkg/controller/certificate-shim/BUILD.bazel index e075ad4c5..fddd18941 100644 --- a/pkg/controller/certificate-shim/BUILD.bazel +++ b/pkg/controller/certificate-shim/BUILD.bazel @@ -16,9 +16,11 @@ go_library( "//pkg/client/clientset/versioned:go_default_library", "//pkg/client/listers/certmanager/v1:go_default_library", "//pkg/controller:go_default_library", + "//pkg/internal/ingress:go_default_library", "//pkg/logs:go_default_library", "@com_github_go_logr_logr//:go_default_library", "@io_k8s_api//core/v1:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", "@io_k8s_api//networking/v1beta1:go_default_library", "@io_k8s_apimachinery//pkg/api/errors:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", @@ -47,7 +49,7 @@ go_test( "//test/unit/gen:go_default_library", "@com_github_go_logr_logr//:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", - "@io_k8s_api//networking/v1beta1:go_default_library", + "@io_k8s_api//networking/v1: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/types:go_default_library", diff --git a/pkg/controller/certificate-shim/ingresses/BUILD.bazel b/pkg/controller/certificate-shim/ingresses/BUILD.bazel index cac49adf1..b30d0e7c0 100644 --- a/pkg/controller/certificate-shim/ingresses/BUILD.bazel +++ b/pkg/controller/certificate-shim/ingresses/BUILD.bazel @@ -9,11 +9,11 @@ go_library( "//pkg/apis/certmanager/v1:go_default_library", "//pkg/controller:go_default_library", "//pkg/controller/certificate-shim:go_default_library", + "//pkg/internal/ingress:go_default_library", "//pkg/logs:go_default_library", "@io_k8s_apimachinery//pkg/api/errors:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_apimachinery//pkg/util/runtime:go_default_library", - "@io_k8s_client_go//listers/networking/v1beta1:go_default_library", "@io_k8s_client_go//tools/cache:go_default_library", "@io_k8s_client_go//util/workqueue:go_default_library", ], @@ -29,7 +29,7 @@ go_test( "//pkg/controller/test:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", "@com_github_stretchr_testify//require:go_default_library", - "@io_k8s_api//networking/v1beta1:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_apimachinery//pkg/runtime:go_default_library", "@io_k8s_client_go//kubernetes:go_default_library", diff --git a/pkg/controller/certificate-shim/ingresses/controller.go b/pkg/controller/certificate-shim/ingresses/controller.go index d88c61d94..8d513ff0f 100644 --- a/pkg/controller/certificate-shim/ingresses/controller.go +++ b/pkg/controller/certificate-shim/ingresses/controller.go @@ -23,13 +23,13 @@ import ( k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/runtime" - networkinglisters "k8s.io/client-go/listers/networking/v1beta1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" controllerpkg "github.com/jetstack/cert-manager/pkg/controller" shimhelper "github.com/jetstack/cert-manager/pkg/controller/certificate-shim" + "github.com/jetstack/cert-manager/pkg/internal/ingress" logf "github.com/jetstack/cert-manager/pkg/logs" ) @@ -38,15 +38,19 @@ const ( ) type controller struct { - ingressLister networkinglisters.IngressLister + ingressLister ingress.InternalIngressLister sync shimhelper.SyncFn } func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitingInterface, []cache.InformerSynced, error) { - kShared := ctx.KubeSharedInformerFactory cmShared := ctx.SharedInformerFactory - c.ingressLister = kShared.Networking().V1beta1().Ingresses().Lister() + internalIngressLister, internalIngressInformer, err := ingress.NewListerInformer(ctx) + if err != nil { + return nil, nil, err + } + + c.ingressLister = internalIngressLister log := logf.FromContext(ctx.RootContext, ControllerName) c.sync = shimhelper.SyncFnFor(ctx.Recorder, log, ctx.CMClient, cmShared.Certmanager().V1().Certificates().Lister(), ctx.IngressShimOptions) @@ -54,7 +58,7 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin queue := workqueue.NewNamedRateLimitingQueue(controllerpkg.DefaultItemBasedRateLimiter(), ControllerName) mustSync := []cache.InformerSynced{ - kShared.Networking().V1beta1().Ingresses().Informer().HasSynced, + internalIngressInformer.HasSynced, cmShared.Certmanager().V1().Certificates().Informer().HasSynced, } @@ -64,7 +68,7 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin // to do some cleanup, we would use a finalizer, and the cleanup logic would // be triggered by the "Updated" event when the object gets marked for // deletion. - kShared.Networking().V1beta1().Ingresses().Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{ + internalIngressInformer.AddEventHandler(&controllerpkg.QueuingEventHandler{ Queue: queue, }) diff --git a/pkg/controller/certificate-shim/ingresses/controller_test.go b/pkg/controller/certificate-shim/ingresses/controller_test.go index bddaa29fc..aef83bb3d 100644 --- a/pkg/controller/certificate-shim/ingresses/controller_test.go +++ b/pkg/controller/certificate-shim/ingresses/controller_test.go @@ -24,7 +24,7 @@ import ( testpkg "github.com/jetstack/cert-manager/pkg/controller/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - networkingv1beta1 "k8s.io/api/networking/v1beta1" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" kclient "k8s.io/client-go/kubernetes" @@ -33,7 +33,7 @@ import ( cmclient "github.com/jetstack/cert-manager/pkg/client/clientset/versioned" ) -var ingressGVK = networkingv1beta1.SchemeGroupVersion.WithKind("Ingress") +var ingressGVK = networkingv1.SchemeGroupVersion.WithKind("Ingress") func Test_controller_Register(t *testing.T) { tests := []struct { @@ -46,7 +46,7 @@ func Test_controller_Register(t *testing.T) { { name: "ingress is re-queued when an 'Added' event is received for this ingress", givenCall: func(t *testing.T, _ cmclient.Interface, c kclient.Interface) { - _, err := c.NetworkingV1beta1().Ingresses("namespace-1").Create(context.Background(), &networkingv1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{ + _, err := c.NetworkingV1().Ingresses("namespace-1").Create(context.Background(), &networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "ingress-1", }}, metav1.CreateOptions{}) require.NoError(t, err) @@ -55,11 +55,11 @@ func Test_controller_Register(t *testing.T) { }, { name: "ingress is re-queued when an 'Updated' event is received for this ingress", - existingKObjects: []runtime.Object{&networkingv1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{ + existingKObjects: []runtime.Object{&networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "ingress-1", }}}, givenCall: func(t *testing.T, _ cmclient.Interface, c kclient.Interface) { - _, err := c.NetworkingV1beta1().Ingresses("namespace-1").Update(context.Background(), &networkingv1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{ + _, err := c.NetworkingV1().Ingresses("namespace-1").Update(context.Background(), &networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "ingress-1", }}, metav1.UpdateOptions{}) require.NoError(t, err) @@ -68,11 +68,11 @@ func Test_controller_Register(t *testing.T) { }, { name: "ingress is re-queued when a 'Deleted' event is received for this ingress", - existingKObjects: []runtime.Object{&networkingv1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{ + existingKObjects: []runtime.Object{&networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "ingress-1", }}}, givenCall: func(t *testing.T, _ cmclient.Interface, c kclient.Interface) { - err := c.NetworkingV1beta1().Ingresses("namespace-1").Delete(context.Background(), "ingress-1", metav1.DeleteOptions{}) + err := c.NetworkingV1().Ingresses("namespace-1").Delete(context.Background(), "ingress-1", metav1.DeleteOptions{}) require.NoError(t, err) }, expectRequeueKey: "namespace-1/ingress-1", @@ -82,7 +82,7 @@ func Test_controller_Register(t *testing.T) { givenCall: func(t *testing.T, c cmclient.Interface, _ kclient.Interface) { _, err := c.CertmanagerV1().Certificates("namespace-1").Create(context.Background(), &cmapi.Certificate{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "cert-1", - OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(&networkingv1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(&networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "ingress-2", }}, ingressGVK)}, }}, metav1.CreateOptions{}) @@ -94,14 +94,14 @@ func Test_controller_Register(t *testing.T) { name: "ingress is re-queued when an 'Updated' event is received for its child Certificate", existingCMObjects: []runtime.Object{&cmapi.Certificate{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "cert-1", - OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(&networkingv1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(&networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "ingress-2", }}, ingressGVK)}, }}}, givenCall: func(t *testing.T, c cmclient.Interface, _ kclient.Interface) { _, err := c.CertmanagerV1().Certificates("namespace-1").Update(context.Background(), &cmapi.Certificate{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "cert-1", - OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(&networkingv1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(&networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "ingress-2", }}, ingressGVK)}, }}, metav1.UpdateOptions{}) @@ -113,7 +113,7 @@ func Test_controller_Register(t *testing.T) { name: "ingress is re-queued when a 'Deleted' event is received for its child Certificate", existingCMObjects: []runtime.Object{&cmapi.Certificate{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "cert-1", - OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(&networkingv1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(&networkingv1.Ingress{ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace-1", Name: "ingress-2", }}, ingressGVK)}, }}}, diff --git a/pkg/controller/certificate-shim/sync.go b/pkg/controller/certificate-shim/sync.go index 7ca9fad86..c3163438b 100644 --- a/pkg/controller/certificate-shim/sync.go +++ b/pkg/controller/certificate-shim/sync.go @@ -25,6 +25,7 @@ import ( "strings" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" networkingv1beta1 "k8s.io/api/networking/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -40,6 +41,7 @@ import ( clientset "github.com/jetstack/cert-manager/pkg/client/clientset/versioned" cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1" "github.com/jetstack/cert-manager/pkg/controller" + ingress "github.com/jetstack/cert-manager/pkg/internal/ingress" logf "github.com/jetstack/cert-manager/pkg/logs" "k8s.io/apimachinery/pkg/util/validation/field" gwapi "sigs.k8s.io/gateway-api/apis/v1alpha1" @@ -52,7 +54,8 @@ const ( reasonDeleteCertificate = "DeleteCertificate" ) -var ingressGVK = networkingv1beta1.SchemeGroupVersion.WithKind("Ingress") +var ingressV1GVK = networkingv1.SchemeGroupVersion.WithKind("Ingress") +var ingressV1Beta1GVK = networkingv1beta1.SchemeGroupVersion.WithKind("Ingress") var gatewayGVK = gwapi.SchemeGroupVersion.WithKind("Gateway") // SyncFn is the reconciliation function passed to a certificate-shim's @@ -88,7 +91,7 @@ func SyncFnFor( // resource. var autoAnnotations []string switch ingLike.(type) { - case *networkingv1beta1.Ingress: + case *networkingv1.Ingress: autoAnnotations = defaults.DefaultAutoCertificateAnnotations } @@ -152,7 +155,7 @@ func SyncFnFor( func validateIngressLike(ingLike metav1.Object) field.ErrorList { switch o := ingLike.(type) { - case *networkingv1beta1.Ingress: + case *networkingv1.Ingress: return checkForDuplicateSecretNames(field.NewPath("spec", "tls"), o.Spec.TLS) case *gwapi.Gateway: return nil @@ -161,7 +164,7 @@ func validateIngressLike(ingLike metav1.Object) field.ErrorList { } } -func checkForDuplicateSecretNames(path *field.Path, tlsBlocks []networkingv1beta1.IngressTLS) field.ErrorList { +func checkForDuplicateSecretNames(path *field.Path, tlsBlocks []networkingv1.IngressTLS) field.ErrorList { var errs field.ErrorList // We can't let two TLS blocks share the same secretName because we decided // to create one Certificate for each TLS block. For example: @@ -202,7 +205,7 @@ func checkForDuplicateSecretNames(path *field.Path, tlsBlocks []networkingv1beta return errs } -func validateIngressTLSBlock(path *field.Path, tlsBlock networkingv1beta1.IngressTLS) field.ErrorList { +func validateIngressTLSBlock(path *field.Path, tlsBlock networkingv1.IngressTLS) field.ErrorList { var errs field.ErrorList if len(tlsBlock.Hosts) == 0 { @@ -273,7 +276,7 @@ func buildCertificates( tlsHosts := make(map[corev1.ObjectReference][]string) switch ingLike := ingLike.(type) { - case *networkingv1beta1.Ingress: + case *networkingv1.Ingress: for i, tls := range ingLike.Spec.TLS { path := field.NewPath("spec", "tls").Index(i) err := validateIngressTLSBlock(path, tls).ToAggregate() @@ -314,8 +317,12 @@ func buildCertificates( var controllerGVK schema.GroupVersionKind switch ingLike.(type) { - case *networkingv1beta1.Ingress: - controllerGVK = ingressGVK + case *networkingv1.Ingress: + if _, found := ingLike.GetAnnotations()[ingress.ConvertedGVKAnnotation]; found { + controllerGVK = ingressV1Beta1GVK + } else { + controllerGVK = ingressV1GVK + } case *gwapi.Gateway: controllerGVK = gatewayGVK } @@ -340,7 +347,7 @@ func buildCertificates( } switch o := ingLike.(type) { - case *networkingv1beta1.Ingress: + case *networkingv1.Ingress: ingLike = o.DeepCopy() case *gwapi.Gateway: ingLike = o.DeepCopy() @@ -410,7 +417,7 @@ func isUnrequiredCertificate(crt *cmapi.Certificate, ingLike metav1.Object) bool } switch o := ingLike.(type) { - case *networkingv1beta1.Ingress: + case *networkingv1.Ingress: for _, tls := range o.Spec.TLS { if crt.Spec.SecretName == tls.SecretName { return false diff --git a/pkg/controller/certificate-shim/sync_test.go b/pkg/controller/certificate-shim/sync_test.go index edbc577bf..7973ccef2 100644 --- a/pkg/controller/certificate-shim/sync_test.go +++ b/pkg/controller/certificate-shim/sync_test.go @@ -24,7 +24,7 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/assert" - networkingv1beta1 "k8s.io/api/networking/v1beta1" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -97,7 +97,7 @@ func TestSync(t *testing.T) { { Name: "return a single Certificate for an ingress with a single valid TLS entry and common-name annotation", Issuer: acmeClusterIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -110,8 +110,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -147,7 +147,7 @@ func TestSync(t *testing.T) { { Name: "return a single HTTP01 Certificate for an ingress with a single valid TLS entry and HTTP01 annotations using edit-in-place", Issuer: acmeClusterIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -160,8 +160,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -200,7 +200,7 @@ func TestSync(t *testing.T) { { Name: "create a Certificate with the HTTP01 name override if the given ingress uses http01 annotations", Issuer: gen.Issuer(acmeIssuer.Name), - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -213,8 +213,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -253,7 +253,7 @@ func TestSync(t *testing.T) { { Name: "return a single HTTP01 Certificate for an ingress with a single valid TLS entry and HTTP01 annotations with no ingress class set", Issuer: acmeClusterIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -262,8 +262,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -295,7 +295,7 @@ func TestSync(t *testing.T) { { Name: "return a single HTTP01 Certificate for an ingress with a single valid TLS entry and HTTP01 annotations with a custom ingress class", Issuer: acmeClusterIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -305,8 +305,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -338,7 +338,7 @@ func TestSync(t *testing.T) { { Name: "return a single HTTP01 Certificate for an ingress with a single valid TLS entry and HTTP01 annotations with a certificate ingress class", Issuer: acmeClusterIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -349,8 +349,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -385,7 +385,7 @@ func TestSync(t *testing.T) { { Name: "edit-in-place set to false should not trigger editing the ingress in-place", Issuer: acmeClusterIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -396,8 +396,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -429,7 +429,7 @@ func TestSync(t *testing.T) { { Name: "return a single DNS01 Certificate for an ingress with a single valid TLS entry", Issuer: acmeClusterIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -438,8 +438,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -475,7 +475,7 @@ func TestSync(t *testing.T) { DefaultIssuerKind: "ClusterIssuer", DefaultIssuerGroup: "cert-manager.io", ClusterIssuerLister: []runtime.Object{clusterIssuer}, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -484,8 +484,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -522,7 +522,7 @@ func TestSync(t *testing.T) { `Warning BadConfig Skipped a TLS block: spec.tls[0].hosts: Required value`, `Normal CreateCertificate Successfully created Certificate "example-com-tls"`, }, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -531,8 +531,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { SecretName: "example-com-tls-invalid", }, @@ -571,7 +571,7 @@ func TestSync(t *testing.T) { `Warning BadConfig Skipped a TLS block: spec.tls[0].secretName: Required value`, `Normal CreateCertificate Successfully created Certificate "example-com-tls"`, }, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -580,8 +580,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, }, @@ -613,7 +613,7 @@ func TestSync(t *testing.T) { }, { Name: "should error if the specified issuer is not found", - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -628,7 +628,7 @@ func TestSync(t *testing.T) { Name: "should not return any certificates if a correct Certificate already exists", Issuer: acmeIssuer, IssuerLister: []runtime.Object{acmeIssuer}, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -637,8 +637,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, SecretName: "existing-crt", @@ -672,7 +672,7 @@ func TestSync(t *testing.T) { Name: "should update a certificate if an incorrect Certificate exists", Issuer: acmeIssuer, IssuerLister: []runtime.Object{acmeIssuer}, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -681,8 +681,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, SecretName: "existing-crt", @@ -721,7 +721,7 @@ func TestSync(t *testing.T) { Name: "should update an existing Certificate resource with new labels if they do not match those specified on the IngressLike", Issuer: acmeIssuer, IssuerLister: []runtime.Object{acmeIssuerNewFormat}, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -733,8 +733,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, SecretName: "cert-secret-name", @@ -791,7 +791,7 @@ func TestSync(t *testing.T) { Name: "should not update certificate if it does not belong to any ingress", Issuer: acmeIssuer, IssuerLister: []runtime.Object{acmeIssuer}, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -801,8 +801,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, SecretName: "existing-crt", @@ -833,7 +833,7 @@ func TestSync(t *testing.T) { Name: "should not update certificate if it does not belong to the ingress", Issuer: acmeIssuer, IssuerLister: []runtime.Object{acmeIssuer}, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -843,8 +843,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, SecretName: "existing-crt", @@ -875,7 +875,7 @@ func TestSync(t *testing.T) { Name: "should delete a Certificate if its SecretName is not present in the ingress", Issuer: acmeIssuer, IssuerLister: []runtime.Object{acmeIssuer}, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -926,7 +926,7 @@ func TestSync(t *testing.T) { Name: "should update a Certificate if is contains a Common Name that is not defined on the ingress annotations", Issuer: acmeIssuer, IssuerLister: []runtime.Object{acmeIssuer}, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -937,8 +937,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, SecretName: "example-com-tls", @@ -994,7 +994,7 @@ func TestSync(t *testing.T) { ExpectedEvents: []string{ `Warning BadConfig spec.tls[0].secretName: Invalid value: "example-com-tls": this secret name must only appear in a single TLS entry but is also used in spec.tls[1].secretName`, }, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -1005,8 +1005,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, SecretName: "example-com-tls", @@ -1022,7 +1022,7 @@ func TestSync(t *testing.T) { { Name: "Failure to translateIngressAnnotations", Issuer: acmeIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -1034,8 +1034,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com"}, SecretName: "example-com-tls", @@ -1048,7 +1048,7 @@ func TestSync(t *testing.T) { { Name: "return a single Certificate for an ingress with a single valid TLS entry with common-name and keyusage annotation", Issuer: acmeClusterIssuer, - IngressLike: &networkingv1beta1.Ingress{ + IngressLike: &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "ingress-name", Namespace: gen.DefaultTestNamespace, @@ -1062,8 +1062,8 @@ func TestSync(t *testing.T) { }, UID: types.UID("ingress-name"), }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ { Hosts: []string{"example.com", "www.example.com"}, SecretName: "example-com-tls", @@ -2484,7 +2484,7 @@ func (f *fakeHelper) GetGenericIssuer(ref cmmeta.ObjectReference, ns string) (cm func TestIssuerForIngress(t *testing.T) { type testT struct { - Ingress *networkingv1beta1.Ingress + Ingress *networkingv1.Ingress DefaultName string DefaultKind string DefaultGroup string @@ -2583,8 +2583,8 @@ func buildCertificate(name, namespace string, ownerReferences []metav1.OwnerRefe } } -func buildIngress(name, namespace string, annotations map[string]string) *networkingv1beta1.Ingress { - return &networkingv1beta1.Ingress{ +func buildIngress(name, namespace string, annotations map[string]string) *networkingv1.Ingress { + return &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, @@ -2606,7 +2606,7 @@ func buildGateway(name, namespace string, annotations map[string]string) *gwapi. func buildIngressOwnerReferences(name, namespace string) []metav1.OwnerReference { return []metav1.OwnerReference{ - *metav1.NewControllerRef(buildIngress(name, namespace, nil), ingressGVK), + *metav1.NewControllerRef(buildIngress(name, namespace, nil), ingressV1GVK), } } diff --git a/pkg/controller/context.go b/pkg/controller/context.go index a67354b00..712731576 100644 --- a/pkg/controller/context.go +++ b/pkg/controller/context.go @@ -21,6 +21,7 @@ import ( "time" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/client-go/discovery" kubeinformers "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -54,6 +55,8 @@ type Context struct { CMClient clientset.Interface // GWClient is a GatewayAPI clientset. GWClient gwclient.Interface + // DiscoveryClient is a discovery interface. Usually set to Client.Discovery unless a fake client is in use. + DiscoveryClient discovery.DiscoveryInterface // Recorder to record events to Recorder record.EventRecorder diff --git a/pkg/controller/test/BUILD.bazel b/pkg/controller/test/BUILD.bazel index e0a12e683..2f0a86d03 100644 --- a/pkg/controller/test/BUILD.bazel +++ b/pkg/controller/test/BUILD.bazel @@ -20,7 +20,9 @@ go_library( "//pkg/logs:go_default_library", "//pkg/metrics:go_default_library", "//pkg/util:go_default_library", + "//test/unit/discovery:go_default_library", "@com_github_kr_pretty//:go_default_library", + "@io_k8s_api//networking/v1: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/util/errors:go_default_library", diff --git a/pkg/controller/test/context_builder.go b/pkg/controller/test/context_builder.go index 89f4f59f5..515c40c67 100644 --- a/pkg/controller/test/context_builder.go +++ b/pkg/controller/test/context_builder.go @@ -24,6 +24,7 @@ import ( "testing" "time" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" utilerrors "k8s.io/apimachinery/pkg/util/errors" @@ -43,6 +44,7 @@ import ( "github.com/jetstack/cert-manager/pkg/logs" "github.com/jetstack/cert-manager/pkg/metrics" "github.com/jetstack/cert-manager/pkg/util" + discoveryfake "github.com/jetstack/cert-manager/test/unit/discovery" ) func init() { @@ -111,6 +113,29 @@ func (b *Builder) Init() { b.Client = kubefake.NewSimpleClientset(b.KubeObjects...) b.CMClient = cmfake.NewSimpleClientset(b.CertManagerObjects...) b.GWClient = gwfake.NewSimpleClientset(b.GWObjects...) + b.DiscoveryClient = discoveryfake.NewDiscovery().WithServerResourcesForGroupVersion(func(groupVersion string) (*metav1.APIResourceList, error) { + if groupVersion == networkingv1.SchemeGroupVersion.String() { + return &metav1.APIResourceList{ + TypeMeta: metav1.TypeMeta{}, + GroupVersion: networkingv1.SchemeGroupVersion.String(), + APIResources: []metav1.APIResource{ + { + Name: "ingresses", + SingularName: "Ingress", + Namespaced: true, + Group: networkingv1.GroupName, + Version: networkingv1.SchemeGroupVersion.Version, + Kind: networkingv1.SchemeGroupVersion.WithKind("Ingress").Kind, + Verbs: metav1.Verbs{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"}, + ShortNames: []string{"ing"}, + Categories: []string{"all"}, + StorageVersionHash: "testing", + }, + }, + }, nil + } + return &metav1.APIResourceList{}, nil + }) b.Recorder = new(FakeRecorder) b.FakeKubeClient().PrependReactor("create", "*", b.generateNameReactor) b.FakeCMClient().PrependReactor("create", "*", b.generateNameReactor) diff --git a/pkg/internal/BUILD.bazel b/pkg/internal/BUILD.bazel index 9aba7f3f4..e68946587 100644 --- a/pkg/internal/BUILD.bazel +++ b/pkg/internal/BUILD.bazel @@ -14,6 +14,7 @@ filegroup( "//pkg/internal/apis/acme:all-srcs", "//pkg/internal/apis/certmanager:all-srcs", "//pkg/internal/apis/meta:all-srcs", + "//pkg/internal/ingress:all-srcs", "//pkg/internal/vault:all-srcs", ], tags = ["automanaged"], diff --git a/pkg/internal/apis/certmanager/validation/plugins/BUILD.bazel b/pkg/internal/apis/certmanager/validation/plugins/BUILD.bazel index 567c94d52..e18f055d8 100644 --- a/pkg/internal/apis/certmanager/validation/plugins/BUILD.bazel +++ b/pkg/internal/apis/certmanager/validation/plugins/BUILD.bazel @@ -31,9 +31,9 @@ go_test( embed = [":go_default_library"], deps = [ "//pkg/internal/apis/certmanager:go_default_library", - "//pkg/internal/apis/certmanager/validation/plugins/fake:go_default_library", "//pkg/internal/apis/meta:go_default_library", "//pkg/webhook:go_default_library", + "//test/unit/discovery:go_default_library", "@io_k8s_api//admission/v1:go_default_library", "@io_k8s_api//authentication/v1:go_default_library", "@io_k8s_api//authorization/v1:go_default_library", @@ -55,10 +55,7 @@ filegroup( filegroup( name = "all-srcs", - srcs = [ - ":package-srcs", - "//pkg/internal/apis/certmanager/validation/plugins/fake:all-srcs", - ], + srcs = [":package-srcs"], tags = ["automanaged"], visibility = ["//visibility:public"], ) diff --git a/pkg/internal/apis/certmanager/validation/plugins/approval_test.go b/pkg/internal/apis/certmanager/validation/plugins/approval_test.go index 26461e1b7..fe51fbf31 100644 --- a/pkg/internal/apis/certmanager/validation/plugins/approval_test.go +++ b/pkg/internal/apis/certmanager/validation/plugins/approval_test.go @@ -33,9 +33,9 @@ import ( coretesting "k8s.io/client-go/testing" internalcmapi "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager" - pluginsfake "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager/validation/plugins/fake" internalcmmeta "github.com/jetstack/cert-manager/pkg/internal/apis/meta" "github.com/jetstack/cert-manager/pkg/webhook" + discoveryfake "github.com/jetstack/cert-manager/test/unit/discovery" ) var ( @@ -54,7 +54,7 @@ var ( } expNoDiscovery = func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(expNoServerGroups(t)). WithServerResourcesForGroupVersion(expNoServerResourcesForGroupVersion(t)) } @@ -147,7 +147,7 @@ func TestValidate(t *testing.T) { newCR: approvedCR, sarreaction: expNoSARReaction, discoverclient: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return &metav1.APIGroupList{ Groups: []metav1.APIGroup{ @@ -175,7 +175,7 @@ func TestValidate(t *testing.T) { oldCR: baseCR, newCR: approvedCR, discoverclient: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return &metav1.APIGroupList{ Groups: []metav1.APIGroup{ @@ -229,7 +229,7 @@ func TestValidate(t *testing.T) { oldCR: baseCR, newCR: approvedCR, discoverclient: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return &metav1.APIGroupList{ Groups: []metav1.APIGroup{ @@ -683,7 +683,7 @@ func TestSignerResource(t *testing.T) { }, client: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return nil, errors.New("this is an error") }). @@ -708,7 +708,7 @@ func TestSignerResource(t *testing.T) { }, client: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return &metav1.APIGroupList{ Groups: []metav1.APIGroup{ @@ -738,7 +738,7 @@ func TestSignerResource(t *testing.T) { }, client: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return &metav1.APIGroupList{ Groups: []metav1.APIGroup{ @@ -776,7 +776,7 @@ func TestSignerResource(t *testing.T) { }, client: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return &metav1.APIGroupList{ Groups: []metav1.APIGroup{ @@ -824,7 +824,7 @@ func TestSignerResource(t *testing.T) { }, client: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return &metav1.APIGroupList{ Groups: []metav1.APIGroup{ @@ -880,7 +880,7 @@ func TestSignerResource(t *testing.T) { }, client: func(t *testing.T) discovery.DiscoveryInterface { - return pluginsfake.NewDiscovery(). + return discoveryfake.NewDiscovery(). WithServerGroups(func() (*metav1.APIGroupList, error) { return &metav1.APIGroupList{ Groups: []metav1.APIGroup{ diff --git a/pkg/internal/ingress/BUILD.bazel b/pkg/internal/ingress/BUILD.bazel new file mode 100644 index 000000000..3df865a22 --- /dev/null +++ b/pkg/internal/ingress/BUILD.bazel @@ -0,0 +1,61 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "convert.go", + "ingress.go", + "v1.go", + "v1beta1.go", + ], + importpath = "github.com/jetstack/cert-manager/pkg/internal/ingress", + visibility = ["//visibility:public"], + deps = [ + "//pkg/controller:go_default_library", + "@io_k8s_api//core/v1:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", + "@io_k8s_api//networking/v1beta1:go_default_library", + "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", + "@io_k8s_apimachinery//pkg/conversion:go_default_library", + "@io_k8s_apimachinery//pkg/labels:go_default_library", + "@io_k8s_apimachinery//pkg/util/intstr:go_default_library", + "@io_k8s_client_go//discovery:go_default_library", + "@io_k8s_client_go//kubernetes:go_default_library", + "@io_k8s_client_go//listers/networking/v1:go_default_library", + "@io_k8s_client_go//listers/networking/v1beta1:go_default_library", + "@io_k8s_client_go//tools/cache:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["ingress_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/controller:go_default_library", + "//test/unit/discovery:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", + "@io_k8s_api//networking/v1beta1:go_default_library", + "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", + "@io_k8s_apimachinery//pkg/labels:go_default_library", + "@io_k8s_apimachinery//pkg/runtime/schema:go_default_library", + "@io_k8s_client_go//informers:go_default_library", + "@io_k8s_client_go//kubernetes/fake:go_default_library", + "@io_k8s_utils//pointer:go_default_library", + ], +) + +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/internal/ingress/convert.go b/pkg/internal/ingress/convert.go new file mode 100644 index 000000000..0f9751ea5 --- /dev/null +++ b/pkg/internal/ingress/convert.go @@ -0,0 +1,341 @@ +/* +Copyright 2021 The cert-manager Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ingress + +import ( + "unsafe" + + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + networkingv1beta1 "k8s.io/api/networking/v1beta1" + "k8s.io/apimachinery/pkg/conversion" + "k8s.io/apimachinery/pkg/util/intstr" +) + +/* +This file contains copies of functions from k8s.io/kubernetes, +as we definitely don't want to import the entire of k8s. The code +is released under the following LICENSE: +*/ + +/* +Copyright The Kubernetes Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +func Convert_networking_Ingress_To_v1beta1_Ingress(in *networkingv1.Ingress, out *networkingv1beta1.Ingress, s conversion.Scope) error { + return autoConvert_networking_Ingress_To_v1beta1_Ingress(in, out, s) +} + +func autoConvert_networking_Ingress_To_v1beta1_Ingress(in *networkingv1.Ingress, out *networkingv1beta1.Ingress, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_networking_IngressSpec_To_v1beta1_IngressSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_networking_IngressStatus_To_v1beta1_IngressStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networkingv1.IngressSpec, out *networkingv1beta1.IngressSpec, s conversion.Scope) error { + if err := autoConvert_networking_IngressSpec_To_v1beta1_IngressSpec(in, out, s); err != nil { + return nil + } + if in.DefaultBackend != nil { + out.Backend = &networkingv1beta1.IngressBackend{} + if err := Convert_networking_IngressBackend_To_v1beta1_IngressBackend(in.DefaultBackend, out.Backend, s); err != nil { + return err + } + } + return nil +} + +func autoConvert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networkingv1.IngressSpec, out *networkingv1beta1.IngressSpec, s conversion.Scope) error { + out.IngressClassName = (*string)(unsafe.Pointer(in.IngressClassName)) + // WARNING: in.DefaultBackend requires manual conversion: does not exist in peer-type + out.TLS = *(*[]networkingv1beta1.IngressTLS)(unsafe.Pointer(&in.TLS)) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]networkingv1beta1.IngressRule, len(*in)) + for i := range *in { + if err := Convert_networking_IngressRule_To_v1beta1_IngressRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Rules = nil + } + return nil +} + +func Convert_networking_IngressStatus_To_v1beta1_IngressStatus(in *networkingv1.IngressStatus, out *networkingv1beta1.IngressStatus, s conversion.Scope) error { + return autoConvert_networking_IngressStatus_To_v1beta1_IngressStatus(in, out, s) +} + +func autoConvert_networking_IngressStatus_To_v1beta1_IngressStatus(in *networkingv1.IngressStatus, out *networkingv1beta1.IngressStatus, s conversion.Scope) error { + if err := Convert_core_LoadBalancerStatus_To_v1_LoadBalancerStatus(&in.LoadBalancer, &out.LoadBalancer, s); err != nil { + return err + } + return nil +} + +func Convert_core_LoadBalancerStatus_To_v1_LoadBalancerStatus(in *corev1.LoadBalancerStatus, out *corev1.LoadBalancerStatus, s conversion.Scope) error { + return autoConvert_core_LoadBalancerStatus_To_v1_LoadBalancerStatus(in, out, s) +} + +func autoConvert_core_LoadBalancerStatus_To_v1_LoadBalancerStatus(in *corev1.LoadBalancerStatus, out *corev1.LoadBalancerStatus, s conversion.Scope) error { + out.Ingress = *(*[]corev1.LoadBalancerIngress)(unsafe.Pointer(&in.Ingress)) + return nil +} + +func Convert_networking_IngressRule_To_v1beta1_IngressRule(in *networkingv1.IngressRule, out *networkingv1beta1.IngressRule, s conversion.Scope) error { + return autoConvert_networking_IngressRule_To_v1beta1_IngressRule(in, out, s) +} + +func autoConvert_networking_IngressRule_To_v1beta1_IngressRule(in *networkingv1.IngressRule, out *networkingv1beta1.IngressRule, s conversion.Scope) error { + out.Host = in.Host + if err := Convert_networking_IngressRuleValue_To_v1beta1_IngressRuleValue(&in.IngressRuleValue, &out.IngressRuleValue, s); err != nil { + return err + } + return nil +} + +func Convert_networking_IngressRuleValue_To_v1beta1_IngressRuleValue(in *networkingv1.IngressRuleValue, out *networkingv1beta1.IngressRuleValue, s conversion.Scope) error { + return autoConvert_networking_IngressRuleValue_To_v1beta1_IngressRuleValue(in, out, s) +} + +func autoConvert_networking_IngressRuleValue_To_v1beta1_IngressRuleValue(in *networkingv1.IngressRuleValue, out *networkingv1beta1.IngressRuleValue, s conversion.Scope) error { + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(networkingv1beta1.HTTPIngressRuleValue) + if err := Convert_networking_HTTPIngressRuleValue_To_v1beta1_HTTPIngressRuleValue(*in, *out, s); err != nil { + return err + } + } else { + out.HTTP = nil + } + return nil +} + +func Convert_networking_HTTPIngressRuleValue_To_v1beta1_HTTPIngressRuleValue(in *networkingv1.HTTPIngressRuleValue, out *networkingv1beta1.HTTPIngressRuleValue, s conversion.Scope) error { + return autoConvert_networking_HTTPIngressRuleValue_To_v1beta1_HTTPIngressRuleValue(in, out, s) +} + +func autoConvert_networking_HTTPIngressRuleValue_To_v1beta1_HTTPIngressRuleValue(in *networkingv1.HTTPIngressRuleValue, out *networkingv1beta1.HTTPIngressRuleValue, s conversion.Scope) error { + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]networkingv1beta1.HTTPIngressPath, len(*in)) + for i := range *in { + if err := Convert_networking_HTTPIngressPath_To_v1beta1_HTTPIngressPath(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Paths = nil + } + return nil +} + +func Convert_networking_HTTPIngressPath_To_v1beta1_HTTPIngressPath(in *networkingv1.HTTPIngressPath, out *networkingv1beta1.HTTPIngressPath, s conversion.Scope) error { + return autoConvert_networking_HTTPIngressPath_To_v1beta1_HTTPIngressPath(in, out, s) +} + +func autoConvert_networking_HTTPIngressPath_To_v1beta1_HTTPIngressPath(in *networkingv1.HTTPIngressPath, out *networkingv1beta1.HTTPIngressPath, s conversion.Scope) error { + out.Path = in.Path + out.PathType = (*networkingv1beta1.PathType)(unsafe.Pointer(in.PathType)) + if err := Convert_networking_IngressBackend_To_v1beta1_IngressBackend(&in.Backend, &out.Backend, s); err != nil { + return err + } + return nil +} + +func Convert_networking_IngressBackend_To_v1beta1_IngressBackend(in *networkingv1.IngressBackend, out *networkingv1beta1.IngressBackend, s conversion.Scope) error { + if err := autoConvert_networking_IngressBackend_To_v1beta1_IngressBackend(in, out, s); err != nil { + return err + } + if in.Service != nil { + out.ServiceName = in.Service.Name + if len(in.Service.Port.Name) > 0 { + out.ServicePort = intstr.FromString(in.Service.Port.Name) + } else { + out.ServicePort = intstr.FromInt(int(in.Service.Port.Number)) + } + } + return nil +} + +func autoConvert_networking_IngressBackend_To_v1beta1_IngressBackend(in *networkingv1.IngressBackend, out *networkingv1beta1.IngressBackend, s conversion.Scope) error { + // WARNING: in.Service requires manual conversion: does not exist in peer-type + out.Resource = (*corev1.TypedLocalObjectReference)(unsafe.Pointer(in.Resource)) + return nil +} + +func Convert_v1beta1_Ingress_To_networking_Ingress(in *networkingv1beta1.Ingress, out *networkingv1.Ingress, s conversion.Scope) error { + return autoConvert_v1beta1_Ingress_To_networking_Ingress(in, out, s) +} + +func autoConvert_v1beta1_Ingress_To_networking_Ingress(in *networkingv1beta1.Ingress, out *networkingv1.Ingress, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_IngressSpec_To_networking_IngressSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_IngressStatus_To_networking_IngressStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *networkingv1beta1.IngressSpec, out *networkingv1.IngressSpec, s conversion.Scope) error { + if err := autoConvert_v1beta1_IngressSpec_To_networking_IngressSpec(in, out, s); err != nil { + return nil + } + if in.Backend != nil { + out.DefaultBackend = &networkingv1.IngressBackend{} + if err := Convert_v1beta1_IngressBackend_To_networking_IngressBackend(in.Backend, out.DefaultBackend, s); err != nil { + return err + } + } + return nil +} + +func autoConvert_v1beta1_IngressSpec_To_networking_IngressSpec(in *networkingv1beta1.IngressSpec, out *networkingv1.IngressSpec, s conversion.Scope) error { + out.IngressClassName = (*string)(unsafe.Pointer(in.IngressClassName)) + // WARNING: in.Backend requires manual conversion: does not exist in peer-type + out.TLS = *(*[]networkingv1.IngressTLS)(unsafe.Pointer(&in.TLS)) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]networkingv1.IngressRule, len(*in)) + for i := range *in { + if err := Convert_v1beta1_IngressRule_To_networking_IngressRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Rules = nil + } + return nil +} + +func Convert_v1beta1_IngressStatus_To_networking_IngressStatus(in *networkingv1beta1.IngressStatus, out *networkingv1.IngressStatus, s conversion.Scope) error { + return autoConvert_v1beta1_IngressStatus_To_networking_IngressStatus(in, out, s) +} + +func autoConvert_v1beta1_IngressStatus_To_networking_IngressStatus(in *networkingv1beta1.IngressStatus, out *networkingv1.IngressStatus, s conversion.Scope) error { + if err := Convert_v1_LoadBalancerStatus_To_core_LoadBalancerStatus(&in.LoadBalancer, &out.LoadBalancer, s); err != nil { + return err + } + return nil +} + +func Convert_v1_LoadBalancerStatus_To_core_LoadBalancerStatus(in *corev1.LoadBalancerStatus, out *corev1.LoadBalancerStatus, s conversion.Scope) error { + return autoConvert_v1_LoadBalancerStatus_To_core_LoadBalancerStatus(in, out, s) +} + +func autoConvert_v1_LoadBalancerStatus_To_core_LoadBalancerStatus(in *corev1.LoadBalancerStatus, out *corev1.LoadBalancerStatus, s conversion.Scope) error { + out.Ingress = *(*[]corev1.LoadBalancerIngress)(unsafe.Pointer(&in.Ingress)) + return nil +} + +func Convert_v1beta1_IngressRule_To_networking_IngressRule(in *networkingv1beta1.IngressRule, out *networkingv1.IngressRule, s conversion.Scope) error { + return autoConvert_v1beta1_IngressRule_To_networking_IngressRule(in, out, s) +} + +func autoConvert_v1beta1_IngressRule_To_networking_IngressRule(in *networkingv1beta1.IngressRule, out *networkingv1.IngressRule, s conversion.Scope) error { + out.Host = in.Host + if err := Convert_v1beta1_IngressRuleValue_To_networking_IngressRuleValue(&in.IngressRuleValue, &out.IngressRuleValue, s); err != nil { + return err + } + return nil +} + +func Convert_v1beta1_IngressRuleValue_To_networking_IngressRuleValue(in *networkingv1beta1.IngressRuleValue, out *networkingv1.IngressRuleValue, s conversion.Scope) error { + return autoConvert_v1beta1_IngressRuleValue_To_networking_IngressRuleValue(in, out, s) +} + +func autoConvert_v1beta1_IngressRuleValue_To_networking_IngressRuleValue(in *networkingv1beta1.IngressRuleValue, out *networkingv1.IngressRuleValue, s conversion.Scope) error { + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(networkingv1.HTTPIngressRuleValue) + if err := Convert_v1beta1_HTTPIngressRuleValue_To_networking_HTTPIngressRuleValue(*in, *out, s); err != nil { + return err + } + } else { + out.HTTP = nil + } + return nil +} + +func Convert_v1beta1_HTTPIngressRuleValue_To_networking_HTTPIngressRuleValue(in *networkingv1beta1.HTTPIngressRuleValue, out *networkingv1.HTTPIngressRuleValue, s conversion.Scope) error { + return autoConvert_v1beta1_HTTPIngressRuleValue_To_networking_HTTPIngressRuleValue(in, out, s) +} + +func autoConvert_v1beta1_HTTPIngressRuleValue_To_networking_HTTPIngressRuleValue(in *networkingv1beta1.HTTPIngressRuleValue, out *networkingv1.HTTPIngressRuleValue, s conversion.Scope) error { + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]networkingv1.HTTPIngressPath, len(*in)) + for i := range *in { + if err := Convert_v1beta1_HTTPIngressPath_To_networking_HTTPIngressPath(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Paths = nil + } + return nil +} + +func Convert_v1beta1_HTTPIngressPath_To_networking_HTTPIngressPath(in *networkingv1beta1.HTTPIngressPath, out *networkingv1.HTTPIngressPath, s conversion.Scope) error { + return autoConvert_v1beta1_HTTPIngressPath_To_networking_HTTPIngressPath(in, out, s) +} + +func autoConvert_v1beta1_HTTPIngressPath_To_networking_HTTPIngressPath(in *networkingv1beta1.HTTPIngressPath, out *networkingv1.HTTPIngressPath, s conversion.Scope) error { + out.Path = in.Path + out.PathType = (*networkingv1.PathType)(unsafe.Pointer(in.PathType)) + if err := Convert_v1beta1_IngressBackend_To_networking_IngressBackend(&in.Backend, &out.Backend, s); err != nil { + return err + } + return nil +} + +func Convert_v1beta1_IngressBackend_To_networking_IngressBackend(in *networkingv1beta1.IngressBackend, out *networkingv1.IngressBackend, s conversion.Scope) error { + if err := autoConvert_v1beta1_IngressBackend_To_networking_IngressBackend(in, out, s); err != nil { + return err + } + if len(in.ServiceName) > 0 || in.ServicePort.IntVal != 0 || in.ServicePort.StrVal != "" || in.ServicePort.Type == intstr.String { + out.Service = &networkingv1.IngressServiceBackend{} + out.Service.Name = in.ServiceName + out.Service.Port.Name = in.ServicePort.StrVal + out.Service.Port.Number = in.ServicePort.IntVal + } + return nil +} + +func autoConvert_v1beta1_IngressBackend_To_networking_IngressBackend(in *networkingv1beta1.IngressBackend, out *networkingv1.IngressBackend, s conversion.Scope) error { + // WARNING: in.ServiceName requires manual conversion: does not exist in peer-type + // WARNING: in.ServicePort requires manual conversion: does not exist in peer-type + out.Resource = (*corev1.TypedLocalObjectReference)(unsafe.Pointer(in.Resource)) + return nil +} diff --git a/pkg/internal/ingress/ingress.go b/pkg/internal/ingress/ingress.go new file mode 100644 index 000000000..65977815e --- /dev/null +++ b/pkg/internal/ingress/ingress.go @@ -0,0 +1,151 @@ +/* +Copyright 2021 The cert-manager Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package ingress lets us use an internal type for supporting multiple kinds of ingresses +package ingress + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + + networkingv1 "k8s.io/api/networking/v1" + networkingv1beta1 "k8s.io/api/networking/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/discovery" + "k8s.io/client-go/tools/cache" + + "github.com/jetstack/cert-manager/pkg/controller" +) + +// keep an internal cache of known API types so calls to the discovery API are kept to a minimum +// https://pkg.go.dev/sync/atomic#example-Value-ReadMostly +var ( + knownAPIVersionCache atomic.Value + cacheLock sync.Mutex +) + +// InternalIngressCreateUpdater mimics a client-go networking/v1 or +// networking/v1beta1 Interface. +type InternalIngressCreateUpdater interface { + Ingresses(namespace string) InternalIngressInterface +} + +// InternalIngressInterface mimics a client-go networking/v1/IngressInterface +// It always returns a *networkingv1.Ingress, so when implementing this you must convert any type of +// Ingress into a v1.Ingress. +type InternalIngressInterface interface { + Create(ctx context.Context, ingress *networkingv1.Ingress, opts metav1.CreateOptions) (*networkingv1.Ingress, error) + Update(ctx context.Context, ingress *networkingv1.Ingress, opts metav1.UpdateOptions) (*networkingv1.Ingress, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + Get(ctx context.Context, name string, opts metav1.GetOptions) (*networkingv1.Ingress, error) +} + +// InternalIngressLister mimics a client-go networking/v1/IngressLister. +type InternalIngressLister interface { + List(selector labels.Selector) (ret []*networkingv1.Ingress, err error) + Ingresses(namespace string) InternalIngressNamespaceLister +} + +// InternalIngressNamespaceLister mimics a client-go networking/v1/IngressNamespaceLister +type InternalIngressNamespaceLister interface { + List(selector labels.Selector) (ret []*networkingv1.Ingress, err error) + Get(name string) (*networkingv1.Ingress, error) +} + +// NewListerInformer returns an InternalIngressLister configured for v1 or v1beta1 ingresses depending on the +// API Versions available in the discovery client. +func NewListerInformer(ctx *controller.Context) (InternalIngressLister, cache.SharedIndexInformer, error) { + switch { + case hasVersion(ctx.DiscoveryClient, networkingv1.SchemeGroupVersion.String()): + return &v1Lister{ + lister: ctx.KubeSharedInformerFactory.Networking().V1().Ingresses().Lister(), + }, + ctx.KubeSharedInformerFactory.Networking().V1().Ingresses().Informer(), + nil + case hasVersion(ctx.DiscoveryClient, networkingv1beta1.SchemeGroupVersion.String()): + return &v1beta1Lister{ + lister: ctx.KubeSharedInformerFactory.Networking().V1beta1().Ingresses().Lister(), + }, + ctx.KubeSharedInformerFactory.Networking().V1beta1().Ingresses().Informer(), + nil + default: + return nil, nil, fmt.Errorf("neither %s nor %s have any APIResources", networkingv1.SchemeGroupVersion, networkingv1beta1.SchemeGroupVersion) + } +} + +// NewCreateUpdater returns an InternalIngressCreateUpdater configured for v1 or v1beta1 ingresses depending on the +// versions available in the discovery client +func NewCreateUpdater(ctx *controller.Context) (InternalIngressCreateUpdater, error) { + if hasVersion(ctx.DiscoveryClient, networkingv1.SchemeGroupVersion.String()) { + return &v1CreaterUpdater{ + client: ctx.Client, + }, nil + } else if hasVersion(ctx.DiscoveryClient, networkingv1beta1.SchemeGroupVersion.String()) { + return &v1beta1CreaterUpdater{ + client: ctx.Client, + }, nil + } else { + return nil, fmt.Errorf("neither %s nor %s have any APIResources", networkingv1.SchemeGroupVersion, networkingv1beta1.SchemeGroupVersion) + } +} + +func hasVersion(d discovery.DiscoveryInterface, GroupVersion string) bool { + // check whether the GroupVersion is already known + knownVersions := knownAPIVersionCache.Load().(map[string]bool) + knownVersion, found := knownVersions[GroupVersion] + if found { + return knownVersion + } + + resourceList, err := d.ServerResourcesForGroupVersion(GroupVersion) + if err != nil { + return false + } + for _, r := range resourceList.APIResources { + if r.Kind == "Ingress" { + // Now we know the APIServer supports this GroupVersion, store the result atomically + // in the knownVersions cache. Lock, get the latest copy, atomically update. + cacheLock.Lock() + oldCache := knownAPIVersionCache.Load().(map[string]bool) + newCache := make(map[string]bool) + for k, v := range oldCache { + newCache[k] = v + } + newCache[GroupVersion] = true + knownAPIVersionCache.Store(newCache) + cacheLock.Unlock() + return true + } + } + // no networking error and no Ingresses found in networking.k8s.io/, cache negative result + cacheLock.Lock() + oldCache := knownAPIVersionCache.Load().(map[string]bool) + newCache := make(map[string]bool) + for k, v := range oldCache { + newCache[k] = v + } + newCache[GroupVersion] = false + knownAPIVersionCache.Store(newCache) + cacheLock.Unlock() + return false +} + +func init() { + knownAPIVersionCache.Store(make(map[string]bool)) +} diff --git a/pkg/internal/ingress/ingress_test.go b/pkg/internal/ingress/ingress_test.go new file mode 100644 index 000000000..ec931aa32 --- /dev/null +++ b/pkg/internal/ingress/ingress_test.go @@ -0,0 +1,231 @@ +/* +Copyright 2021 The cert-manager Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ingress + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + networkingv1 "k8s.io/api/networking/v1" + networkingv1beta1 "k8s.io/api/networking/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/informers" + kubefake "k8s.io/client-go/kubernetes/fake" + "k8s.io/utils/pointer" + + "github.com/jetstack/cert-manager/pkg/controller" + discoveryfake "github.com/jetstack/cert-manager/test/unit/discovery" +) + +func TestFunctionalityAgainstV1(t *testing.T) { + // wipe known versions cache + cacheLock.Lock() + knownAPIVersionCache.Store(make(map[string]bool)) + cacheLock.Unlock() + + fakeClient := kubefake.NewSimpleClientset() + v1ctx := &controller.Context{ + RootContext: context.TODO(), + Client: fakeClient, + DiscoveryClient: fakeDiscoveryFor(networkingv1.SchemeGroupVersion), + KubeSharedInformerFactory: informers.NewSharedInformerFactory(fakeClient, 10*time.Hour), + } + ch := make(chan struct{}) + v1ctx.KubeSharedInformerFactory.Start(ch) + errs := testFunctionality(t, v1ctx) + assert.Len(t, errs, 0, "InternalIngress should not fail on an API server that supports networking.k8s.io/v1") + close(ch) +} + +func TestFunctionalityAgainstV1Beta1(t *testing.T) { + cacheLock.Lock() + knownAPIVersionCache.Store(make(map[string]bool)) + cacheLock.Unlock() + + fakeClient := kubefake.NewSimpleClientset() + + v1beta1ctx := &controller.Context{ + RootContext: context.TODO(), + Client: fakeClient, + DiscoveryClient: fakeDiscoveryFor(networkingv1beta1.SchemeGroupVersion), + KubeSharedInformerFactory: informers.NewSharedInformerFactory(fakeClient, 10*time.Hour), + } + ch := make(chan struct{}) + v1beta1ctx.KubeSharedInformerFactory.Start(ch) + errs := testFunctionality(t, v1beta1ctx) + assert.Len(t, errs, 0, "InternalIngress should not fail on an API server that supports networking.k8s.io/v1beta1") + close(ch) +} + +func TestFunctionalityAgainstNone(t *testing.T) { + // wipe known versions cache + cacheLock.Lock() + knownAPIVersionCache.Store(make(map[string]bool)) + cacheLock.Unlock() + + fakeClient := kubefake.NewSimpleClientset() + noneCtx := &controller.Context{ + RootContext: context.TODO(), + Client: fakeClient, + DiscoveryClient: uselessDiscovery(), + KubeSharedInformerFactory: informers.NewSharedInformerFactory(fakeClient, 10*time.Hour), + } + _, _, err := NewListerInformer(noneCtx) + if assert.Error(t, err) { + assert.Equal( + t, + fmt.Errorf("neither %s nor %s have any APIResources", networkingv1.SchemeGroupVersion, networkingv1beta1.SchemeGroupVersion), + err, + ) + } +} + +func fakeDiscoveryFor(version schema.GroupVersion) *discoveryfake.Discovery { + return discoveryfake.NewDiscovery().WithServerResourcesForGroupVersion(func(groupVersion string) (*metav1.APIResourceList, error) { + if groupVersion == version.String() { + return &metav1.APIResourceList{ + TypeMeta: metav1.TypeMeta{}, + GroupVersion: version.String(), + APIResources: []metav1.APIResource{ + { + Name: "ingresses", + SingularName: "Ingress", + Namespaced: true, + Group: version.Group, + Version: version.Version, + Kind: version.WithKind("Ingress").Kind, + Verbs: metav1.Verbs{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"}, + ShortNames: []string{"ing"}, + Categories: []string{"all"}, + StorageVersionHash: "testing", + }, + }, + }, nil + } else { + return &metav1.APIResourceList{}, nil + } + }) +} + +func uselessDiscovery() *discoveryfake.Discovery { + return discoveryfake.NewDiscovery(). + WithServerResourcesForGroupVersion( + func(groupVersion string) (*metav1.APIResourceList, error) { + return &metav1.APIResourceList{}, nil + }, + ) +} + +func testFunctionality(t *testing.T, ctx *controller.Context) []error { + var ret []error + lister, _, err := NewListerInformer(ctx) + assert.NoError(t, err, "New ListerInformer should not fail") + if err != nil { + ret = append(ret, err) + } + createUpdater, err := NewCreateUpdater(ctx) + assert.NoError(t, err, "New CreateUpdater should not fail") + if err != nil { + ret = append(ret, err) + } + _, err = createUpdater.Ingresses("default").Create(context.TODO(), &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: networkingv1.IngressSpec{ + IngressClassName: pointer.StringPtr("test1"), + Rules: []networkingv1.IngressRule{ + { + Host: "test", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: func() *networkingv1.PathType { s := networkingv1.PathTypePrefix; return &s }(), + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test", + Port: networkingv1.ServiceBackendPort{ + Number: 80, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, metav1.CreateOptions{}) + assert.NoError(t, err, "Create should not fail") + if err != nil { + ret = append(ret, err) + } + _, err = createUpdater.Ingresses("default").Update(context.TODO(), &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: networkingv1.IngressSpec{ + IngressClassName: pointer.StringPtr("test1"), + Rules: []networkingv1.IngressRule{ + { + Host: "test", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: func() *networkingv1.PathType { s := networkingv1.PathTypePrefix; return &s }(), + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test", + Port: networkingv1.ServiceBackendPort{ + Number: 80, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, metav1.UpdateOptions{}) + assert.NoError(t, err, "Update should not fail") + if err != nil { + ret = append(ret, err) + } + _, err = lister.List(labels.Everything()) + assert.NoError(t, err, "List should not fail") + + err = createUpdater.Ingresses("default").Delete(context.TODO(), "test", metav1.DeleteOptions{}) + assert.NoError(t, err, "delete should not fail") + if err != nil { + ret = append(ret, err) + } + return ret +} diff --git a/pkg/internal/ingress/v1.go b/pkg/internal/ingress/v1.go new file mode 100644 index 000000000..c86bb1453 --- /dev/null +++ b/pkg/internal/ingress/v1.go @@ -0,0 +1,83 @@ +/* +Copyright 2021 The cert-manager Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ingress + +import ( + "context" + + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + listersv1 "k8s.io/client-go/listers/networking/v1" +) + +type v1Lister struct { + lister listersv1.IngressLister +} + +type v1NamespaceLister struct { + nsLister listersv1.IngressNamespaceLister +} + +func (l *v1Lister) List(selector labels.Selector) ([]*networkingv1.Ingress, error) { + return l.lister.List(selector) +} + +func (l *v1Lister) Ingresses(namespace string) InternalIngressNamespaceLister { + return &v1NamespaceLister{nsLister: l.lister.Ingresses(namespace)} +} + +func (nl *v1NamespaceLister) List(selector labels.Selector) ([]*networkingv1.Ingress, error) { + return nl.nsLister.List(selector) +} + +func (nl *v1NamespaceLister) Get(name string) (*networkingv1.Ingress, error) { + return nl.nsLister.Get(name) +} + +type v1CreaterUpdater struct { + client kubernetes.Interface +} + +func (v1 *v1CreaterUpdater) Ingresses(namespace string) InternalIngressInterface { + return &v1Interface{ + client: v1.client, + ns: namespace, + } +} + +type v1Interface struct { + client kubernetes.Interface + ns string +} + +func (v1 *v1Interface) Get(ctx context.Context, name string, opts metav1.GetOptions) (*networkingv1.Ingress, error) { + return v1.client.NetworkingV1().Ingresses(v1.ns).Get(ctx, name, opts) +} + +func (v1 *v1Interface) Create(ctx context.Context, ingress *networkingv1.Ingress, opts metav1.CreateOptions) (*networkingv1.Ingress, error) { + return v1.client.NetworkingV1().Ingresses(v1.ns).Create(ctx, ingress, opts) +} + +func (v1 *v1Interface) Update(ctx context.Context, ingress *networkingv1.Ingress, opts metav1.UpdateOptions) (*networkingv1.Ingress, error) { + return v1.client.NetworkingV1().Ingresses(v1.ns).Update(ctx, ingress, opts) +} + +func (v1 *v1Interface) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + return v1.client.NetworkingV1().Ingresses(v1.ns).Delete(ctx, name, opts) +} diff --git a/pkg/internal/ingress/v1beta1.go b/pkg/internal/ingress/v1beta1.go new file mode 100644 index 000000000..224210274 --- /dev/null +++ b/pkg/internal/ingress/v1beta1.go @@ -0,0 +1,182 @@ +/* +Copyright 2021 The cert-manager Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ingress + +import ( + "context" + "fmt" + + networkingv1 "k8s.io/api/networking/v1" + networkingv1beta1 "k8s.io/api/networking/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + listersv1beta1 "k8s.io/client-go/listers/networking/v1beta1" +) + +// ConvertedGVKAnnotation is the annotation key set by cert-manager on Ingress +// resources converted from networking.k8s.io/v1beta1 to networking.k8s.io/v1 +// +// On Kubernetes 1.16, 1.17, and 1.18, the `networking.k8s.io/v1` API did not exist yet. +// For these Kubernetes versions, cert-manager converts `networking.k8s.io/v1beta1` +// into `networking.k8s.io/v1`, which is the one version used across cert-manager. +// +// But this conversion means that we lose the "original" apiVersion, which is needed +// by the ingress-shim sync function to set the correct `ownerReference` on the +// Certificate it creates. +// +// This annotation is only set on Ingresses on Kubernetes 1.16, 1.17, and 1.18. +const ConvertedGVKAnnotation = `internal.cert-manager.io/converted-gvk` + +type v1beta1Lister struct { + lister listersv1beta1.IngressLister +} + +type v1beta1NamespaceLister struct { + nsLister listersv1beta1.IngressNamespaceLister +} + +func (l *v1beta1Lister) List(selector labels.Selector) ([]*networkingv1.Ingress, error) { + all, err := l.lister.List(selector) + if err != nil { + return nil, err + } + return convertV1Beta1ListToV1(all) +} + +func (l *v1beta1Lister) Ingresses(namespace string) InternalIngressNamespaceLister { + return &v1beta1NamespaceLister{ + nsLister: l.lister.Ingresses(namespace), + } +} + +func (nl *v1beta1NamespaceLister) List(selector labels.Selector) ([]*networkingv1.Ingress, error) { + all, err := nl.nsLister.List(selector) + if err != nil { + return nil, err + } + return convertV1Beta1ListToV1(all) +} + +func (nl *v1beta1NamespaceLister) Get(name string) (*networkingv1.Ingress, error) { + ing, err := nl.nsLister.Get(name) + if err != nil { + return nil, err + } + return convertV1Beta1ToV1(ing) +} + +func convertV1Beta1ListToV1(list []*networkingv1beta1.Ingress) ([]*networkingv1.Ingress, error) { + var ret []*networkingv1.Ingress + for _, in := range list { + out, err := convertV1Beta1ToV1(in) + if err != nil { + return nil, err + } + ret = append(ret, out) + } + return ret, nil +} + +func convertV1Beta1ToV1(in *networkingv1beta1.Ingress) (*networkingv1.Ingress, error) { + out := new(networkingv1.Ingress) + err := Convert_v1beta1_Ingress_To_networking_Ingress(in.DeepCopy(), out, nil) + + if err != nil { + return nil, fmt.Errorf( + "could not convert %s to %s when processing object %s/%s: %w", + networkingv1beta1.SchemeGroupVersion, + networkingv1.SchemeGroupVersion, + in.Namespace, + in.Name, + err, + ) + } + if out.Annotations == nil { + out.Annotations = make(map[string]string) + } + out.Annotations[ConvertedGVKAnnotation] = networkingv1beta1.SchemeGroupVersion.WithKind("Ingress").String() + return out, nil +} + +func convertV1ToV1Beta1(in *networkingv1.Ingress) (*networkingv1beta1.Ingress, error) { + out := new(networkingv1beta1.Ingress) + err := Convert_networking_Ingress_To_v1beta1_Ingress(in.DeepCopy(), out, nil) + if err != nil { + return nil, fmt.Errorf( + "could not convert %s to %s when processing object %s/%s: %w", + networkingv1.SchemeGroupVersion, + networkingv1beta1.SchemeGroupVersion, + in.Namespace, + in.Name, + err, + ) + } + return out, nil +} + +type v1beta1CreaterUpdater struct { + client kubernetes.Interface +} + +func (v *v1beta1CreaterUpdater) Ingresses(namespace string) InternalIngressInterface { + return &v1beta1Interface{ + client: v.client, + ns: namespace, + } +} + +type v1beta1Interface struct { + client kubernetes.Interface + ns string +} + +func (v *v1beta1Interface) Get(ctx context.Context, name string, opts metav1.GetOptions) (*networkingv1.Ingress, error) { + ing, err := v.client.NetworkingV1beta1().Ingresses(v.ns).Get(ctx, name, opts) + if err != nil { + return nil, err + } + return convertV1Beta1ToV1(ing) +} + +func (v *v1beta1Interface) Create(ctx context.Context, ingress *networkingv1.Ingress, opts metav1.CreateOptions) (*networkingv1.Ingress, error) { + ing, err := convertV1ToV1Beta1(ingress) + if err != nil { + return nil, err + } + newIng, err := v.client.NetworkingV1beta1().Ingresses(v.ns).Create(ctx, ing, opts) + if err != nil { + return nil, err + } + return convertV1Beta1ToV1(newIng) +} + +func (v *v1beta1Interface) Update(ctx context.Context, ingress *networkingv1.Ingress, opts metav1.UpdateOptions) (*networkingv1.Ingress, error) { + ing, err := convertV1ToV1Beta1(ingress) + if err != nil { + return nil, err + } + newIng, err := v.client.NetworkingV1beta1().Ingresses(v.ns).Update(ctx, ing, opts) + if err != nil { + return nil, err + } + return convertV1Beta1ToV1(newIng) +} + +func (v *v1beta1Interface) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + return v.client.NetworkingV1beta1().Ingresses(v.ns).Delete(ctx, name, opts) +} diff --git a/pkg/issuer/acme/http/BUILD.bazel b/pkg/issuer/acme/http/BUILD.bazel index eba282845..9c1a45bfa 100644 --- a/pkg/issuer/acme/http/BUILD.bazel +++ b/pkg/issuer/acme/http/BUILD.bazel @@ -14,11 +14,12 @@ go_library( "//pkg/apis/acme/v1:go_default_library", "//pkg/apis/certmanager/v1:go_default_library", "//pkg/controller:go_default_library", + "//pkg/internal/ingress:go_default_library", "//pkg/issuer/acme/http/solver:go_default_library", "//pkg/logs:go_default_library", "//pkg/util:go_default_library", "@io_k8s_api//core/v1:go_default_library", - "@io_k8s_api//networking/v1beta1:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", "@io_k8s_apimachinery//pkg/api/errors:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_apimachinery//pkg/labels:go_default_library", @@ -26,7 +27,6 @@ go_library( "@io_k8s_apimachinery//pkg/util/errors:go_default_library", "@io_k8s_apimachinery//pkg/util/intstr:go_default_library", "@io_k8s_client_go//listers/core/v1:go_default_library", - "@io_k8s_client_go//listers/networking/v1beta1:go_default_library", "@io_k8s_utils//net:go_default_library", "@io_k8s_utils//pointer:go_default_library", ], @@ -47,13 +47,12 @@ go_test( "//pkg/controller/test:go_default_library", "//test/unit/gen:go_default_library", "@io_k8s_api//core/v1:go_default_library", - "@io_k8s_api//networking/v1beta1:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", "@io_k8s_apimachinery//pkg/api/errors:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_apimachinery//pkg/labels:go_default_library", "@io_k8s_apimachinery//pkg/runtime:go_default_library", "@io_k8s_apimachinery//pkg/util/diff:go_default_library", - "@io_k8s_apimachinery//pkg/util/intstr:go_default_library", "@io_k8s_client_go//testing:go_default_library", ], ) diff --git a/pkg/issuer/acme/http/http.go b/pkg/issuer/acme/http/http.go index a91ba4dfb..2914d5c2d 100644 --- a/pkg/issuer/acme/http/http.go +++ b/pkg/issuer/acme/http/http.go @@ -31,11 +31,11 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" corev1listers "k8s.io/client-go/listers/core/v1" - networkingv1beta1listers "k8s.io/client-go/listers/networking/v1beta1" cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1" v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" "github.com/jetstack/cert-manager/pkg/controller" + "github.com/jetstack/cert-manager/pkg/internal/ingress" "github.com/jetstack/cert-manager/pkg/issuer/acme/http/solver" logf "github.com/jetstack/cert-manager/pkg/logs" pkgutil "github.com/jetstack/cert-manager/pkg/util" @@ -57,9 +57,10 @@ var ( type Solver struct { *controller.Context - podLister corev1listers.PodLister - serviceLister corev1listers.ServiceLister - ingressLister networkingv1beta1listers.IngressLister + podLister corev1listers.PodLister + serviceLister corev1listers.ServiceLister + ingressLister ingress.InternalIngressLister + ingressCreateUpdater ingress.InternalIngressCreateUpdater testReachability reachabilityTest requiredPasses int @@ -67,17 +68,25 @@ type Solver struct { type reachabilityTest func(ctx context.Context, url *url.URL, key string) error -// NewSolver returns a new ACME HTTP01 solver for the given Issuer and client. -// TODO: refactor this to have fewer args -func NewSolver(ctx *controller.Context) *Solver { - return &Solver{ - Context: ctx, - podLister: ctx.KubeSharedInformerFactory.Core().V1().Pods().Lister(), - serviceLister: ctx.KubeSharedInformerFactory.Core().V1().Services().Lister(), - ingressLister: ctx.KubeSharedInformerFactory.Networking().V1beta1().Ingresses().Lister(), - testReachability: testReachability, - requiredPasses: 5, +// NewSolver returns a new ACME HTTP01 solver for the given *controller.Context. +func NewSolver(ctx *controller.Context) (*Solver, error) { + ingressLister, _, err := ingress.NewListerInformer(ctx) + if err != nil { + return nil, err } + ingressCreateUpdater, err := ingress.NewCreateUpdater(ctx) + if err != nil { + return nil, err + } + return &Solver{ + Context: ctx, + podLister: ctx.KubeSharedInformerFactory.Core().V1().Pods().Lister(), + serviceLister: ctx.KubeSharedInformerFactory.Core().V1().Services().Lister(), + ingressLister: ingressLister, + ingressCreateUpdater: ingressCreateUpdater, + testReachability: testReachability, + requiredPasses: 5, + }, nil } func http01LogCtx(ctx context.Context) context.Context { diff --git a/pkg/issuer/acme/http/ingress.go b/pkg/issuer/acme/http/ingress.go index 5573daf49..942b44168 100644 --- a/pkg/issuer/acme/http/ingress.go +++ b/pkg/issuer/acme/http/ingress.go @@ -21,13 +21,12 @@ import ( "fmt" "net" - networkingv1beta1 "k8s.io/api/networking/v1beta1" + networkingv1 "k8s.io/api/networking/v1" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/intstr" cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1" cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" @@ -37,7 +36,7 @@ import ( // getIngressesForChallenge returns a list of Ingresses that were created to solve // http challenges for the given domain -func (s *Solver) getIngressesForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*networkingv1beta1.Ingress, error) { +func (s *Solver) getIngressesForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*networkingv1.Ingress, error) { log := logf.FromContext(ctx) podLabels := podLabels(ch) @@ -56,7 +55,7 @@ func (s *Solver) getIngressesForChallenge(ctx context.Context, ch *cmacme.Challe return nil, err } - var relevantIngresses []*networkingv1beta1.Ingress + var relevantIngresses []*networkingv1.Ingress for _, ingress := range ingressList { if !metav1.IsControlledBy(ingress, ch) { logf.WithRelatedResource(log, ingress).Info("found existing solver ingress for this challenge resource, however " + @@ -72,7 +71,7 @@ func (s *Solver) getIngressesForChallenge(ctx context.Context, ch *cmacme.Challe // ensureIngress will ensure the ingress required to solve this challenge // exists, or if an existing ingress is specified on the secret will ensure // that the ingress has an appropriate challenge path configured -func (s *Solver) ensureIngress(ctx context.Context, ch *cmacme.Challenge, svcName string) (ing *networkingv1beta1.Ingress, err error) { +func (s *Solver) ensureIngress(ctx context.Context, ch *cmacme.Challenge, svcName string) (ing *networkingv1.Ingress, err error) { log := logf.FromContext(ctx).WithName("ensureIngress") httpDomainCfg, err := httpDomainCfgForChallenge(ch) if err != nil { @@ -113,13 +112,13 @@ func (s *Solver) ensureIngress(ctx context.Context, ch *cmacme.Challenge, svcNam return s.createIngress(ctx, ch, svcName) } -func ingressServiceName(ing *networkingv1beta1.Ingress) string { - return ing.Spec.Rules[0].HTTP.Paths[0].Backend.ServiceName +func ingressServiceName(ing *networkingv1.Ingress) string { + return ing.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name } // createIngress will create a challenge solving ingress for the given certificate, // domain, token and key. -func (s *Solver) createIngress(ctx context.Context, ch *cmacme.Challenge, svcName string) (*networkingv1beta1.Ingress, error) { +func (s *Solver) createIngress(ctx context.Context, ch *cmacme.Challenge, svcName string) (*networkingv1.Ingress, error) { ing, err := buildIngressResource(ch, svcName) if err != nil { return nil, err @@ -131,10 +130,10 @@ func (s *Solver) createIngress(ctx context.Context, ch *cmacme.Challenge, svcNam ing = s.mergeIngressObjectMetaWithIngressResourceTemplate(ing, ch.Spec.Solver.HTTP01.Ingress.IngressTemplate) } - return s.Client.NetworkingV1beta1().Ingresses(ch.Namespace).Create(ctx, ing, metav1.CreateOptions{}) + return s.ingressCreateUpdater.Ingresses(ch.Namespace).Create(ctx, ing, metav1.CreateOptions{}) } -func buildIngressResource(ch *cmacme.Challenge, svcName string) (*networkingv1beta1.Ingress, error) { +func buildIngressResource(ch *cmacme.Challenge, svcName string) (*networkingv1.Ingress, error) { httpDomainCfg, err := httpDomainCfgForChallenge(ch) if err != nil { return nil, err @@ -162,7 +161,7 @@ func buildIngressResource(ch *cmacme.Challenge, svcName string) (*networkingv1be if net.ParseIP(httpHost) != nil { httpHost = "" } - return &networkingv1beta1.Ingress{ + return &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "cm-acme-http-solver-", Namespace: ch.Namespace, @@ -170,13 +169,15 @@ func buildIngressResource(ch *cmacme.Challenge, svcName string) (*networkingv1be Annotations: ingAnnotations, OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ch, challengeGvk)}, }, - Spec: networkingv1beta1.IngressSpec{ - Rules: []networkingv1beta1.IngressRule{ + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ { Host: httpHost, - IngressRuleValue: networkingv1beta1.IngressRuleValue{ - HTTP: &networkingv1beta1.HTTPIngressRuleValue{ - Paths: []networkingv1beta1.HTTPIngressPath{ingPathToAdd}, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + ingPathToAdd, + }, }, }, }, @@ -186,7 +187,7 @@ func buildIngressResource(ch *cmacme.Challenge, svcName string) (*networkingv1be } // Merge object meta from the ingress template. Fall back to default values. -func (s *Solver) mergeIngressObjectMetaWithIngressResourceTemplate(ingress *networkingv1beta1.Ingress, ingressTempl *cmacme.ACMEChallengeSolverHTTP01IngressTemplate) *networkingv1beta1.Ingress { +func (s *Solver) mergeIngressObjectMetaWithIngressResourceTemplate(ingress *networkingv1.Ingress, ingressTempl *cmacme.ACMEChallengeSolverHTTP01IngressTemplate) *networkingv1.Ingress { if ingressTempl == nil { return ingress } @@ -210,7 +211,7 @@ func (s *Solver) mergeIngressObjectMetaWithIngressResourceTemplate(ingress *netw return ingress } -func (s *Solver) addChallengePathToIngress(ctx context.Context, ch *cmacme.Challenge, svcName string) (*networkingv1beta1.Ingress, error) { +func (s *Solver) addChallengePathToIngress(ctx context.Context, ch *cmacme.Challenge, svcName string) (*networkingv1.Ingress, error) { httpDomainCfg, err := httpDomainCfgForChallenge(ch) if err != nil { return nil, err @@ -227,36 +228,36 @@ func (s *Solver) addChallengePathToIngress(ctx context.Context, ch *cmacme.Chall for _, rule := range ing.Spec.Rules { if rule.Host == ch.Spec.DNSName { if rule.HTTP == nil { - rule.HTTP = &networkingv1beta1.HTTPIngressRuleValue{} + rule.HTTP = &networkingv1.HTTPIngressRuleValue{} } for i, p := range rule.HTTP.Paths { // if an existing path exists on this rule for the challenge path, // we overwrite it else we'll confuse ingress controllers if p.Path == ingPathToAdd.Path { // ingress resource is already up to date - if p.Backend.ServiceName == ingPathToAdd.Backend.ServiceName && - p.Backend.ServicePort == ingPathToAdd.Backend.ServicePort { + if p.Backend.Service.Name == ingPathToAdd.Backend.Service.Name && + p.Backend.Service.Port == ingPathToAdd.Backend.Service.Port { return ing, nil } rule.HTTP.Paths[i] = ingPathToAdd - return s.Client.NetworkingV1beta1().Ingresses(ing.Namespace).Update(ctx, ing, metav1.UpdateOptions{}) + return s.ingressCreateUpdater.Ingresses(ing.Namespace).Update(ctx, ing, metav1.UpdateOptions{}) } } - rule.HTTP.Paths = append([]networkingv1beta1.HTTPIngressPath{ingPathToAdd}, rule.HTTP.Paths...) - return s.Client.NetworkingV1beta1().Ingresses(ing.Namespace).Update(ctx, ing, metav1.UpdateOptions{}) + rule.HTTP.Paths = append([]networkingv1.HTTPIngressPath{ingPathToAdd}, rule.HTTP.Paths...) + return s.ingressCreateUpdater.Ingresses(ing.Namespace).Update(ctx, ing, metav1.UpdateOptions{}) } } // if one doesn't exist, create a new IngressRule - ing.Spec.Rules = append(ing.Spec.Rules, networkingv1beta1.IngressRule{ + ing.Spec.Rules = append(ing.Spec.Rules, networkingv1.IngressRule{ Host: ch.Spec.DNSName, - IngressRuleValue: networkingv1beta1.IngressRuleValue{ - HTTP: &networkingv1beta1.HTTPIngressRuleValue{ - Paths: []networkingv1beta1.HTTPIngressPath{ingPathToAdd}, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ingPathToAdd}, }, }, }) - return s.Client.NetworkingV1beta1().Ingresses(ing.Namespace).Update(ctx, ing, metav1.UpdateOptions{}) + return s.ingressCreateUpdater.Ingresses(ing.Namespace).Update(ctx, ing, metav1.UpdateOptions{}) } // cleanupIngresses will remove the rules added by cert-manager to an existing @@ -283,7 +284,7 @@ func (s *Solver) cleanupIngresses(ctx context.Context, ch *cmacme.Challenge) err log := logf.WithRelatedResource(log, ingress).V(logf.DebugLevel) log.V(logf.DebugLevel).Info("deleting ingress resource") - err := s.Client.NetworkingV1beta1().Ingresses(ingress.Namespace).Delete(ctx, ingress.Name, metav1.DeleteOptions{}) + err := s.ingressCreateUpdater.Ingresses(ingress.Namespace).Delete(ctx, ingress.Name, metav1.DeleteOptions{}) if err != nil { log.V(logf.WarnLevel).Info("failed to delete ingress resource", "error", err) errs = append(errs, err) @@ -295,7 +296,7 @@ func (s *Solver) cleanupIngresses(ctx context.Context, ch *cmacme.Challenge) err } // otherwise, we need to remove any cert-manager added rules from the ingress resource - ing, err := s.Client.NetworkingV1beta1().Ingresses(ch.Namespace).Get(ctx, existingIngressName, metav1.GetOptions{}) + ing, err := s.ingressCreateUpdater.Ingresses(ch.Namespace).Get(ctx, existingIngressName, metav1.GetOptions{}) if k8sErrors.IsNotFound(err) { log.Error(err, "named ingress resource not found, skipping cleanup") return nil @@ -307,7 +308,7 @@ func (s *Solver) cleanupIngresses(ctx context.Context, ch *cmacme.Challenge) err log.V(logf.DebugLevel).Info("attempting to clean up automatically added solver paths on ingress resource") ingPathToDel := solverPathFn(ch.Spec.Token) - var ingRules []networkingv1beta1.IngressRule + var ingRules []networkingv1.IngressRule for _, rule := range ing.Spec.Rules { // always retain rules that are not for the same DNSName if rule.Host != ch.Spec.DNSName { @@ -338,7 +339,7 @@ func (s *Solver) cleanupIngresses(ctx context.Context, ch *cmacme.Challenge) err ing.Spec.Rules = ingRules - _, err = s.Client.NetworkingV1beta1().Ingresses(ing.Namespace).Update(ctx, ing, metav1.UpdateOptions{}) + _, err = s.ingressCreateUpdater.Ingresses(ing.Namespace).Update(ctx, ing, metav1.UpdateOptions{}) if err != nil { return err } @@ -350,12 +351,17 @@ func (s *Solver) cleanupIngresses(ctx context.Context, ch *cmacme.Challenge) err // ingressPath returns the ingress HTTPIngressPath object needed to solve this // challenge. -func ingressPath(token, serviceName string) networkingv1beta1.HTTPIngressPath { - return networkingv1beta1.HTTPIngressPath{ - Path: solverPathFn(token), - Backend: networkingv1beta1.IngressBackend{ - ServiceName: serviceName, - ServicePort: intstr.FromInt(acmeSolverListenPort), +func ingressPath(token, serviceName string) networkingv1.HTTPIngressPath { + return networkingv1.HTTPIngressPath{ + Path: solverPathFn(token), + PathType: func() *networkingv1.PathType { s := networkingv1.PathTypeExact; return &s }(), + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: serviceName, + Port: networkingv1.ServiceBackendPort{ + Number: acmeSolverListenPort, + }, + }, }, } } diff --git a/pkg/issuer/acme/http/ingress_test.go b/pkg/issuer/acme/http/ingress_test.go index e0e223b27..7453ccb05 100644 --- a/pkg/issuer/acme/http/ingress_test.go +++ b/pkg/issuer/acme/http/ingress_test.go @@ -22,13 +22,12 @@ import ( "reflect" "testing" - "k8s.io/api/networking/v1beta1" + "k8s.io/api/networking/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/diff" - "k8s.io/apimachinery/pkg/util/intstr" coretesting "k8s.io/client-go/testing" cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1" @@ -59,8 +58,8 @@ func TestGetIngressesForChallenge(t *testing.T) { s.Builder.Sync() }, CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) { - createdIngress := s.testResources[createdIngressKey].(*v1beta1.Ingress) - resp := args[0].([]*v1beta1.Ingress) + createdIngress := s.testResources[createdIngressKey].(*v1.Ingress) + resp := args[0].([]*v1.Ingress) if len(resp) != 1 { t.Errorf("expected one ingress to be returned, but got %d", len(resp)) t.Fail() @@ -92,8 +91,8 @@ func TestGetIngressesForChallenge(t *testing.T) { s.Builder.Sync() }, CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) { - createdIngress := s.testResources[createdIngressKey].(*v1beta1.Ingress) - resp := args[0].([]*v1beta1.Ingress) + createdIngress := s.testResources[createdIngressKey].(*v1.Ingress) + resp := args[0].([]*v1.Ingress) if len(resp) != 1 { t.Errorf("expected one ingress to be returned, but got %d", len(resp)) t.Fail() @@ -126,7 +125,7 @@ func TestGetIngressesForChallenge(t *testing.T) { s.Builder.Sync() }, CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) { - resp := args[0].([]*v1beta1.Ingress) + resp := args[0].([]*v1.Ingress) if len(resp) != 0 { t.Errorf("expected zero ingresses to be returned, but got %d", len(resp)) t.Fail() @@ -176,8 +175,8 @@ func TestCleanupIngresses(t *testing.T) { s.Builder.Sync() }, CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) { - createdIngress := s.testResources[createdIngressKey].(*v1beta1.Ingress) - ing, err := s.Builder.FakeKubeClient().NetworkingV1beta1().Ingresses(s.Challenge.Namespace).Get(context.TODO(), createdIngress.Name, metav1.GetOptions{}) + createdIngress := s.testResources[createdIngressKey].(*v1.Ingress) + ing, err := s.Builder.FakeKubeClient().NetworkingV1().Ingresses(s.Challenge.Namespace).Get(context.TODO(), createdIngress.Name, metav1.GetOptions{}) if err != nil && !apierrors.IsNotFound(err) { t.Errorf("error when getting test ingress, expected 'not found' but got: %v", err) } @@ -210,8 +209,8 @@ func TestCleanupIngresses(t *testing.T) { s.testResources[createdIngressKey] = ing }, CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) { - createdIngress := s.testResources[createdIngressKey].(*v1beta1.Ingress) - _, err := s.Builder.FakeKubeClient().NetworkingV1beta1().Ingresses(s.Challenge.Namespace).Get(context.TODO(), createdIngress.Name, metav1.GetOptions{}) + createdIngress := s.testResources[createdIngressKey].(*v1.Ingress) + _, err := s.Builder.FakeKubeClient().NetworkingV1().Ingresses(s.Challenge.Namespace).Get(context.TODO(), createdIngress.Name, metav1.GetOptions{}) if apierrors.IsNotFound(err) { t.Errorf("expected ingress resource %q to not be deleted, but it was deleted", createdIngress.Name) } @@ -223,27 +222,35 @@ func TestCleanupIngresses(t *testing.T) { "should clean up an ingress with a single challenge path inserted": { Builder: &test.Builder{ KubeObjects: []runtime.Object{ - &v1beta1.Ingress{ + &v1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "testingress", Namespace: defaultTestNamespace, }, - Spec: v1beta1.IngressSpec{ - Backend: &v1beta1.IngressBackend{ - ServiceName: "testsvc", - ServicePort: intstr.FromInt(8080), + Spec: v1.IngressSpec{ + DefaultBackend: &v1.IngressBackend{ + Service: &v1.IngressServiceBackend{ + Name: "", + Port: v1.ServiceBackendPort{ + Number: 8080, + }, + }, }, - Rules: []v1beta1.IngressRule{ + Rules: []v1.IngressRule{ { Host: "example.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ + IngressRuleValue: v1.IngressRuleValue{ + HTTP: &v1.HTTPIngressRuleValue{ + Paths: []v1.HTTPIngressPath{ { Path: "/.well-known/acme-challenge/abcd", - Backend: v1beta1.IngressBackend{ - ServiceName: "solversvc", - ServicePort: intstr.FromInt(8081), + Backend: v1.IngressBackend{ + Service: &v1.IngressServiceBackend{ + Name: "solversvc", + Port: v1.ServiceBackendPort{ + Number: 8081, + }, + }, }, }, }, @@ -275,10 +282,10 @@ func TestCleanupIngresses(t *testing.T) { PreFn: func(t *testing.T, s *solverFixture) { }, CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) { - expectedIng := s.KubeObjects[0].(*v1beta1.Ingress).DeepCopy() + expectedIng := s.KubeObjects[0].(*v1.Ingress).DeepCopy() expectedIng.Spec.Rules = nil - actualIng, err := s.Builder.FakeKubeClient().NetworkingV1beta1().Ingresses(s.Challenge.Namespace).Get(context.TODO(), expectedIng.Name, metav1.GetOptions{}) + actualIng, err := s.Builder.FakeKubeClient().NetworkingV1().Ingresses(s.Challenge.Namespace).Get(context.TODO(), expectedIng.Name, metav1.GetOptions{}) if apierrors.IsNotFound(err) { t.Errorf("expected ingress resource %q to not be deleted, but it was deleted", expectedIng.Name) } @@ -294,27 +301,35 @@ func TestCleanupIngresses(t *testing.T) { "should clean up an ingress with a single challenge path inserted without removing second HTTP rule": { Builder: &test.Builder{ KubeObjects: []runtime.Object{ - &v1beta1.Ingress{ + &v1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "testingress", Namespace: defaultTestNamespace, }, - Spec: v1beta1.IngressSpec{ - Backend: &v1beta1.IngressBackend{ - ServiceName: "testsvc", - ServicePort: intstr.FromInt(8080), + Spec: v1.IngressSpec{ + DefaultBackend: &v1.IngressBackend{ + Service: &v1.IngressServiceBackend{ + Name: "testsvc", + Port: v1.ServiceBackendPort{ + Number: 8080, + }, + }, }, - Rules: []v1beta1.IngressRule{ + Rules: []v1.IngressRule{ { Host: "example.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ + IngressRuleValue: v1.IngressRuleValue{ + HTTP: &v1.HTTPIngressRuleValue{ + Paths: []v1.HTTPIngressPath{ { Path: "/.well-known/acme-challenge/abcd", - Backend: v1beta1.IngressBackend{ - ServiceName: "solversvc", - ServicePort: intstr.FromInt(8081), + Backend: v1.IngressBackend{ + Service: &v1.IngressServiceBackend{ + Name: "solversvc", + Port: v1.ServiceBackendPort{ + Number: 8081, + }, + }, }, }, }, @@ -323,14 +338,18 @@ func TestCleanupIngresses(t *testing.T) { }, { Host: "a.example.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ + IngressRuleValue: v1.IngressRuleValue{ + HTTP: &v1.HTTPIngressRuleValue{ + Paths: []v1.HTTPIngressPath{ { Path: "/", - Backend: v1beta1.IngressBackend{ - ServiceName: "real-backend-svc", - ServicePort: intstr.FromInt(8081), + Backend: v1.IngressBackend{ + Service: &v1.IngressServiceBackend{ + Name: "real-backend-svc", + Port: v1.ServiceBackendPort{ + Number: 8081, + }, + }, }, }, }, @@ -362,10 +381,10 @@ func TestCleanupIngresses(t *testing.T) { PreFn: func(t *testing.T, s *solverFixture) { }, CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) { - expectedIng := s.KubeObjects[0].(*v1beta1.Ingress).DeepCopy() - expectedIng.Spec.Rules = []v1beta1.IngressRule{expectedIng.Spec.Rules[1]} + expectedIng := s.KubeObjects[0].(*v1.Ingress).DeepCopy() + expectedIng.Spec.Rules = []v1.IngressRule{expectedIng.Spec.Rules[1]} - actualIng, err := s.Builder.FakeKubeClient().NetworkingV1beta1().Ingresses(s.Challenge.Namespace).Get(context.TODO(), expectedIng.Name, metav1.GetOptions{}) + actualIng, err := s.Builder.FakeKubeClient().NetworkingV1().Ingresses(s.Challenge.Namespace).Get(context.TODO(), expectedIng.Name, metav1.GetOptions{}) if apierrors.IsNotFound(err) { t.Errorf("expected ingress resource %q to not be deleted, but it was deleted", expectedIng.Name) } @@ -520,9 +539,9 @@ func TestMergeIngressObjectMetaWithIngressResourceTemplate(t *testing.T) { s.Builder.Sync() }, CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) { - expectedIngress := s.testResources[createdIngressKey].(*v1beta1.Ingress) + expectedIngress := s.testResources[createdIngressKey].(*v1.Ingress) - resp, ok := args[0].(*v1beta1.Ingress) + resp, ok := args[0].(*v1.Ingress) if !ok { t.Errorf("expected ingress to be returned, but got %v", args[0]) t.Fail() diff --git a/pkg/issuer/acme/http/util_test.go b/pkg/issuer/acme/http/util_test.go index 8f0fddb28..2ce9a7642 100644 --- a/pkg/issuer/acme/http/util_test.go +++ b/pkg/issuer/acme/http/util_test.go @@ -63,7 +63,11 @@ func (s *solverFixture) Setup(t *testing.T) { s.Builder.T = t } s.Builder.Init() - s.Solver = buildFakeSolver(s.Builder) + var err error + s.Solver, err = buildFakeSolver(s.Builder) + if err != nil { + t.Fatal(err) + } if s.PreFn != nil { s.PreFn(t, s) s.Builder.Sync() @@ -80,11 +84,14 @@ func (s *solverFixture) Finish(t *testing.T, args ...interface{}) { } } -func buildFakeSolver(b *test.Builder) *Solver { +func buildFakeSolver(b *test.Builder) (*Solver, error) { b.Init() - s := NewSolver(b.Context) + s, err := NewSolver(b.Context) + if err != nil { + return nil, err + } b.Start() - return s + return s, nil } func strPtr(s string) *string { diff --git a/test/e2e/suite/conformance/certificates/BUILD.bazel b/test/e2e/suite/conformance/certificates/BUILD.bazel index a59b691c7..2e5475978 100644 --- a/test/e2e/suite/conformance/certificates/BUILD.bazel +++ b/test/e2e/suite/conformance/certificates/BUILD.bazel @@ -21,6 +21,8 @@ go_library( "@com_github_onsi_ginkgo//:go_default_library", "@com_github_onsi_gomega//:go_default_library", "@io_k8s_api//core/v1:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", + "@io_k8s_api//networking/v1beta1:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_apimachinery//pkg/types:go_default_library", "@io_k8s_client_go//util/retry:go_default_library", diff --git a/test/e2e/suite/conformance/certificates/tests.go b/test/e2e/suite/conformance/certificates/tests.go index c7250818f..81f9bc988 100644 --- a/test/e2e/suite/conformance/certificates/tests.go +++ b/test/e2e/suite/conformance/certificates/tests.go @@ -24,6 +24,8 @@ import ( . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + networkingv1beta1 "k8s.io/api/networking/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/retry" @@ -641,20 +643,38 @@ func (s *Suite) Define() { }, featureset.ReusePrivateKeyFeature, featureset.OnlySAN) s.it(f, "should issue a certificate for a single distinct DNS Name defined by an ingress with annotations", func(issuerRef cmmeta.ObjectReference) { - ingClient := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace.Name) + var certName string + switch { + case e2eutil.HasIngresses(f.KubeClientSet.Discovery(), networkingv1.SchemeGroupVersion.String()): + ingClient := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace.Name) - name := "testcert-ingress" - secretName := "testcert-ingress-tls" + name := "testcert-ingress" + secretName := "testcert-ingress-tls" - By("Creating an Ingress with the issuer name annotation set") - ingress, err := ingClient.Create(context.TODO(), e2eutil.NewIngress(name, secretName, map[string]string{ - "cert-manager.io/issuer": issuerRef.Name, - "cert-manager.io/issuer-kind": issuerRef.Kind, - "cert-manager.io/issuer-group": issuerRef.Group, - }, e2eutil.RandomSubdomain(s.DomainSuffix)), metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) + By("Creating an Ingress with the issuer name annotation set") + ingress, err := ingClient.Create(context.TODO(), e2eutil.NewIngress(name, secretName, map[string]string{ + "cert-manager.io/issuer": issuerRef.Name, + "cert-manager.io/issuer-kind": issuerRef.Kind, + "cert-manager.io/issuer-group": issuerRef.Group, + }, e2eutil.RandomSubdomain(s.DomainSuffix)), metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + certName = ingress.Spec.TLS[0].SecretName + case e2eutil.HasIngresses(f.KubeClientSet.Discovery(), networkingv1beta1.SchemeGroupVersion.String()): + ingClient := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace.Name) + name := "testcert-ingress" + secretName := "testcert-ingress-tls" - certName := ingress.Spec.TLS[0].SecretName + By("Creating an Ingress with the issuer name annotation set") + ingress, err := ingClient.Create(context.TODO(), e2eutil.NewV1Beta1Ingress(name, secretName, map[string]string{ + "cert-manager.io/issuer": issuerRef.Name, + "cert-manager.io/issuer-kind": issuerRef.Kind, + "cert-manager.io/issuer-group": issuerRef.Group, + }, e2eutil.RandomSubdomain(s.DomainSuffix)), metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + certName = ingress.Spec.TLS[0].SecretName + default: + Fail("Neither " + networkingv1.SchemeGroupVersion.String() + " nor " + networkingv1beta1.SchemeGroupVersion.String() + " were discovered in the API server") + } By("Waiting for the Certificate to exist...") Expect(e2eutil.WaitForCertificateToExist( @@ -662,7 +682,7 @@ func (s *Suite) Define() { )).NotTo(HaveOccurred()) By("Waiting for the Certificate to be issued...") - _, err = f.Helper().WaitForCertificateReady(f.Namespace.Name, certName, time.Minute*5) + _, err := f.Helper().WaitForCertificateReady(f.Namespace.Name, certName, time.Minute*5) Expect(err).NotTo(HaveOccurred()) By("Validating the issued Certificate...") @@ -671,26 +691,51 @@ func (s *Suite) Define() { }, featureset.OnlySAN) s.it(f, "should issue a certificate defined by an ingress with certificate field annotations", func(issuerRef cmmeta.ObjectReference) { - ingClient := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace.Name) - - name := "testcert-ingress" - secretName := "testcert-ingress-tls" + var certName string domain := e2eutil.RandomSubdomain(s.DomainSuffix) duration := time.Hour * 999 renewBefore := time.Hour * 111 - By("Creating an Ingress with annotations for issuerRef and other Certificate fields") - ingress, err := ingClient.Create(context.TODO(), e2eutil.NewIngress(name, secretName, map[string]string{ - "cert-manager.io/issuer": issuerRef.Name, - "cert-manager.io/issuer-kind": issuerRef.Kind, - "cert-manager.io/issuer-group": issuerRef.Group, - "cert-manager.io/common-name": domain, - "cert-manager.io/duration": duration.String(), - "cert-manager.io/renew-before": renewBefore.String(), - }, domain), metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) + switch { + case e2eutil.HasIngresses(f.KubeClientSet.Discovery(), networkingv1.SchemeGroupVersion.String()): + ingClient := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace.Name) - certName := ingress.Spec.TLS[0].SecretName + name := "testcert-ingress" + secretName := "testcert-ingress-tls" + + By("Creating an Ingress with annotations for issuerRef and other Certificate fields") + ingress, err := ingClient.Create(context.TODO(), e2eutil.NewIngress(name, secretName, map[string]string{ + "cert-manager.io/issuer": issuerRef.Name, + "cert-manager.io/issuer-kind": issuerRef.Kind, + "cert-manager.io/issuer-group": issuerRef.Group, + "cert-manager.io/common-name": domain, + "cert-manager.io/duration": duration.String(), + "cert-manager.io/renew-before": renewBefore.String(), + }, domain), metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + certName = ingress.Spec.TLS[0].SecretName + case e2eutil.HasIngresses(f.KubeClientSet.Discovery(), networkingv1beta1.SchemeGroupVersion.String()): + ingClient := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace.Name) + + name := "testcert-ingress" + secretName := "testcert-ingress-tls" + + By("Creating an Ingress with annotations for issuerRef and other Certificate fields") + ingress, err := ingClient.Create(context.TODO(), e2eutil.NewV1Beta1Ingress(name, secretName, map[string]string{ + "cert-manager.io/issuer": issuerRef.Name, + "cert-manager.io/issuer-kind": issuerRef.Kind, + "cert-manager.io/issuer-group": issuerRef.Group, + "cert-manager.io/common-name": domain, + "cert-manager.io/duration": duration.String(), + "cert-manager.io/renew-before": renewBefore.String(), + }, domain), metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + certName = ingress.Spec.TLS[0].SecretName + default: + Fail("Neither " + networkingv1.SchemeGroupVersion.String() + " nor " + networkingv1beta1.SchemeGroupVersion.String() + " were discovered in the API server") + } By("Waiting for the Certificate to exist...") Expect(e2eutil.WaitForCertificateToExist( @@ -698,7 +743,7 @@ func (s *Suite) Define() { )).NotTo(HaveOccurred()) By("Waiting for the Certificate to be issued...") - _, err = f.Helper().WaitForCertificateReady(f.Namespace.Name, certName, time.Minute*5) + _, err := f.Helper().WaitForCertificateReady(f.Namespace.Name, certName, time.Minute*5) Expect(err).NotTo(HaveOccurred()) // Verify that the ingres-shim has translated all the supplied diff --git a/test/e2e/suite/issuers/acme/certificate/BUILD.bazel b/test/e2e/suite/issuers/acme/certificate/BUILD.bazel index 96ed0f47a..84f944036 100644 --- a/test/e2e/suite/issuers/acme/certificate/BUILD.bazel +++ b/test/e2e/suite/issuers/acme/certificate/BUILD.bazel @@ -26,11 +26,14 @@ go_library( "@com_github_onsi_ginkgo//:go_default_library", "@com_github_onsi_gomega//:go_default_library", "@io_k8s_api//core/v1:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", "@io_k8s_api//networking/v1beta1:go_default_library", "@io_k8s_apiextensions_apiserver//pkg/apis/apiextensions/v1beta1:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_apimachinery//pkg/util/intstr:go_default_library", "@io_k8s_apimachinery//pkg/util/wait:go_default_library", + "@io_k8s_client_go//util/retry:go_default_library", + "@io_k8s_utils//pointer:go_default_library", ], ) diff --git a/test/e2e/suite/issuers/acme/certificate/http01.go b/test/e2e/suite/issuers/acme/certificate/http01.go index d7371e851..1354377ea 100644 --- a/test/e2e/suite/issuers/acme/certificate/http01.go +++ b/test/e2e/suite/issuers/acme/certificate/http01.go @@ -27,10 +27,13 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" networkingv1beta1 "k8s.io/api/networking/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/retry" + "k8s.io/utils/pointer" cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1" v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" @@ -174,13 +177,22 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() { _, err = f.Helper().WaitForCertificateNotReadyUpdate(cert, 30*time.Second) Expect(err).NotTo(HaveOccurred()) - By("Getting the latest version of the Certificate") - cert, err = certClient.Get(context.TODO(), certificateName, metav1.GetOptions{}) - Expect(err).NotTo(HaveOccurred()) + err = retry.RetryOnConflict(retry.DefaultRetry, func() error { + By("Getting the latest version of the Certificate") + cert, err = certClient.Get(context.TODO(), certificateName, metav1.GetOptions{}) + if err != nil { + return err + } - By("Replacing dnsNames with a valid dns name") - cert.Spec.DNSNames = []string{e2eutil.RandomSubdomain(acmeIngressDomain)} - _, err = certClient.Update(context.TODO(), cert, metav1.UpdateOptions{}) + By("Replacing dnsNames with a valid dns name") + cert = cert.DeepCopy() + cert.Spec.DNSNames = []string{e2eutil.RandomSubdomain(acmeIngressDomain)} + _, err = certClient.Update(context.TODO(), cert, metav1.UpdateOptions{}) + if err != nil { + return err + } + return nil + }) Expect(err).NotTo(HaveOccurred()) By("Waiting for the Certificate to have the Ready=True condition") @@ -226,17 +238,29 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() { }) It("should obtain a signed certificate with a single CN from the ACME server when putting an annotation on an ingress resource", func() { - ingClient := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace.Name) + + switch { + case util.HasIngresses(f.KubeClientSet.Discovery(), networkingv1.SchemeGroupVersion.String()): + ingClient := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace.Name) + By("Creating an Ingress with the issuer name annotation set") + _, err := ingClient.Create(context.TODO(), util.NewIngress(certificateSecretName, certificateSecretName, map[string]string{ + "cert-manager.io/issuer": issuerName, + }, acmeIngressDomain), metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + case util.HasIngresses(f.KubeClientSet.Discovery(), networkingv1beta1.SchemeGroupVersion.String()): + ingClient := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace.Name) + By("Creating an Ingress with the issuer name annotation set") + _, err := ingClient.Create(context.TODO(), util.NewV1Beta1Ingress(certificateSecretName, certificateSecretName, map[string]string{ + "cert-manager.io/issuer": issuerName, + }, acmeIngressDomain), metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + default: + Fail("Neither " + networkingv1.SchemeGroupVersion.String() + " nor " + networkingv1beta1.SchemeGroupVersion.String() + " were discovered in the API server") + } + certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name) - - By("Creating an Ingress with the issuer name annotation set") - _, err := ingClient.Create(context.TODO(), util.NewIngress(certificateSecretName, certificateSecretName, map[string]string{ - "cert-manager.io/issuer": issuerName, - }, acmeIngressDomain), metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - By("Waiting for Certificate to exist") - err = util.WaitForCertificateToExist(certClient, certificateSecretName, foreverTestTimeout) + err := util.WaitForCertificateToExist(certClient, certificateSecretName, foreverTestTimeout) Expect(err).NotTo(HaveOccurred()) By("Waiting for the Certificate to be issued...") @@ -287,33 +311,42 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() { // create an ingress that points at nothing, but has the TLS redirect annotation set // using the TLS secret that we just got from the self-sign - ingress := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace.Name) - _, err = ingress.Create(context.TODO(), &networkingv1beta1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: fixedIngressName, - Annotations: map[string]string{ - "nginx.ingress.kubernetes.io/force-ssl-redirect": "true", - "kubernetes.io/ingress.class": "nginx", - }, - }, - Spec: networkingv1beta1.IngressSpec{ - TLS: []networkingv1beta1.IngressTLS{ - { - Hosts: []string{acmeIngressDomain}, - SecretName: secretname, + + switch { + case util.HasIngresses(f.KubeClientSet.Discovery(), networkingv1.SchemeGroupVersion.String()): + ingress := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace.Name) + _, err = ingress.Create(context.TODO(), &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: fixedIngressName, + Annotations: map[string]string{ + "nginx.ingress.kubernetes.io/force-ssl-redirect": "true", }, }, - Rules: []networkingv1beta1.IngressRule{ - { - Host: acmeIngressDomain, - IngressRuleValue: networkingv1beta1.IngressRuleValue{ - HTTP: &networkingv1beta1.HTTPIngressRuleValue{ - Paths: []networkingv1beta1.HTTPIngressPath{ - { - Path: "/", - Backend: networkingv1beta1.IngressBackend{ - ServiceName: "doesnotexist", - ServicePort: intstr.FromInt(443), + Spec: networkingv1.IngressSpec{ + IngressClassName: pointer.StringPtr("nginx"), + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{acmeIngressDomain}, + SecretName: secretname, + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: acmeIngressDomain, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: func() *networkingv1.PathType { s := networkingv1.PathTypePrefix; return &s }(), + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "doesnotexist", + Port: networkingv1.ServiceBackendPort{ + Number: 443, + }, + }, + }, }, }, }, @@ -321,11 +354,50 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() { }, }, }, - }, - }, metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + case util.HasIngresses(f.KubeClientSet.Discovery(), networkingv1beta1.SchemeGroupVersion.String()): + ingress := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace.Name) + _, err = ingress.Create(context.TODO(), &networkingv1beta1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: fixedIngressName, + Annotations: map[string]string{ + "nginx.ingress.kubernetes.io/force-ssl-redirect": "true", + }, + }, + Spec: networkingv1beta1.IngressSpec{ + IngressClassName: pointer.StringPtr("nginx"), + TLS: []networkingv1beta1.IngressTLS{ + { + Hosts: []string{acmeIngressDomain}, + SecretName: secretname, + }, + }, + Rules: []networkingv1beta1.IngressRule{ + { + Host: acmeIngressDomain, + IngressRuleValue: networkingv1beta1.IngressRuleValue{ + HTTP: &networkingv1beta1.HTTPIngressRuleValue{ + Paths: []networkingv1beta1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1beta1.IngressBackend{ + ServiceName: "doesnotexist", + ServicePort: intstr.FromInt(443), + }, + }, + }, + }, + }, + }, + }, + }, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + default: + Fail("Neither " + networkingv1.SchemeGroupVersion.String() + " nor " + networkingv1beta1.SchemeGroupVersion.String() + " were discovered in the API server") + } - By("Creating a Certificate") // This is a special cert for the test suite, where we specify an ingress rather than a // class By("Creating a Certificate") diff --git a/test/e2e/util/BUILD.bazel b/test/e2e/util/BUILD.bazel index e0dd9afb0..e66919961 100644 --- a/test/e2e/util/BUILD.bazel +++ b/test/e2e/util/BUILD.bazel @@ -18,12 +18,14 @@ go_library( "//pkg/util/pki:go_default_library", "//test/e2e/framework/log:go_default_library", "@io_k8s_api//core/v1:go_default_library", + "@io_k8s_api//networking/v1:go_default_library", "@io_k8s_api//networking/v1beta1:go_default_library", "@io_k8s_apiextensions_apiserver//pkg/client/clientset/clientset/typed/apiextensions/v1beta1:go_default_library", "@io_k8s_apimachinery//pkg/api/errors:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_apimachinery//pkg/util/intstr:go_default_library", "@io_k8s_apimachinery//pkg/util/wait:go_default_library", + "@io_k8s_client_go//discovery:go_default_library", "@io_k8s_client_go//kubernetes:go_default_library", "@io_k8s_sigs_gateway_api//apis/v1alpha1:go_default_library", ], diff --git a/test/e2e/util/util.go b/test/e2e/util/util.go index 731b9e39a..9b4cd9014 100644 --- a/test/e2e/util/util.go +++ b/test/e2e/util/util.go @@ -30,12 +30,14 @@ import ( "time" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" networkingv1beta1 "k8s.io/api/networking/v1beta1" apiextcs "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" "sigs.k8s.io/gateway-api/apis/v1alpha1" gwapiv1alpha1 "sigs.k8s.io/gateway-api/apis/v1alpha1" @@ -417,7 +419,47 @@ func NewCertManagerVaultCertificate(name, secretName, issuerName string, issuerK } } -func NewIngress(name, secretName string, annotations map[string]string, dnsNames ...string) *networkingv1beta1.Ingress { +func NewIngress(name, secretName string, annotations map[string]string, dnsNames ...string) *networkingv1.Ingress { + return &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Annotations: annotations, + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: dnsNames, + SecretName: secretName, + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: dnsNames[0], + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: pathTypePrefix(), + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "somesvc", + Port: networkingv1.ServiceBackendPort{ + Number: 80, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func NewV1Beta1Ingress(name, secretName string, annotations map[string]string, dnsNames ...string) *networkingv1beta1.Ingress { return &networkingv1beta1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -439,7 +481,7 @@ func NewIngress(name, secretName string, annotations map[string]string, dnsNames { Path: "/", Backend: networkingv1beta1.IngressBackend{ - ServiceName: "dummy-service", + ServiceName: "somesvc", ServicePort: intstr.FromInt(80), }, }, @@ -452,6 +494,11 @@ func NewIngress(name, secretName string, annotations map[string]string, dnsNames } } +func pathTypePrefix() *networkingv1.PathType { + p := networkingv1.PathTypePrefix + return &p +} + func NewGateway(gatewayName, ns, secretName string, annotations map[string]string, dnsNames ...string) (*gwapiv1alpha1.Gateway, *gwapiv1alpha1.HTTPRoute) { var hostnames []gwapiv1alpha1.Hostname for _, dnsName := range dnsNames { @@ -526,3 +573,18 @@ func ptrPort(port int32) *gwapiv1alpha1.PortNumber { p := gwapiv1alpha1.PortNumber(port) return &p } + +// HasIngresses lets you know if an API exists in the discovery API +// calling this function always performs a request to the API server. +func HasIngresses(d discovery.DiscoveryInterface, GroupVersion string) bool { + resourceList, err := d.ServerResourcesForGroupVersion(GroupVersion) + if err != nil { + return false + } + for _, r := range resourceList.APIResources { + if r.Kind == "Ingress" { + return true + } + } + return false +} diff --git a/pkg/internal/apis/certmanager/validation/plugins/fake/BUILD.bazel b/test/unit/discovery/BUILD.bazel similarity index 88% rename from pkg/internal/apis/certmanager/validation/plugins/fake/BUILD.bazel rename to test/unit/discovery/BUILD.bazel index 1e05b48e2..ad0711406 100644 --- a/pkg/internal/apis/certmanager/validation/plugins/fake/BUILD.bazel +++ b/test/unit/discovery/BUILD.bazel @@ -3,7 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", srcs = ["discovery.go"], - importpath = "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager/validation/plugins/fake", + importpath = "github.com/jetstack/cert-manager/test/unit/discovery", visibility = ["//pkg:__subpackages__"], deps = [ "@com_github_googleapis_gnostic//openapiv2:go_default_library", diff --git a/pkg/internal/apis/certmanager/validation/plugins/fake/discovery.go b/test/unit/discovery/discovery.go similarity index 99% rename from pkg/internal/apis/certmanager/validation/plugins/fake/discovery.go rename to test/unit/discovery/discovery.go index 1993d37b7..cb72238c2 100644 --- a/pkg/internal/apis/certmanager/validation/plugins/fake/discovery.go +++ b/test/unit/discovery/discovery.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fake +package discovery import ( openapi_v2 "github.com/googleapis/gnostic/openapiv2"