diff --git a/pkg/apis/certmanager/v1alpha1/types.go b/pkg/apis/certmanager/v1alpha1/types.go index be10511c5..f3cd08aa1 100644 --- a/pkg/apis/certmanager/v1alpha1/types.go +++ b/pkg/apis/certmanager/v1alpha1/types.go @@ -29,6 +29,21 @@ const ( // Annotation names for CertificateRequests const ( CRPrivateKeyAnnotationKey = "certmanager.k8s.io/private-key-secret-name" + + // If this annotation is specified on a Certificate or Order resource when + // using the HTTP01 solver type, the ingress.name field of the HTTP01 + // solver's configuration will be set to the value given here. + // This is especially useful for users of Ingress controllers that maintain + // a 1:1 mapping between endpoint IP and Ingress resource. + ACMECertificateHTTP01IngressNameOverride = "acme.certmanager.k8s.io/http01-override-ingress-name" + + // If this annotation is specified on a Certificate or Order resource when + // using the HTTP01 solver type, the ingress.class field of the HTTP01 + // solver's configuration will be set to the value given here. + // This is especially useful for users deploying many different ingress + // classes into a single cluster that want to be able to re-use a single + // solver for each ingress class. + ACMECertificateHTTP01IngressClassOverride = "acme.certmanager.k8s.io/http01-override-ingress-class" ) // ConditionStatus represents a condition's status. diff --git a/pkg/controller/acmeorders/BUILD.bazel b/pkg/controller/acmeorders/BUILD.bazel index bbef59eb0..bb0d262f3 100644 --- a/pkg/controller/acmeorders/BUILD.bazel +++ b/pkg/controller/acmeorders/BUILD.bazel @@ -60,7 +60,10 @@ filegroup( go_test( name = "go_default_test", - srcs = ["sync_test.go"], + srcs = [ + "sync_test.go", + "util_test.go", + ], embed = [":go_default_library"], deps = [ "//pkg/acme/client:go_default_library", @@ -74,9 +77,10 @@ go_test( "//vendor/github.com/kr/pretty:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library", "//vendor/k8s.io/component-base/featuregate/testing:go_default_library", "//vendor/k8s.io/utils/clock/testing:go_default_library", + "//vendor/k8s.io/utils/diff:go_default_library", + "//vendor/k8s.io/utils/pointer:go_default_library", ], ) diff --git a/pkg/controller/acmeorders/sync_test.go b/pkg/controller/acmeorders/sync_test.go index 26aa26128..256faba08 100644 --- a/pkg/controller/acmeorders/sync_test.go +++ b/pkg/controller/acmeorders/sync_test.go @@ -18,14 +18,11 @@ package acmeorders import ( "context" - "reflect" "testing" "time" - "github.com/kr/pretty" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/diff" coretesting "k8s.io/client-go/testing" featuregatetesting "k8s.io/component-base/featuregate/testing" fakeclock "k8s.io/utils/clock/testing" @@ -410,1250 +407,3 @@ func runTest(t *testing.T, test testT) { test.builder.CheckAndFinish(err) } - -//func (c *controller) challengeSpecForAuthorization(ctx context.Context, cl acmecl.Interface, issuer cmapi.GenericIssuer, o *cmapi.Order, authz *acmeapi.Authorization) (*cmapi.ChallengeSpec, error) { -func TestChallengeSpecForAuthorization(t *testing.T) { - // a reusable and very simple ACME client that only implements the HTTP01 - // and DNS01 challenge response/record methods - basicACMEClient := &acmecl.FakeACME{ - FakeHTTP01ChallengeResponse: func(string) (string, error) { - return "http01", nil - }, - FakeDNS01ChallengeRecord: func(string) (string, error) { - return "dns01", nil - }, - } - // define some reusable solvers that are used in multiple unit tests - emptySelectorSolverHTTP01 := v1alpha1.ACMEChallengeSolver{ - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "empty-selector-solver", - }, - }, - } - emptySelectorSolverDNS01 := v1alpha1.ACMEChallengeSolver{ - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "test-cloudflare-email", - }, - }, - } - nonMatchingSelectorSolver := v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "does-not-exist", - "does-not": "match", - }, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "non-matching-selector-solver", - }, - }, - } - exampleComDNSNameSelectorSolver := v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSNames: []string{"example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dns-name-selector-solver", - }, - }, - } - // define ACME challenges that are used during tests - acmeChallengeHTTP01 := &v1alpha1.ACMEChallenge{ - Type: "http-01", - Token: "http-01-token", - } - acmeChallengeDNS01 := &v1alpha1.ACMEChallenge{ - Type: "dns-01", - Token: "dns-01-token", - } - - tests := map[string]struct { - acmeClient acmecl.Interface - issuer v1alpha1.GenericIssuer - order *v1alpha1.Order - authz *v1alpha1.ACMEAuthorization - - expectedChallengeSpec *v1alpha1.ChallengeSpec - expectedError bool - }{ - "should use configured default solver when no others are present": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{emptySelectorSolverHTTP01}, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &emptySelectorSolverHTTP01, - }, - }, - "should use configured default solver when no others are present but selector is non-nil": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{}, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "empty-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{}, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "empty-selector-solver", - }, - }, - }, - }, - }, - "should use configured default solver when others do not match": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - emptySelectorSolverHTTP01, - nonMatchingSelectorSolver, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &emptySelectorSolverHTTP01, - }, - }, - "should use DNS01 solver over HTTP01 if challenge is of type DNS01": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - emptySelectorSolverHTTP01, - emptySelectorSolverDNS01, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "dns-01", - DNSName: "example.com", - Token: acmeChallengeDNS01.Token, - Key: "dns01", - Solver: &emptySelectorSolverDNS01, - }, - }, - "should return an error if none match": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - nonMatchingSelectorSolver, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedError: true, - }, - "uses correct solver when selector explicitly names dnsName": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - emptySelectorSolverHTTP01, - exampleComDNSNameSelectorSolver, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &exampleComDNSNameSelectorSolver, - }, - }, - "uses default solver if dnsName does not match": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - emptySelectorSolverHTTP01, - exampleComDNSNameSelectorSolver, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"notexample.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "notexample.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "notexample.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &emptySelectorSolverHTTP01, - }, - }, - "if two solvers specify the same dnsName, the one with the most labels should be chosen": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - exampleComDNSNameSelectorSolver, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - }, - DNSNames: []string{"example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dns-name-labels-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "label": "exists", - }, - }, - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - }, - DNSNames: []string{"example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dns-name-labels-selector-solver", - }, - }, - }, - }, - }, - "if one solver matches with dnsNames, and the other solver matches with labels, the dnsName solver should be chosen": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - exampleComDNSNameSelectorSolver, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - }, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-labels-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "label": "exists", - }, - }, - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &exampleComDNSNameSelectorSolver, - }, - }, - // identical to the test above, but the solvers are listed in reverse - // order to ensure that this behaviour isn't just incidental - "if one solver matches with dnsNames, and the other solver matches with labels, the dnsName solver should be chosen (solvers listed in reverse order)": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - }, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-labels-selector-solver", - }, - }, - }, - exampleComDNSNameSelectorSolver, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "label": "exists", - }, - }, - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &exampleComDNSNameSelectorSolver, - }, - }, - "if one solver matches with dnsNames, and the other solver matches with 2 labels, the dnsName solver should be chosen": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - exampleComDNSNameSelectorSolver, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - "another": "label", - }, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-labels-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "label": "exists", - "another": "label", - }, - }, - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &exampleComDNSNameSelectorSolver, - }, - }, - "should choose the solver with the most labels matching if multiple match": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - }, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-labels-selector-solver", - }, - }, - }, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - "another": "matches", - }, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-multiple-labels-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "label": "exists", - "another": "matches", - }, - }, - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - "another": "matches", - }, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-multiple-labels-selector-solver", - }, - }, - }, - }, - }, - "should match wildcard dnsName solver if authorization has Wildcard=true": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - emptySelectorSolverDNS01, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSNames: []string{"*.example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "example-com-wc-dnsname-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"*.example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Wildcard: true, - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "dns-01", - DNSName: "example.com", - Wildcard: true, - Token: acmeChallengeDNS01.Token, - Key: "dns01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSNames: []string{"*.example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "example-com-wc-dnsname-selector-solver", - }, - }, - }, - }, - }, - "dnsName selectors should take precedence over dnsZone selectors": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - exampleComDNSNameSelectorSolver, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "com-dnszone-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &exampleComDNSNameSelectorSolver, - }, - }, - "dnsName selectors should take precedence over dnsZone selectors (reversed order)": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "com-dnszone-selector-solver", - }, - }, - }, - exampleComDNSNameSelectorSolver, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &exampleComDNSNameSelectorSolver, - }, - }, - "should allow matching with dnsZones": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - emptySelectorSolverDNS01, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "example-com-dnszone-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"www.example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "www.example.com", - Wildcard: true, - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "dns-01", - DNSName: "www.example.com", - Wildcard: true, - Token: acmeChallengeDNS01.Token, - Key: "dns01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "example-com-dnszone-selector-solver", - }, - }, - }, - }, - }, - "most specific dnsZone should be selected if multiple match": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "example-com-dnszone-selector-solver", - }, - }, - }, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"prod.example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "prod-example-com-dnszone-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"www.prod.example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "www.prod.example.com", - Wildcard: true, - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "dns-01", - DNSName: "www.prod.example.com", - Wildcard: true, - Token: acmeChallengeDNS01.Token, - Key: "dns01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"prod.example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "prod-example-com-dnszone-selector-solver", - }, - }, - }, - }, - }, - "most specific dnsZone should be selected if multiple match (reversed)": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"prod.example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "prod-example-com-dnszone-selector-solver", - }, - }, - }, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "example-com-dnszone-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"www.prod.example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "www.prod.example.com", - Wildcard: true, - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "dns-01", - DNSName: "www.prod.example.com", - Wildcard: true, - Token: acmeChallengeDNS01.Token, - Key: "dns01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"prod.example.com"}, - }, - DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ - Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ - Email: "prod-example-com-dnszone-selector-solver", - }, - }, - }, - }, - }, - "if two solvers specify the same dnsZone, the one with the most labels should be chosen": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnszone-selector-solver", - }, - }, - }, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - }, - DNSZones: []string{"example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnszone-labels-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "label": "exists", - }, - }, - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"www.example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "www.example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "www.example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - MatchLabels: map[string]string{ - "label": "exists", - }, - DNSZones: []string{"example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnszone-labels-selector-solver", - }, - }, - }, - }, - }, - "if both solvers match dnsNames, and one also matches dnsZones, choose the one that matches dnsZones": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSNames: []string{"www.example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnsname-selector-solver", - }, - }, - }, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - DNSNames: []string{"www.example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnsname-dnszone-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"www.example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "www.example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "www.example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - DNSNames: []string{"www.example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnsname-dnszone-selector-solver", - }, - }, - }, - }, - }, - "if both solvers match dnsNames, and one also matches dnsZones, choose the one that matches dnsZones (reversed)": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - DNSNames: []string{"www.example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnsname-dnszone-selector-solver", - }, - }, - }, - { - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSNames: []string{"www.example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnsname-selector-solver", - }, - }, - }, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"www.example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "www.example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "www.example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &v1alpha1.ACMEChallengeSolver{ - Selector: &v1alpha1.CertificateDNSNameSelector{ - DNSZones: []string{"example.com"}, - DNSNames: []string{"www.example.com"}, - }, - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - Name: "example-com-dnsname-dnszone-selector-solver", - }, - }, - }, - }, - }, - "uses correct solver when selector explicitly names dnsName (reversed)": { - acmeClient: basicACMEClient, - issuer: &v1alpha1.Issuer{ - Spec: v1alpha1.IssuerSpec{ - IssuerConfig: v1alpha1.IssuerConfig{ - ACME: &v1alpha1.ACMEIssuer{ - Solvers: []v1alpha1.ACMEChallengeSolver{ - exampleComDNSNameSelectorSolver, - emptySelectorSolverHTTP01, - }, - }, - }, - }, - }, - order: &v1alpha1.Order{ - Spec: v1alpha1.OrderSpec{ - DNSNames: []string{"example.com"}, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, - }, - expectedChallengeSpec: &v1alpha1.ChallengeSpec{ - Type: "http-01", - DNSName: "example.com", - Token: acmeChallengeHTTP01.Token, - Key: "http01", - Solver: &exampleComDNSNameSelectorSolver, - }, - }, - } - for name, test := range tests { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - cs, err := challengeSpecForAuthorization(ctx, test.acmeClient, test.issuer, test.order, *test.authz) - if err != nil && !test.expectedError { - t.Errorf("expected to not get an error, but got: %v", err) - t.Fail() - } - if err == nil && test.expectedError { - t.Errorf("expected to get an error, but got none") - } - if !reflect.DeepEqual(cs, test.expectedChallengeSpec) { - t.Errorf("returned challenge spec was not as expected: %v", pretty.Diff(test.expectedChallengeSpec, cs)) - } - }) - } -} - -func TestSolverConfigurationForAuthorization(t *testing.T) { - type testT struct { - cfg []v1alpha1.DomainSolverConfig - authz *v1alpha1.ACMEAuthorization - expectedCfg *v1alpha1.SolverConfig - expectedErr bool - } - tests := map[string]testT{ - "correctly selects normal domain": { - cfg: []v1alpha1.DomainSolverConfig{ - { - Domains: []string{"example.com"}, - SolverConfig: v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "correctdns", - }, - }, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - }, - expectedCfg: &v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "correctdns", - }, - }, - }, - "correctly selects normal domain with multiple domains configured": { - cfg: []v1alpha1.DomainSolverConfig{ - { - Domains: []string{"notexample.com", "example.com"}, - SolverConfig: v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "correctdns", - }, - }, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - }, - expectedCfg: &v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "correctdns", - }, - }, - }, - "correctly selects normal domain with multiple domains configured separately": { - cfg: []v1alpha1.DomainSolverConfig{ - { - Domains: []string{"example.com"}, - SolverConfig: v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "correctdns", - }, - }, - }, - { - Domains: []string{"notexample.com"}, - SolverConfig: v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "incorrectdns", - }, - }, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - }, - expectedCfg: &v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "correctdns", - }, - }, - }, - "correctly selects configuration for wildcard domain": { - cfg: []v1alpha1.DomainSolverConfig{ - { - Domains: []string{"example.com"}, - SolverConfig: v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "incorrectdns", - }, - }, - }, - { - Domains: []string{"*.example.com"}, - SolverConfig: v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "correctdns", - }, - }, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Wildcard: true, - Identifier: "example.com", - }, - expectedCfg: &v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "correctdns", - }, - }, - }, - "returns an error when configuration for the domain is not found": { - cfg: []v1alpha1.DomainSolverConfig{ - { - Domains: []string{"notexample.com"}, - SolverConfig: v1alpha1.SolverConfig{ - DNS01: &v1alpha1.DNS01SolverConfig{ - Provider: "incorrectdns", - }, - }, - }, - }, - authz: &v1alpha1.ACMEAuthorization{ - Identifier: "example.com", - }, - expectedErr: true, - }, - } - for n, test := range tests { - t.Run(n, func(t *testing.T) { - actualCfg, err := solverConfigurationForAuthorization(test.cfg, test.authz) - if err != nil && !test.expectedErr { - t.Errorf("Expected to return non-nil error, but got %v", err) - return - } - if err == nil && test.expectedErr { - t.Errorf("Expected error, but got none") - return - } - if !reflect.DeepEqual(test.expectedCfg, actualCfg) { - t.Errorf("Expected did not equal actual: %v", diff.ObjectDiff(test.expectedCfg, actualCfg)) - } - }) - } -} diff --git a/pkg/controller/acmeorders/util.go b/pkg/controller/acmeorders/util.go index d608eb7d4..dda03e954 100644 --- a/pkg/controller/acmeorders/util.go +++ b/pkg/controller/acmeorders/util.go @@ -280,7 +280,13 @@ func challengeSpecForAuthorization(ctx context.Context, cl acmecl.Interface, iss return nil, err } - // 4. construct Challenge resource with spec.solver field set + // 4. handle overriding the HTTP01 ingress class and name fields using the + // ACMECertificateHTTP01IngressNameOverride & Class annotations + if err := applyIngressParameterAnnotationOverrides(o, selectedSolver); err != nil { + return nil, err + } + + // 5. construct Challenge resource with spec.solver field set return &cmapi.ChallengeSpec{ AuthzURL: authz.URL, Type: selectedChallenge.Type, @@ -294,6 +300,32 @@ func challengeSpecForAuthorization(ctx context.Context, cl acmecl.Interface, iss }, nil } +func applyIngressParameterAnnotationOverrides(o *cmapi.Order, s *cmapi.ACMEChallengeSolver) error { + if s.HTTP01 == nil || s.HTTP01.Ingress == nil || o.Annotations == nil { + return nil + } + + manualIngressName, hasManualIngressName := o.Annotations[cmapi.ACMECertificateHTTP01IngressNameOverride] + manualIngressClass, hasManualIngressClass := o.Annotations[cmapi.ACMECertificateHTTP01IngressClassOverride] + // don't allow both override annotations to be specified at once + if hasManualIngressName && hasManualIngressClass { + return fmt.Errorf("both ingress name and ingress class overrides specified - only one may be specified at a time") + } + // if an override annotation is specified, clear out the existing solver + // config + if hasManualIngressClass || hasManualIngressName { + s.HTTP01.Ingress.Class = nil + s.HTTP01.Ingress.Name = "" + } + if hasManualIngressName { + s.HTTP01.Ingress.Name = manualIngressName + } + if hasManualIngressClass { + s.HTTP01.Ingress.Class = &manualIngressClass + } + return nil +} + func keyForChallenge(cl acmecl.Interface, challenge *cmapi.ACMEChallenge) (string, error) { var err error switch challenge.Type { diff --git a/pkg/controller/acmeorders/util_test.go b/pkg/controller/acmeorders/util_test.go new file mode 100644 index 000000000..ef0ea20f1 --- /dev/null +++ b/pkg/controller/acmeorders/util_test.go @@ -0,0 +1,1416 @@ +/* +Copyright 2019 The Jetstack cert-manager contributors. + +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 acmeorders + +import ( + "context" + "reflect" + "testing" + + "github.com/kr/pretty" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/diff" + "k8s.io/utils/pointer" + + acmecl "github.com/jetstack/cert-manager/pkg/acme/client" + "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1" +) + +func TestChallengeSpecForAuthorization(t *testing.T) { + // a reusable and very simple ACME client that only implements the HTTP01 + // and DNS01 challenge response/record methods + basicACMEClient := &acmecl.FakeACME{ + FakeHTTP01ChallengeResponse: func(string) (string, error) { + return "http01", nil + }, + FakeDNS01ChallengeRecord: func(string) (string, error) { + return "dns01", nil + }, + } + // define some reusable solvers that are used in multiple unit tests + emptySelectorSolverHTTP01 := v1alpha1.ACMEChallengeSolver{ + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "empty-selector-solver", + }, + }, + } + emptySelectorSolverDNS01 := v1alpha1.ACMEChallengeSolver{ + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "test-cloudflare-email", + }, + }, + } + nonMatchingSelectorSolver := v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "does-not-exist", + "does-not": "match", + }, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "non-matching-selector-solver", + }, + }, + } + exampleComDNSNameSelectorSolver := v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSNames: []string{"example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dns-name-selector-solver", + }, + }, + } + // define ACME challenges that are used during tests + acmeChallengeHTTP01 := &v1alpha1.ACMEChallenge{ + Type: "http-01", + Token: "http-01-token", + } + acmeChallengeDNS01 := &v1alpha1.ACMEChallenge{ + Type: "dns-01", + Token: "dns-01-token", + } + + tests := map[string]struct { + acmeClient acmecl.Interface + issuer v1alpha1.GenericIssuer + order *v1alpha1.Order + authz *v1alpha1.ACMEAuthorization + + expectedChallengeSpec *v1alpha1.ChallengeSpec + expectedError bool + }{ + "should override the ingress name to edit if override annotation is specified": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{emptySelectorSolverHTTP01}, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + v1alpha1.ACMECertificateHTTP01IngressNameOverride: "test-name-to-override", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &v1alpha1.ACMEChallengeSolver{ + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "test-name-to-override", + }, + }, + }, + }, + }, + "should override the ingress class to edit if override annotation is specified": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{emptySelectorSolverHTTP01}, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + v1alpha1.ACMECertificateHTTP01IngressClassOverride: "test-class-to-override", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &v1alpha1.ACMEChallengeSolver{ + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Class: pointer.StringPtr("test-class-to-override"), + }, + }, + }, + }, + }, + "should return an error if both ingress class and name override annotations are set": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{emptySelectorSolverHTTP01}, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + v1alpha1.ACMECertificateHTTP01IngressNameOverride: "test-name-to-override", + v1alpha1.ACMECertificateHTTP01IngressClassOverride: "test-class-to-override", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedError: true, + }, + "should ignore HTTP01 override annotations if DNS01 solver is chosen": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{emptySelectorSolverDNS01}, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + v1alpha1.ACMECertificateHTTP01IngressNameOverride: "test-name-to-override", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "dns-01", + DNSName: "example.com", + Token: acmeChallengeDNS01.Token, + Key: "dns01", + Solver: &emptySelectorSolverDNS01, + }, + }, + "should use configured default solver when no others are present": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{emptySelectorSolverHTTP01}, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &emptySelectorSolverHTTP01, + }, + }, + "should use configured default solver when no others are present but selector is non-nil": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{}, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "empty-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{}, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "empty-selector-solver", + }, + }, + }, + }, + }, + "should use configured default solver when others do not match": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + emptySelectorSolverHTTP01, + nonMatchingSelectorSolver, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &emptySelectorSolverHTTP01, + }, + }, + "should use DNS01 solver over HTTP01 if challenge is of type DNS01": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + emptySelectorSolverHTTP01, + emptySelectorSolverDNS01, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "dns-01", + DNSName: "example.com", + Token: acmeChallengeDNS01.Token, + Key: "dns01", + Solver: &emptySelectorSolverDNS01, + }, + }, + "should return an error if none match": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + nonMatchingSelectorSolver, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedError: true, + }, + "uses correct solver when selector explicitly names dnsName": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + emptySelectorSolverHTTP01, + exampleComDNSNameSelectorSolver, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &exampleComDNSNameSelectorSolver, + }, + }, + "uses default solver if dnsName does not match": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + emptySelectorSolverHTTP01, + exampleComDNSNameSelectorSolver, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"notexample.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "notexample.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "notexample.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &emptySelectorSolverHTTP01, + }, + }, + "if two solvers specify the same dnsName, the one with the most labels should be chosen": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + exampleComDNSNameSelectorSolver, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + }, + DNSNames: []string{"example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dns-name-labels-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "label": "exists", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + }, + DNSNames: []string{"example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dns-name-labels-selector-solver", + }, + }, + }, + }, + }, + "if one solver matches with dnsNames, and the other solver matches with labels, the dnsName solver should be chosen": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + exampleComDNSNameSelectorSolver, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + }, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-labels-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "label": "exists", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &exampleComDNSNameSelectorSolver, + }, + }, + // identical to the test above, but the solvers are listed in reverse + // order to ensure that this behaviour isn't just incidental + "if one solver matches with dnsNames, and the other solver matches with labels, the dnsName solver should be chosen (solvers listed in reverse order)": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + }, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-labels-selector-solver", + }, + }, + }, + exampleComDNSNameSelectorSolver, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "label": "exists", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &exampleComDNSNameSelectorSolver, + }, + }, + "if one solver matches with dnsNames, and the other solver matches with 2 labels, the dnsName solver should be chosen": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + exampleComDNSNameSelectorSolver, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + "another": "label", + }, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-labels-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "label": "exists", + "another": "label", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &exampleComDNSNameSelectorSolver, + }, + }, + "should choose the solver with the most labels matching if multiple match": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + }, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-labels-selector-solver", + }, + }, + }, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + "another": "matches", + }, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-multiple-labels-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "label": "exists", + "another": "matches", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + "another": "matches", + }, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-multiple-labels-selector-solver", + }, + }, + }, + }, + }, + "should match wildcard dnsName solver if authorization has Wildcard=true": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + emptySelectorSolverDNS01, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSNames: []string{"*.example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "example-com-wc-dnsname-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"*.example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Wildcard: true, + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "dns-01", + DNSName: "example.com", + Wildcard: true, + Token: acmeChallengeDNS01.Token, + Key: "dns01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSNames: []string{"*.example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "example-com-wc-dnsname-selector-solver", + }, + }, + }, + }, + }, + "dnsName selectors should take precedence over dnsZone selectors": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + exampleComDNSNameSelectorSolver, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "com-dnszone-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &exampleComDNSNameSelectorSolver, + }, + }, + "dnsName selectors should take precedence over dnsZone selectors (reversed order)": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "com-dnszone-selector-solver", + }, + }, + }, + exampleComDNSNameSelectorSolver, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &exampleComDNSNameSelectorSolver, + }, + }, + "should allow matching with dnsZones": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + emptySelectorSolverDNS01, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "example-com-dnszone-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"www.example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "www.example.com", + Wildcard: true, + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "dns-01", + DNSName: "www.example.com", + Wildcard: true, + Token: acmeChallengeDNS01.Token, + Key: "dns01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "example-com-dnszone-selector-solver", + }, + }, + }, + }, + }, + "most specific dnsZone should be selected if multiple match": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "example-com-dnszone-selector-solver", + }, + }, + }, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"prod.example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "prod-example-com-dnszone-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"www.prod.example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "www.prod.example.com", + Wildcard: true, + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "dns-01", + DNSName: "www.prod.example.com", + Wildcard: true, + Token: acmeChallengeDNS01.Token, + Key: "dns01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"prod.example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "prod-example-com-dnszone-selector-solver", + }, + }, + }, + }, + }, + "most specific dnsZone should be selected if multiple match (reversed)": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"prod.example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "prod-example-com-dnszone-selector-solver", + }, + }, + }, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "example-com-dnszone-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"www.prod.example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "www.prod.example.com", + Wildcard: true, + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeDNS01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "dns-01", + DNSName: "www.prod.example.com", + Wildcard: true, + Token: acmeChallengeDNS01.Token, + Key: "dns01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"prod.example.com"}, + }, + DNS01: &v1alpha1.ACMEChallengeSolverDNS01{ + Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{ + Email: "prod-example-com-dnszone-selector-solver", + }, + }, + }, + }, + }, + "if two solvers specify the same dnsZone, the one with the most labels should be chosen": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnszone-selector-solver", + }, + }, + }, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + }, + DNSZones: []string{"example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnszone-labels-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "label": "exists", + }, + }, + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"www.example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "www.example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "www.example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + MatchLabels: map[string]string{ + "label": "exists", + }, + DNSZones: []string{"example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnszone-labels-selector-solver", + }, + }, + }, + }, + }, + "if both solvers match dnsNames, and one also matches dnsZones, choose the one that matches dnsZones": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSNames: []string{"www.example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnsname-selector-solver", + }, + }, + }, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + DNSNames: []string{"www.example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnsname-dnszone-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"www.example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "www.example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "www.example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + DNSNames: []string{"www.example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnsname-dnszone-selector-solver", + }, + }, + }, + }, + }, + "if both solvers match dnsNames, and one also matches dnsZones, choose the one that matches dnsZones (reversed)": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + DNSNames: []string{"www.example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnsname-dnszone-selector-solver", + }, + }, + }, + { + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSNames: []string{"www.example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnsname-selector-solver", + }, + }, + }, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"www.example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "www.example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "www.example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &v1alpha1.ACMEChallengeSolver{ + Selector: &v1alpha1.CertificateDNSNameSelector{ + DNSZones: []string{"example.com"}, + DNSNames: []string{"www.example.com"}, + }, + HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ + Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ + Name: "example-com-dnsname-dnszone-selector-solver", + }, + }, + }, + }, + }, + "uses correct solver when selector explicitly names dnsName (reversed)": { + acmeClient: basicACMEClient, + issuer: &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + ACME: &v1alpha1.ACMEIssuer{ + Solvers: []v1alpha1.ACMEChallengeSolver{ + exampleComDNSNameSelectorSolver, + emptySelectorSolverHTTP01, + }, + }, + }, + }, + }, + order: &v1alpha1.Order{ + Spec: v1alpha1.OrderSpec{ + DNSNames: []string{"example.com"}, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + Challenges: []v1alpha1.ACMEChallenge{*acmeChallengeHTTP01}, + }, + expectedChallengeSpec: &v1alpha1.ChallengeSpec{ + Type: "http-01", + DNSName: "example.com", + Token: acmeChallengeHTTP01.Token, + Key: "http01", + Solver: &exampleComDNSNameSelectorSolver, + }, + }, + } + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctx := context.Background() + cs, err := challengeSpecForAuthorization(ctx, test.acmeClient, test.issuer, test.order, *test.authz) + if err != nil && !test.expectedError { + t.Errorf("expected to not get an error, but got: %v", err) + t.Fail() + } + if err == nil && test.expectedError { + t.Errorf("expected to get an error, but got none") + } + if !reflect.DeepEqual(cs, test.expectedChallengeSpec) { + t.Errorf("returned challenge spec was not as expected: %v", pretty.Diff(test.expectedChallengeSpec, cs)) + } + }) + } +} + +func TestSolverConfigurationForAuthorization(t *testing.T) { + type testT struct { + cfg []v1alpha1.DomainSolverConfig + authz *v1alpha1.ACMEAuthorization + expectedCfg *v1alpha1.SolverConfig + expectedErr bool + } + tests := map[string]testT{ + "correctly selects normal domain": { + cfg: []v1alpha1.DomainSolverConfig{ + { + Domains: []string{"example.com"}, + SolverConfig: v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "correctdns", + }, + }, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + }, + expectedCfg: &v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "correctdns", + }, + }, + }, + "correctly selects normal domain with multiple domains configured": { + cfg: []v1alpha1.DomainSolverConfig{ + { + Domains: []string{"notexample.com", "example.com"}, + SolverConfig: v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "correctdns", + }, + }, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + }, + expectedCfg: &v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "correctdns", + }, + }, + }, + "correctly selects normal domain with multiple domains configured separately": { + cfg: []v1alpha1.DomainSolverConfig{ + { + Domains: []string{"example.com"}, + SolverConfig: v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "correctdns", + }, + }, + }, + { + Domains: []string{"notexample.com"}, + SolverConfig: v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "incorrectdns", + }, + }, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + }, + expectedCfg: &v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "correctdns", + }, + }, + }, + "correctly selects configuration for wildcard domain": { + cfg: []v1alpha1.DomainSolverConfig{ + { + Domains: []string{"example.com"}, + SolverConfig: v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "incorrectdns", + }, + }, + }, + { + Domains: []string{"*.example.com"}, + SolverConfig: v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "correctdns", + }, + }, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Wildcard: true, + Identifier: "example.com", + }, + expectedCfg: &v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "correctdns", + }, + }, + }, + "returns an error when configuration for the domain is not found": { + cfg: []v1alpha1.DomainSolverConfig{ + { + Domains: []string{"notexample.com"}, + SolverConfig: v1alpha1.SolverConfig{ + DNS01: &v1alpha1.DNS01SolverConfig{ + Provider: "incorrectdns", + }, + }, + }, + }, + authz: &v1alpha1.ACMEAuthorization{ + Identifier: "example.com", + }, + expectedErr: true, + }, + } + for n, test := range tests { + t.Run(n, func(t *testing.T) { + actualCfg, err := solverConfigurationForAuthorization(test.cfg, test.authz) + if err != nil && !test.expectedErr { + t.Errorf("Expected to return non-nil error, but got %v", err) + return + } + if err == nil && test.expectedErr { + t.Errorf("Expected error, but got none") + return + } + if !reflect.DeepEqual(test.expectedCfg, actualCfg) { + t.Errorf("Expected did not equal actual: %v", diff.ObjectDiff(test.expectedCfg, actualCfg)) + } + }) + } +} diff --git a/pkg/controller/ingress-shim/sync.go b/pkg/controller/ingress-shim/sync.go index 0aeb2cb8f..354f5fd47 100644 --- a/pkg/controller/ingress-shim/sync.go +++ b/pkg/controller/ingress-shim/sync.go @@ -326,7 +326,25 @@ func (c *controller) setIssuerSpecificConfig(crt *v1alpha1.Certificate, issuer v if ingAnnotations == nil { ingAnnotations = map[string]string{} } + // for ACME issuers + editInPlaceVal, _ := ingAnnotations[editInPlaceAnnotation] + editInPlace := editInPlaceVal == "true" + if editInPlace { + if crt.Annotations == nil { + crt.Annotations = make(map[string]string) + } + crt.Annotations[v1alpha1.ACMECertificateHTTP01IngressNameOverride] = ing.Name + } + + ingressClassVal, hasIngressClassVal := ingAnnotations[acmeIssuerHTTP01IngressClassAnnotation] + if hasIngressClassVal { + if crt.Annotations == nil { + crt.Annotations = make(map[string]string) + } + crt.Annotations[v1alpha1.ACMECertificateHTTP01IngressClassOverride] = ingressClassVal + } + if issuer.GetSpec().ACME != nil { challengeType, ok := ingAnnotations[acmeIssuerChallengeTypeAnnotation] if !ok { diff --git a/pkg/controller/ingress-shim/sync_test.go b/pkg/controller/ingress-shim/sync_test.go index 8f016105f..a6c349cec 100644 --- a/pkg/controller/ingress-shim/sync_test.go +++ b/pkg/controller/ingress-shim/sync_test.go @@ -148,6 +148,9 @@ func TestSync(t *testing.T) { Labels: map[string]string{ "my-test-label": "should be copied", }, + Annotations: map[string]string{ + v1alpha1.ACMECertificateHTTP01IngressNameOverride: "ingress-name", + }, OwnerReferences: buildOwnerReferences("ingress-name", gen.DefaultTestNamespace), }, Spec: v1alpha1.CertificateSpec{ @@ -173,6 +176,56 @@ 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), + Ingress: &extv1beta1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ingress-name", + Namespace: gen.DefaultTestNamespace, + Labels: map[string]string{ + "my-test-label": "should be copied", + }, + Annotations: map[string]string{ + clusterIssuerNameAnnotation: "issuer-name", + editInPlaceAnnotation: "true", + }, + UID: types.UID("ingress-name"), + }, + Spec: extv1beta1.IngressSpec{ + TLS: []extv1beta1.IngressTLS{ + { + Hosts: []string{"example.com", "www.example.com"}, + SecretName: "example-com-tls", + }, + }, + }, + }, + ClusterIssuerLister: []runtime.Object{acmeClusterIssuer}, + ExpectedCreate: []*v1alpha1.Certificate{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "example-com-tls", + Namespace: gen.DefaultTestNamespace, + Labels: map[string]string{ + "my-test-label": "should be copied", + }, + Annotations: map[string]string{ + v1alpha1.ACMECertificateHTTP01IngressNameOverride: "ingress-name", + }, + OwnerReferences: buildOwnerReferences("ingress-name", gen.DefaultTestNamespace), + }, + Spec: v1alpha1.CertificateSpec{ + DNSNames: []string{"example.com", "www.example.com"}, + SecretName: "example-com-tls", + IssuerRef: v1alpha1.ObjectReference{ + Name: "issuer-name", + Kind: "ClusterIssuer", + }, + }, + }, + }, + }, { 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, @@ -309,6 +362,9 @@ func TestSync(t *testing.T) { Name: "example-com-tls", Namespace: gen.DefaultTestNamespace, OwnerReferences: buildOwnerReferences("ingress-name", gen.DefaultTestNamespace), + Annotations: map[string]string{ + v1alpha1.ACMECertificateHTTP01IngressClassOverride: "cert-ing", + }, }, Spec: v1alpha1.CertificateSpec{ DNSNames: []string{"example.com", "www.example.com"},