diff --git a/deploy/manifests/00-crds.yaml b/deploy/manifests/00-crds.yaml
index db9079aa3..7091855be 100644
--- a/deploy/manifests/00-crds.yaml
+++ b/deploy/manifests/00-crds.yaml
@@ -428,15 +428,32 @@ spec:
resource that should be solved using this challenge solver.
properties:
dnsNames:
- description: List of DNSNames that can be used to further refine
- the domains that this solver applies to.
+ description: List of DNSNames that this solver will be used
+ to solve. If specified and a match is found, a dnsNames selector
+ will take precedence over a dnsZones selector. If multiple
+ solvers match with the same dnsNames value, the solver with
+ the most matching labels in matchLabels will be selected.
+ If neither has more matches, the solver defined earlier in
+ the list will be selected.
+ items:
+ type: string
+ type: array
+ dnsZones:
+ description: List of DNSZones that this solver will be used
+ to solve. The most specific DNS zone match specified here
+ will take precedence over other DNS zone matches, so a solver
+ specifying sys.example.com will be selected over one specifying
+ example.com for the domain www.sys.example.com. If multiple
+ solvers match with the same dnsZones value, the solver with
+ the most matching labels in matchLabels will be selected.
+ If neither has more matches, the solver defined earlier in
+ the list will be selected.
items:
type: string
type: array
matchLabels:
- description: 'A label selector that is used to refine the set
- of certificate''s that this challenge solver will apply to.
- TODO: use kubernetes standard types for matchLabels'
+ description: A label selector that is used to refine the set
+ of certificate's that this challenge solver will apply to.
type: object
type: object
type: object
@@ -583,16 +600,33 @@ spec:
resource that should be solved using this challenge solver.
properties:
dnsNames:
- description: List of DNSNames that can be used to further
- refine the domains that this solver applies to.
+ description: List of DNSNames that this solver will be
+ used to solve. If specified and a match is found, a
+ dnsNames selector will take precedence over a dnsZones
+ selector. If multiple solvers match with the same dnsNames
+ value, the solver with the most matching labels in matchLabels
+ will be selected. If neither has more matches, the solver
+ defined earlier in the list will be selected.
+ items:
+ type: string
+ type: array
+ dnsZones:
+ description: List of DNSZones that this solver will be
+ used to solve. The most specific DNS zone match specified
+ here will take precedence over other DNS zone matches,
+ so a solver specifying sys.example.com will be selected
+ over one specifying example.com for the domain www.sys.example.com.
+ If multiple solvers match with the same dnsZones value,
+ the solver with the most matching labels in matchLabels
+ will be selected. If neither has more matches, the solver
+ defined earlier in the list will be selected.
items:
type: string
type: array
matchLabels:
- description: 'A label selector that is used to refine
- the set of certificate''s that this challenge solver
- will apply to. TODO: use kubernetes standard types for
- matchLabels'
+ description: A label selector that is used to refine the
+ set of certificate's that this challenge solver will
+ apply to.
type: object
type: object
type: object
@@ -867,16 +901,33 @@ spec:
resource that should be solved using this challenge solver.
properties:
dnsNames:
- description: List of DNSNames that can be used to further
- refine the domains that this solver applies to.
+ description: List of DNSNames that this solver will be
+ used to solve. If specified and a match is found, a
+ dnsNames selector will take precedence over a dnsZones
+ selector. If multiple solvers match with the same dnsNames
+ value, the solver with the most matching labels in matchLabels
+ will be selected. If neither has more matches, the solver
+ defined earlier in the list will be selected.
+ items:
+ type: string
+ type: array
+ dnsZones:
+ description: List of DNSZones that this solver will be
+ used to solve. The most specific DNS zone match specified
+ here will take precedence over other DNS zone matches,
+ so a solver specifying sys.example.com will be selected
+ over one specifying example.com for the domain www.sys.example.com.
+ If multiple solvers match with the same dnsZones value,
+ the solver with the most matching labels in matchLabels
+ will be selected. If neither has more matches, the solver
+ defined earlier in the list will be selected.
items:
type: string
type: array
matchLabels:
- description: 'A label selector that is used to refine
- the set of certificate''s that this challenge solver
- will apply to. TODO: use kubernetes standard types for
- matchLabels'
+ description: A label selector that is used to refine the
+ set of certificate's that this challenge solver will
+ apply to.
type: object
type: object
type: object
@@ -1253,16 +1304,33 @@ spec:
resource that should be solved using this challenge solver.
properties:
dnsNames:
- description: List of DNSNames that can be used to further
- refine the domains that this solver applies to.
+ description: List of DNSNames that this solver will be
+ used to solve. If specified and a match is found, a
+ dnsNames selector will take precedence over a dnsZones
+ selector. If multiple solvers match with the same dnsNames
+ value, the solver with the most matching labels in matchLabels
+ will be selected. If neither has more matches, the solver
+ defined earlier in the list will be selected.
+ items:
+ type: string
+ type: array
+ dnsZones:
+ description: List of DNSZones that this solver will be
+ used to solve. The most specific DNS zone match specified
+ here will take precedence over other DNS zone matches,
+ so a solver specifying sys.example.com will be selected
+ over one specifying example.com for the domain www.sys.example.com.
+ If multiple solvers match with the same dnsZones value,
+ the solver with the most matching labels in matchLabels
+ will be selected. If neither has more matches, the solver
+ defined earlier in the list will be selected.
items:
type: string
type: array
matchLabels:
- description: 'A label selector that is used to refine
- the set of certificate''s that this challenge solver
- will apply to. TODO: use kubernetes standard types for
- matchLabels'
+ description: A label selector that is used to refine the
+ set of certificate's that this challenge solver will
+ apply to.
type: object
type: object
type: object
diff --git a/docs/generated/reference/output/reference/api-docs/index.html b/docs/generated/reference/output/reference/api-docs/index.html
index 0165a3aeb..efb0fad45 100755
--- a/docs/generated/reference/output/reference/api-docs/index.html
+++ b/docs/generated/reference/output/reference/api-docs/index.html
@@ -1557,7 +1557,11 @@ Appears In:
dnsNames string array |
-List of DNSNames that can be used to further refine the domains that this solver applies to. |
+List of DNSNames that this solver will be used to solve. If specified and a match is found, a dnsNames selector will take precedence over a dnsZones selector. If multiple solvers match with the same dnsNames value, the solver with the most matching labels in matchLabels will be selected. If neither has more matches, the solver defined earlier in the list will be selected. |
+
+
+dnsZones string array |
+List of DNSZones that this solver will be used to solve. The most specific DNS zone match specified here will take precedence over other DNS zone matches, so a solver specifying sys.example.com will be selected over one specifying example.com for the domain www.sys.example.com. If multiple solvers match with the same dnsZones value, the solver with the most matching labels in matchLabels will be selected. If neither has more matches, the solver defined earlier in the list will be selected. |
matchLabels object |
diff --git a/pkg/apis/certmanager/v1alpha1/types_issuer.go b/pkg/apis/certmanager/v1alpha1/types_issuer.go
index b4773a459..7ee8d6f97 100644
--- a/pkg/apis/certmanager/v1alpha1/types_issuer.go
+++ b/pkg/apis/certmanager/v1alpha1/types_issuer.go
@@ -238,14 +238,30 @@ type ACMEChallengeSolver struct {
type CertificateDNSNameSelector struct {
// A label selector that is used to refine the set of certificate's that
// this challenge solver will apply to.
- // TODO: use kubernetes standard types for matchLabels
// +optional
MatchLabels map[string]string `json:"matchLabels,omitempty"`
- // List of DNSNames that can be used to further refine the domains that
- // this solver applies to.
+ // List of DNSNames that this solver will be used to solve.
+ // If specified and a match is found, a dnsNames selector will take
+ // precedence over a dnsZones selector.
+ // If multiple solvers match with the same dnsNames value, the solver
+ // with the most matching labels in matchLabels will be selected.
+ // If neither has more matches, the solver defined earlier in the list
+ // will be selected.
// +optional
DNSNames []string `json:"dnsNames,omitempty"`
+
+ // List of DNSZones that this solver will be used to solve.
+ // The most specific DNS zone match specified here will take precedence
+ // over other DNS zone matches, so a solver specifying sys.example.com
+ // will be selected over one specifying example.com for the domain
+ // www.sys.example.com.
+ // If multiple solvers match with the same dnsZones value, the solver
+ // with the most matching labels in matchLabels will be selected.
+ // If neither has more matches, the solver defined earlier in the list
+ // will be selected.
+ // +optional
+ DNSZones []string `json:"dnsZones,omitempty"`
}
// ACMEChallengeSolverHTTP01 contains configuration detailing how to solve
diff --git a/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go
index 72b146fc5..67bd3006a 100644
--- a/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go
@@ -593,6 +593,11 @@ func (in *CertificateDNSNameSelector) DeepCopyInto(out *CertificateDNSNameSelect
*out = make([]string, len(*in))
copy(*out, *in)
}
+ if in.DNSZones != nil {
+ in, out := &in.DNSZones, &out.DNSZones
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
return
}
diff --git a/pkg/controller/acmeorders/BUILD.bazel b/pkg/controller/acmeorders/BUILD.bazel
index 2c80a5d58..0751e5203 100644
--- a/pkg/controller/acmeorders/BUILD.bazel
+++ b/pkg/controller/acmeorders/BUILD.bazel
@@ -16,6 +16,7 @@ go_library(
"//pkg/client/clientset/versioned:go_default_library",
"//pkg/client/listers/certmanager/v1alpha1:go_default_library",
"//pkg/controller:go_default_library",
+ "//pkg/controller/acmeorders/selectors:go_default_library",
"//pkg/issuer:go_default_library",
"//pkg/logs:go_default_library",
"//pkg/metrics:go_default_library",
@@ -49,6 +50,7 @@ go_test(
"//pkg/apis/certmanager/v1alpha1:go_default_library",
"//pkg/controller/test:go_default_library",
"//third_party/crypto/acme:go_default_library",
+ "//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",
@@ -66,7 +68,10 @@ filegroup(
filegroup(
name = "all-srcs",
- srcs = [":package-srcs"],
+ srcs = [
+ ":package-srcs",
+ "//pkg/controller/acmeorders/selectors:all-srcs",
+ ],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
diff --git a/pkg/controller/acmeorders/selectors/BUILD.bazel b/pkg/controller/acmeorders/selectors/BUILD.bazel
new file mode 100644
index 000000000..b72d6f571
--- /dev/null
+++ b/pkg/controller/acmeorders/selectors/BUILD.bazel
@@ -0,0 +1,32 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = [
+ "dns_names.go",
+ "dns_zones.go",
+ "labels.go",
+ "selector.go",
+ ],
+ importpath = "github.com/jetstack/cert-manager/pkg/controller/acmeorders/selectors",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//pkg/apis/certmanager/v1alpha1:go_default_library",
+ "//vendor/github.com/miekg/dns:go_default_library",
+ "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1: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/controller/acmeorders/selectors/dns_names.go b/pkg/controller/acmeorders/selectors/dns_names.go
new file mode 100644
index 000000000..4c88ff13f
--- /dev/null
+++ b/pkg/controller/acmeorders/selectors/dns_names.go
@@ -0,0 +1,47 @@
+/*
+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 selectors
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
+)
+
+func DNSNames(sel cmapi.CertificateDNSNameSelector) Selector {
+ return &dnsNamesSelector{
+ allowedDNSNames: sel.DNSNames,
+ }
+}
+
+type dnsNamesSelector struct {
+ allowedDNSNames []string
+}
+
+func (s *dnsNamesSelector) Matches(meta metav1.ObjectMeta, dnsName string) (bool, int) {
+ if len(s.allowedDNSNames) == 0 {
+ return true, 0
+ }
+
+ for _, d := range s.allowedDNSNames {
+ if dnsName == d {
+ return true, 1
+ }
+ }
+
+ return false, 0
+}
diff --git a/pkg/controller/acmeorders/selectors/dns_zones.go b/pkg/controller/acmeorders/selectors/dns_zones.go
new file mode 100644
index 000000000..4c14ba55b
--- /dev/null
+++ b/pkg/controller/acmeorders/selectors/dns_zones.go
@@ -0,0 +1,54 @@
+/*
+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 selectors
+
+import (
+ "github.com/miekg/dns"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
+)
+
+func DNSZones(sel cmapi.CertificateDNSNameSelector) Selector {
+ return &dnsZonesSelector{
+ allowedDNSZones: sel.DNSZones,
+ }
+}
+
+type dnsZonesSelector struct {
+ allowedDNSZones []string
+}
+
+func (s *dnsZonesSelector) Matches(meta metav1.ObjectMeta, dnsName string) (bool, int) {
+ if len(s.allowedDNSZones) == 0 {
+ return true, 0
+ }
+
+ maxMatchingLabels := 0
+ for _, zone := range s.allowedDNSZones {
+ numMatchingLabels := dns.CompareDomainName(zone, dnsName)
+ if numMatchingLabels != dns.CountLabel(zone) {
+ continue
+ }
+
+ if numMatchingLabels > maxMatchingLabels {
+ maxMatchingLabels = numMatchingLabels
+ }
+ }
+
+ return maxMatchingLabels > 0, maxMatchingLabels
+}
diff --git a/pkg/controller/acmeorders/selectors/labels.go b/pkg/controller/acmeorders/selectors/labels.go
new file mode 100644
index 000000000..5acec8194
--- /dev/null
+++ b/pkg/controller/acmeorders/selectors/labels.go
@@ -0,0 +1,52 @@
+/*
+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 selectors
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
+)
+
+func Labels(sel cmapi.CertificateDNSNameSelector) Selector {
+ return &labelSelector{
+ requiredLabels: sel.MatchLabels,
+ }
+}
+
+type labelSelector struct {
+ requiredLabels map[string]string
+}
+
+func (s *labelSelector) Matches(meta metav1.ObjectMeta, dnsName string) (bool, int) {
+ if len(s.requiredLabels) == 0 {
+ return true, 0
+ }
+
+ hasAllLabels := true
+ matches := 0
+ for k, v := range s.requiredLabels {
+ actualV, hasLabel := meta.Labels[k]
+ if !hasLabel || v != actualV {
+ hasAllLabels = false
+ break
+ }
+ matches++
+ }
+
+ return hasAllLabels, matches
+}
diff --git a/pkg/controller/acmeorders/selectors/selector.go b/pkg/controller/acmeorders/selectors/selector.go
new file mode 100644
index 000000000..43bc1858c
--- /dev/null
+++ b/pkg/controller/acmeorders/selectors/selector.go
@@ -0,0 +1,32 @@
+/*
+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 selectors
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type Selector interface {
+ // Matches returns the number of matches that this selector
+ // has with the given object metadata and dnsName.
+ // The greater the returned number, the more 'specific' of a
+ // match this meta/dnsName pair has with this selector.
+ // In some cases, the selector may 'match' (i.e. the bool == true),
+ // but the number of matches may be zero (i.e. for a label selector,
+ // where an empty selector matches all).
+ Matches(meta metav1.ObjectMeta, dnsName string) (bool, int)
+}
diff --git a/pkg/controller/acmeorders/sync.go b/pkg/controller/acmeorders/sync.go
index 9675c69e1..c975791e7 100644
--- a/pkg/controller/acmeorders/sync.go
+++ b/pkg/controller/acmeorders/sync.go
@@ -33,6 +33,7 @@ import (
"github.com/jetstack/cert-manager/pkg/acme"
acmecl "github.com/jetstack/cert-manager/pkg/acme/client"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
+ "github.com/jetstack/cert-manager/pkg/controller/acmeorders/selectors"
logf "github.com/jetstack/cert-manager/pkg/logs"
"github.com/jetstack/cert-manager/pkg/metrics"
acmeapi "github.com/jetstack/cert-manager/third_party/crypto/acme"
@@ -399,7 +400,7 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, issue
return fmt.Errorf("error constructing old format Challenge resource for authorization: %v", err)
}
} else {
- cs, err = c.challengeSpecForAuthorization(ctx, cl, issuer, o, authz)
+ cs, err = challengeSpecForAuthorization(ctx, cl, issuer, o, authz)
if err != nil {
return fmt.Errorf("error constructing Challenge resource for authorization: %v", err)
}
@@ -412,68 +413,24 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, issue
return nil
}
-func (c *controller) challengeSpecForAuthorization(ctx context.Context, cl acmecl.Interface, issuer cmapi.GenericIssuer, o *cmapi.Order, authz *acmeapi.Authorization) (*cmapi.ChallengeSpec, error) {
+func challengeSpecForAuthorization(ctx context.Context, cl acmecl.Interface, issuer cmapi.GenericIssuer, o *cmapi.Order, authz *acmeapi.Authorization) (*cmapi.ChallengeSpec, error) {
+ log := logf.FromContext(ctx, "challengeSpecForAuthorization")
+ dbg := log.V(logf.DebugLevel)
+
// 1. fetch solvers from issuer
solvers := issuer.GetSpec().ACME.Solvers
- // 2. filter solvers to only those that matchLabels
- var candidates []cmapi.ACMEChallengeSolver
- for _, cfg := range solvers {
- // if this config has no selector at all, then it can be used for all
- // domain authorizations, so we include it
- if cfg.Selector == nil {
- candidates = append(candidates, cfg)
- continue
- }
- if !resourceMatchesSelector(o, cfg.Selector.MatchLabels) {
- continue
- }
- if len(cfg.Selector.DNSNames) > 0 && !orderHasOneOfDNSNames(o, cfg.Selector.DNSNames...) {
- continue
- }
- candidates = append(candidates, cfg)
- }
-
- // 3. iterate through each solver, finding the most specific match (taking account of dnsNames)
- // if a solver config that matches all dns names is found, we'll use the
- // one with the most labels, as this is the 'most specific match' for the
- // certificate this order is fulfilling.
- // the matchAll solver is only used if the domainToFind is not listed in
- // any other solver's DNSNames list.
domainToFind := authz.Identifier.Value
if authz.Wildcard {
domainToFind = "*." + domainToFind
}
- acmeCh, solverConfigToUse := determineSolverConfigToUse(candidates, authz, domainToFind)
- if acmeCh == nil || solverConfigToUse == nil {
- return nil, fmt.Errorf("solver configuration for domain %q not found. Ensure at least one Solver on your Issuer matches the order", domainToFind)
- }
- key, err := keyForChallenge(cl, acmeCh)
- if err != nil {
- return nil, err
- }
+ var selectedSolver *cmapi.ACMEChallengeSolver
+ var selectedChallenge *acmeapi.Challenge
+ selectedNumLabelsMatch := 0
+ selectedNumDNSNamesMatch := 0
+ selectedNumDNSZonesMatch := 0
- // 4. construct Challenge resource with spec.solver field set
- return &cmapi.ChallengeSpec{
- AuthzURL: authz.URL,
- Type: acmeCh.Type,
- URL: acmeCh.URL,
- DNSName: authz.Identifier.Value,
- Token: acmeCh.Token,
- Key: key,
- Solver: solverConfigToUse,
- Wildcard: authz.Wildcard,
- IssuerRef: o.Spec.IssuerRef,
- }, nil
-}
-
-// if a solver config that matches all dns names is found, we'll use the
-// one with the most labels, as this is the 'most specific match' for the
-// certificate this order is fulfilling.
-// the matchAll solver is only used if the domainToFind is not listed in
-// any other solver's DNSNames list.
-func determineSolverConfigToUse(candidates []cmapi.ACMEChallengeSolver, authz *acmeapi.Authorization, domainToFind string) (*acmeapi.Challenge, *cmapi.ACMEChallengeSolver) {
challengeForSolver := func(solver *cmapi.ACMEChallengeSolver) *acmeapi.Challenge {
for _, ch := range authz.Challenges {
switch {
@@ -486,90 +443,157 @@ func determineSolverConfigToUse(candidates []cmapi.ACMEChallengeSolver, authz *a
return nil
}
- // this variable tracks the number of labels that matched when a solver
- // that specifically names the dnsName in the authorization matches.
- // This is used to tie-break if two different solver configurations both
- // explicitly name a dnsName
- numLabelsSpecificMatch := 0
- var specificMatch *cmapi.ACMEChallengeSolver
- var specificMatchToSolve *acmeapi.Challenge
-
- // this variable tracks the number of labels that matched when a solver
- // that does NOT specifically list the authorization's dnsName matches.
- // If no solver explicitly lists the dnsName then the solver that matches
- // the most labels is used.
- matchAllDomainsNumLabels := 0
- // matchAll is the most-specific solver that matches the authorization,
- // that does not list the authorization's dns name
- var matchAll *cmapi.ACMEChallengeSolver
- var matchAllToSolve *acmeapi.Challenge
-
- for idx := range candidates {
- d := &candidates[idx]
- acmech := challengeForSolver(d)
+ // 2. filter solvers to only those that matchLabels
+ for _, cfg := range solvers {
+ acmech := challengeForSolver(&cfg)
if acmech == nil {
+ dbg.Info("cannot use solver as the ACME authorization does not allow solvers of this type")
continue
}
- // empty selector/dnsName list matches all
- if d.Selector == nil {
- if matchAll == nil {
- matchAllDomainsNumLabels = 0
- matchAll = d
- matchAllToSolve = acmech
- }
+ if cfg.Selector == nil && selectedSolver == nil {
+ dbg.Info("selecting solver due to nil selector and no previously selected solver")
+ selectedSolver = cfg.DeepCopy()
+ selectedChallenge = acmech
continue
}
- if len(d.Selector.DNSNames) == 0 {
- if len(d.Selector.MatchLabels) > matchAllDomainsNumLabels || matchAll == nil {
- matchAll = d
- matchAllToSolve = acmech
- matchAllDomainsNumLabels = len(d.Selector.MatchLabels)
- }
+
+ labelsMatch, numLabelsMatch := selectors.Labels(*cfg.Selector).Matches(o.ObjectMeta, domainToFind)
+ dnsNamesMatch, numDNSNamesMatch := selectors.DNSNames(*cfg.Selector).Matches(o.ObjectMeta, domainToFind)
+ dnsZonesMatch, numDNSZonesMatch := selectors.DNSZones(*cfg.Selector).Matches(o.ObjectMeta, domainToFind)
+
+ if !labelsMatch || !dnsNamesMatch || !dnsZonesMatch {
+ dbg.Info("not selecting solver", "labels_match", labelsMatch, "dnsnames_match", dnsNamesMatch, "dnszones_match", dnsZonesMatch)
+ continue
}
- for _, dom := range d.Selector.DNSNames {
- if dom != domainToFind {
+
+ dbg.Info("selector matches")
+
+ selectSolver := func() {
+ selectedSolver = cfg.DeepCopy()
+ selectedChallenge = acmech
+ selectedNumLabelsMatch = numLabelsMatch
+ selectedNumDNSNamesMatch = numDNSNamesMatch
+ selectedNumDNSZonesMatch = numDNSZonesMatch
+ }
+
+ if selectedSolver == nil {
+ dbg.Info("selecting solver as there is no previously selected solver")
+ selectSolver()
+ continue
+ }
+
+ dbg.Info("determining whether this match is more significant than last")
+
+ // because we don't count multiple dnsName matches as extra 'weight'
+ // in the selection process, we normalise the numDNSNamesMatch vars
+ // to be either 1 or 0 (i.e. true or false)
+ selectedHasMatchingDNSNames := selectedNumDNSNamesMatch > 0
+ hasMatchingDNSNames := numDNSNamesMatch > 0
+
+ // dnsName selectors have the highest precedence, so check them first
+ switch {
+ case !selectedHasMatchingDNSNames && hasMatchingDNSNames:
+ dbg.Info("selecting solver as this solver has matching DNS names and the previous one does not")
+ selectSolver()
+ continue
+ case selectedHasMatchingDNSNames && !hasMatchingDNSNames:
+ dbg.Info("not selecting solver as the previous one has matching DNS names and this one does not")
+ continue
+ case !selectedHasMatchingDNSNames && !hasMatchingDNSNames:
+ dbg.Info("solver does not have any matching DNS names, checking dnsZones")
+ // check zones
+ case selectedHasMatchingDNSNames && hasMatchingDNSNames:
+ dbg.Info("both this solver and the previously selected one matches dnsNames, comparing zones")
+ if numDNSZonesMatch > selectedNumDNSZonesMatch {
+ dbg.Info("selecting solver as this one has a more specific dnsZone match than the previously selected one")
+ selectSolver()
continue
}
- if len(d.Selector.MatchLabels) > numLabelsSpecificMatch || specificMatch == nil {
- specificMatch = d
- specificMatchToSolve = acmech
- numLabelsSpecificMatch = len(d.Selector.MatchLabels)
- break
+ if selectedNumDNSZonesMatch > numDNSZonesMatch {
+ dbg.Info("not selecting this solver as the previously selected one has a more specific dnsZone match")
+ continue
}
+ dbg.Info("both this solver and the previously selected one match dnsZones, comparing labels")
+ // choose the one with the most labels
+ if numLabelsMatch > selectedNumLabelsMatch {
+ dbg.Info("selecting solver as this one has more labels than the previously selected one")
+ selectSolver()
+ continue
+ }
+ dbg.Info("not selecting this solver as previous one has either the same number of or more labels")
+ continue
}
- }
- if specificMatch != nil {
- return specificMatchToSolve, specificMatch
- }
- if matchAll != nil {
- return matchAllToSolve, matchAll
- }
- return nil, nil
-}
-func resourceMatchesSelector(r metav1.Object, sel map[string]string) bool {
- labels := r.GetLabels()
- for k, v := range sel {
- val, ok := labels[k]
- if !ok || v != val {
- return false
- }
- }
- return true
-}
+ selectedHasMatchingDNSZones := selectedNumDNSZonesMatch > 0
+ hasMatchingDNSZones := numDNSZonesMatch > 0
-func orderHasOneOfDNSNames(o *cmapi.Order, dnsNames ...string) bool {
- dnsNameMap := map[string]struct{}{}
- for _, d := range o.Spec.DNSNames {
- dnsNameMap[d] = struct{}{}
- }
- for _, d := range dnsNames {
- if _, ok := dnsNameMap[d]; ok {
- return true
+ switch {
+ case !selectedHasMatchingDNSZones && hasMatchingDNSZones:
+ dbg.Info("selecting solver as this solver has matching DNS zones and the previous one does not")
+ selectSolver()
+ continue
+ case selectedHasMatchingDNSZones && !hasMatchingDNSZones:
+ dbg.Info("not selecting solver as the previous one has matching DNS zones and this one does not")
+ continue
+ case !selectedHasMatchingDNSZones && !hasMatchingDNSZones:
+ dbg.Info("solver does not have any matching DNS zones, checking labels")
+ // check labels
+ case selectedHasMatchingDNSZones && hasMatchingDNSZones:
+ dbg.Info("both this solver and the previously selected one matches dnsZones")
+ dbg.Info("comparing number of matching domain segments")
+ // choose the one with the most matching DNS zone segments
+ if numDNSZonesMatch > selectedNumDNSZonesMatch {
+ dbg.Info("selecting solver because this one has more matching DNS zone segments")
+ selectSolver()
+ continue
+ }
+ if selectedNumDNSZonesMatch > numDNSZonesMatch {
+ dbg.Info("not selecting solver because previous one has more matching DNS zone segments")
+ continue
+ }
+ // choose the one with the most labels
+ if numLabelsMatch > selectedNumLabelsMatch {
+ dbg.Info("selecting solver because this one has more labels than the previous one")
+ selectSolver()
+ continue
+ }
+ dbg.Info("not selecting solver as this one's number of matching labels is equal to or less than the last one")
+ continue
}
+
+ if numLabelsMatch > selectedNumLabelsMatch {
+ dbg.Info("selecting solver as this one has more labels than the last one")
+ selectSolver()
+ continue
+ }
+
+ dbg.Info("not selecting solver as this one's number of matching labels is equal to or less than the last one (reached end of loop)")
+ // if we get here, the number of matches is less than or equal so we
+ // fallback to choosing the first in the list
}
- return false
+
+ if selectedSolver == nil || selectedChallenge == nil {
+ return nil, fmt.Errorf("failed to find matching challenge solver for challenge")
+ }
+
+ key, err := keyForChallenge(cl, selectedChallenge)
+ if err != nil {
+ return nil, err
+ }
+
+ // 4. construct Challenge resource with spec.solver field set
+ return &cmapi.ChallengeSpec{
+ AuthzURL: authz.URL,
+ Type: selectedChallenge.Type,
+ URL: selectedChallenge.URL,
+ DNSName: authz.Identifier.Value,
+ Token: selectedChallenge.Token,
+ Key: key,
+ Solver: selectedSolver,
+ Wildcard: authz.Wildcard,
+ IssuerRef: o.Spec.IssuerRef,
+ }, nil
}
func (c *controller) oldFormatChallengeSpecForAuthorization(ctx context.Context, cl acmecl.Interface, issuer cmapi.GenericIssuer, o *cmapi.Order, authz *acmeapi.Authorization) (*cmapi.ChallengeSpec, error) {
diff --git a/pkg/controller/acmeorders/sync_test.go b/pkg/controller/acmeorders/sync_test.go
index b51e3cb15..98a39ba5a 100644
--- a/pkg/controller/acmeorders/sync_test.go
+++ b/pkg/controller/acmeorders/sync_test.go
@@ -22,6 +22,7 @@ import (
"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"
@@ -312,6 +313,1124 @@ dGVzdA==
}
}
+//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 := &acmeapi.Challenge{
+ Type: "http-01",
+ Token: "http-01-token",
+ }
+ acmeChallengeDNS01 := &acmeapi.Challenge{
+ Type: "dns-01",
+ Token: "dns-01-token",
+ }
+
+ tests := map[string]struct {
+ acmeClient acmecl.Interface
+ issuer v1alpha1.GenericIssuer
+ order *v1alpha1.Order
+ authz *acmeapi.Authorization
+
+ 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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "notexample.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Wildcard: true,
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "www.example.com",
+ },
+ Wildcard: true,
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "www.prod.example.com",
+ },
+ Wildcard: true,
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "www.prod.example.com",
+ },
+ Wildcard: true,
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "www.example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "www.example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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: &acmeapi.Authorization{
+ Identifier: acmeapi.AuthzID{
+ Value: "www.example.com",
+ },
+ Challenges: []*acmeapi.Challenge{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",
+ },
+ },
+ },
+ },
+ },
+ }
+ 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