Add solverFor and wait.go unit tests for acme dns solver
This commit is contained in:
parent
d6fcee3b8f
commit
01fa76e00a
278
pkg/issuer/acme/dns/dns_test.go
Normal file
278
pkg/issuer/acme/dns/dns_test.go
Normal file
@ -0,0 +1,278 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/cloudflare"
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
// Issuer resource this solver is for
|
||||
Issuer v1alpha1.GenericIssuer
|
||||
|
||||
// Objects here are pre-loaded into the fake client
|
||||
KubeObjects []runtime.Object
|
||||
|
||||
// Secret objects to store in the fake lister
|
||||
SecretLister []*corev1.Secret
|
||||
|
||||
// the resourceNamespace to set on the solver
|
||||
ResourceNamespace string
|
||||
|
||||
// certificate used in the test
|
||||
Certificate *v1alpha1.Certificate
|
||||
}
|
||||
|
||||
func (f *fixture) solver() *Solver {
|
||||
kubeClient := kubefake.NewSimpleClientset(f.KubeObjects...)
|
||||
sharedInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, 0)
|
||||
secretsLister := sharedInformerFactory.Core().V1().Secrets().Lister()
|
||||
for _, s := range f.SecretLister {
|
||||
sharedInformerFactory.Core().V1().Secrets().Informer().GetIndexer().Add(s)
|
||||
}
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
sharedInformerFactory.Start(stopCh)
|
||||
return &Solver{
|
||||
issuer: f.Issuer,
|
||||
client: kubeClient,
|
||||
secretLister: secretsLister,
|
||||
resourceNamespace: f.ResourceNamespace,
|
||||
}
|
||||
}
|
||||
|
||||
func newIssuer(name, namespace string, configs []v1alpha1.ACMEIssuerDNS01Provider) *v1alpha1.Issuer {
|
||||
return &v1alpha1.Issuer{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha1.IssuerSpec{
|
||||
IssuerConfig: v1alpha1.IssuerConfig{
|
||||
ACME: &v1alpha1.ACMEIssuer{
|
||||
DNS01: &v1alpha1.ACMEIssuerDNS01Config{
|
||||
Providers: configs,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCertificate(name, namespace, cn string, dnsNames []string, configs []v1alpha1.ACMECertificateDomainConfig) *v1alpha1.Certificate {
|
||||
return &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
CommonName: cn,
|
||||
DNSNames: dnsNames,
|
||||
ACME: &v1alpha1.ACMECertificateConfig{
|
||||
Config: configs,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newSecret(name, namespace string, data map[string][]byte) *corev1.Secret {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func TestSolverFor(t *testing.T) {
|
||||
type testT struct {
|
||||
f *fixture
|
||||
domain string
|
||||
expectErr bool
|
||||
expectedSolverType reflect.Type
|
||||
}
|
||||
tests := map[string]testT{
|
||||
"loads secret for cloudflare provider": {
|
||||
f: &fixture{
|
||||
Issuer: newIssuer("test", "default", []v1alpha1.ACMEIssuerDNS01Provider{
|
||||
{
|
||||
Name: "fake-cloudflare",
|
||||
Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIKey: v1alpha1.SecretKeySelector{
|
||||
LocalObjectReference: v1alpha1.LocalObjectReference{
|
||||
Name: "cloudflare-key",
|
||||
},
|
||||
Key: "api-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
SecretLister: []*corev1.Secret{newSecret("cloudflare-key", "default", map[string][]byte{
|
||||
"api-key": []byte("a-cloudflare-api-key"),
|
||||
})},
|
||||
ResourceNamespace: "default",
|
||||
Certificate: newCertificate("test", "default", "example.com", nil, []v1alpha1.ACMECertificateDomainConfig{
|
||||
{
|
||||
Domains: []string{"example.com"},
|
||||
DNS01: &v1alpha1.ACMECertificateDNS01Config{
|
||||
Provider: "fake-cloudflare",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
domain: "example.com",
|
||||
expectedSolverType: reflect.TypeOf(&cloudflare.DNSProvider{}),
|
||||
},
|
||||
"fails to load a cloudflare provider with a missing secret": {
|
||||
f: &fixture{
|
||||
Issuer: newIssuer("test", "default", []v1alpha1.ACMEIssuerDNS01Provider{
|
||||
{
|
||||
Name: "fake-cloudflare",
|
||||
Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIKey: v1alpha1.SecretKeySelector{
|
||||
LocalObjectReference: v1alpha1.LocalObjectReference{
|
||||
Name: "cloudflare-key",
|
||||
},
|
||||
Key: "api-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
// don't include any secrets in the lister
|
||||
SecretLister: []*corev1.Secret{},
|
||||
ResourceNamespace: "default",
|
||||
Certificate: newCertificate("test", "default", "example.com", nil, []v1alpha1.ACMECertificateDomainConfig{
|
||||
{
|
||||
Domains: []string{"example.com"},
|
||||
DNS01: &v1alpha1.ACMECertificateDNS01Config{
|
||||
Provider: "fake-cloudflare",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
domain: "example.com",
|
||||
expectErr: true,
|
||||
},
|
||||
"fails to load a cloudflare provider with an invalid secret": {
|
||||
f: &fixture{
|
||||
Issuer: newIssuer("test", "default", []v1alpha1.ACMEIssuerDNS01Provider{
|
||||
{
|
||||
Name: "fake-cloudflare",
|
||||
Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIKey: v1alpha1.SecretKeySelector{
|
||||
LocalObjectReference: v1alpha1.LocalObjectReference{
|
||||
Name: "cloudflare-key",
|
||||
},
|
||||
Key: "api-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
SecretLister: []*corev1.Secret{newSecret("cloudflare-key", "default", map[string][]byte{
|
||||
"api-key-oops": []byte("a-cloudflare-api-key"),
|
||||
})},
|
||||
ResourceNamespace: "default",
|
||||
Certificate: newCertificate("test", "default", "example.com", nil, []v1alpha1.ACMECertificateDomainConfig{
|
||||
{
|
||||
Domains: []string{"example.com"},
|
||||
DNS01: &v1alpha1.ACMECertificateDNS01Config{
|
||||
Provider: "fake-cloudflare",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
domain: "example.com",
|
||||
expectErr: true,
|
||||
},
|
||||
"fails to load a provider with no config set for the domain": {
|
||||
f: &fixture{
|
||||
Issuer: newIssuer("test", "default", []v1alpha1.ACMEIssuerDNS01Provider{
|
||||
{
|
||||
Name: "fake-cloudflare",
|
||||
Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIKey: v1alpha1.SecretKeySelector{
|
||||
LocalObjectReference: v1alpha1.LocalObjectReference{
|
||||
Name: "cloudflare-key",
|
||||
},
|
||||
Key: "api-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
SecretLister: []*corev1.Secret{newSecret("cloudflare-key", "default", map[string][]byte{
|
||||
"api-key": []byte("a-cloudflare-api-key"),
|
||||
})},
|
||||
ResourceNamespace: "default",
|
||||
Certificate: newCertificate("test", "default", "example.com", nil, []v1alpha1.ACMECertificateDomainConfig{
|
||||
{
|
||||
Domains: []string{"example-oops.com"},
|
||||
DNS01: &v1alpha1.ACMECertificateDNS01Config{
|
||||
Provider: "fake-cloudflare",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
domain: "example.com",
|
||||
expectErr: true,
|
||||
},
|
||||
"fails to load a provider with a non-existent provider set for the domain": {
|
||||
f: &fixture{
|
||||
Issuer: newIssuer("test", "default", []v1alpha1.ACMEIssuerDNS01Provider{
|
||||
{
|
||||
Name: "fake-cloudflare",
|
||||
Cloudflare: &v1alpha1.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIKey: v1alpha1.SecretKeySelector{
|
||||
LocalObjectReference: v1alpha1.LocalObjectReference{
|
||||
Name: "cloudflare-key",
|
||||
},
|
||||
Key: "api-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
SecretLister: []*corev1.Secret{newSecret("cloudflare-key", "default", map[string][]byte{
|
||||
"api-key": []byte("a-cloudflare-api-key"),
|
||||
})},
|
||||
ResourceNamespace: "default",
|
||||
Certificate: newCertificate("test", "default", "example.com", nil, []v1alpha1.ACMECertificateDomainConfig{
|
||||
{
|
||||
Domains: []string{"example.com"},
|
||||
DNS01: &v1alpha1.ACMECertificateDNS01Config{
|
||||
Provider: "fake-cloudflare-oops",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
domain: "example.com",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
testFn := func(test testT) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
s := test.f.solver()
|
||||
dnsSolver, err := s.solverFor(test.f.Certificate, test.domain)
|
||||
if err != nil && !test.expectErr {
|
||||
t.Errorf("expected solverFor to not error, but got: %s", err.Error())
|
||||
return
|
||||
}
|
||||
typeOfSolver := reflect.TypeOf(dnsSolver)
|
||||
if typeOfSolver != test.expectedSolverType {
|
||||
t.Errorf("expected solver of type %q but got one of type %q", test.expectedSolverType, typeOfSolver)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
for name, test := range tests {
|
||||
t.Run(name, testFn(test))
|
||||
}
|
||||
}
|
||||
5
pkg/issuer/acme/dns/util/testdata/resolv.conf.1
vendored
Normal file
5
pkg/issuer/acme/dns/util/testdata/resolv.conf.1
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
domain company.com
|
||||
nameserver 10.200.3.249
|
||||
nameserver 10.200.3.250:5353
|
||||
nameserver 2001:4860:4860::8844
|
||||
nameserver [10.0.0.1]:5353
|
||||
166
pkg/issuer/acme/dns/util/wait_test.go
Normal file
166
pkg/issuer/acme/dns/util/wait_test.go
Normal file
@ -0,0 +1,166 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var lookupNameserversTestsOK = []struct {
|
||||
fqdn string
|
||||
nss []string
|
||||
}{
|
||||
{"books.google.com.ng.",
|
||||
[]string{"ns1.google.com.", "ns2.google.com.", "ns3.google.com.", "ns4.google.com."},
|
||||
},
|
||||
{"www.google.com.",
|
||||
[]string{"ns1.google.com.", "ns2.google.com.", "ns3.google.com.", "ns4.google.com."},
|
||||
},
|
||||
{"physics.georgetown.edu.",
|
||||
[]string{"ns1.georgetown.edu.", "ns2.georgetown.edu.", "ns3.georgetown.edu."},
|
||||
},
|
||||
}
|
||||
|
||||
var lookupNameserversTestsErr = []struct {
|
||||
fqdn string
|
||||
error string
|
||||
}{
|
||||
// invalid tld
|
||||
{"_null.n0n0.",
|
||||
"Could not determine the zone",
|
||||
},
|
||||
}
|
||||
|
||||
var findZoneByFqdnTests = []struct {
|
||||
fqdn string
|
||||
zone string
|
||||
}{
|
||||
{"mail.google.com.", "google.com."}, // domain is a CNAME
|
||||
{"foo.google.com.", "google.com."}, // domain is a non-existent subdomain
|
||||
// TODO: work out why this test doesn't work
|
||||
//{"example.com.ac.", "ac."}, // domain is a eTLD
|
||||
}
|
||||
|
||||
var checkAuthoritativeNssTests = []struct {
|
||||
fqdn, value string
|
||||
ns []string
|
||||
ok bool
|
||||
}{
|
||||
// TXT RR w/ expected value
|
||||
{"8.8.8.8.asn.routeviews.org.", "151698.8.8.024", []string{"asnums.routeviews.org."},
|
||||
true,
|
||||
},
|
||||
// No TXT RR
|
||||
{"ns1.google.com.", "", []string{"ns2.google.com."},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
var checkAuthoritativeNssTestsErr = []struct {
|
||||
fqdn, value string
|
||||
ns []string
|
||||
error string
|
||||
}{
|
||||
// TXT RR /w unexpected value
|
||||
{"8.8.8.8.asn.routeviews.org.", "fe01=", []string{"asnums.routeviews.org."},
|
||||
"did not return the expected TXT record",
|
||||
},
|
||||
// No TXT RR
|
||||
{"ns1.google.com.", "fe01=", []string{"ns2.google.com."},
|
||||
"did not return the expected TXT record",
|
||||
},
|
||||
}
|
||||
|
||||
var checkResolvConfServersTests = []struct {
|
||||
fixture string
|
||||
expected []string
|
||||
defaults []string
|
||||
}{
|
||||
{"testdata/resolv.conf.1", []string{"10.200.3.249:53", "10.200.3.250:5353", "[2001:4860:4860::8844]:53", "[10.0.0.1]:5353"}, []string{"127.0.0.1:53"}},
|
||||
{"testdata/resolv.conf.nonexistant", []string{"127.0.0.1:53"}, []string{"127.0.0.1:53"}},
|
||||
}
|
||||
|
||||
func TestPreCheckDNS(t *testing.T) {
|
||||
// TODO: find a better TXT record to use in tests
|
||||
ok, err := PreCheckDNS("google.com.", "v=spf1 include:_spf.google.com ~all")
|
||||
if err != nil || !ok {
|
||||
t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupNameserversOK(t *testing.T) {
|
||||
for _, tt := range lookupNameserversTestsOK {
|
||||
nss, err := lookupNameservers(tt.fqdn)
|
||||
if err != nil {
|
||||
t.Fatalf("#%s: got %q; want nil", tt.fqdn, err)
|
||||
}
|
||||
|
||||
sort.Strings(nss)
|
||||
sort.Strings(tt.nss)
|
||||
|
||||
if !reflect.DeepEqual(nss, tt.nss) {
|
||||
t.Errorf("#%s: got %v; want %v", tt.fqdn, nss, tt.nss)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupNameserversErr(t *testing.T) {
|
||||
for _, tt := range lookupNameserversTestsErr {
|
||||
_, err := lookupNameservers(tt.fqdn)
|
||||
if err == nil {
|
||||
t.Fatalf("#%s: expected %q (error); got <nil>", tt.fqdn, tt.error)
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), tt.error) {
|
||||
t.Errorf("#%s: expected %q (error); got %q", tt.fqdn, tt.error, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindZoneByFqdn(t *testing.T) {
|
||||
for _, tt := range findZoneByFqdnTests {
|
||||
res, err := FindZoneByFqdn(tt.fqdn, RecursiveNameservers)
|
||||
if err != nil {
|
||||
t.Errorf("FindZoneByFqdn failed for %s: %v", tt.fqdn, err)
|
||||
}
|
||||
if res != tt.zone {
|
||||
t.Errorf("%s: got %s; want %s", tt.fqdn, res, tt.zone)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckAuthoritativeNss(t *testing.T) {
|
||||
for _, tt := range checkAuthoritativeNssTests {
|
||||
ok, _ := checkAuthoritativeNss(tt.fqdn, tt.value, tt.ns)
|
||||
if ok != tt.ok {
|
||||
t.Errorf("%s: got %t; want %t", tt.fqdn, ok, tt.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckAuthoritativeNssErr(t *testing.T) {
|
||||
for _, tt := range checkAuthoritativeNssTestsErr {
|
||||
_, err := checkAuthoritativeNss(tt.fqdn, tt.value, tt.ns)
|
||||
if err == nil {
|
||||
t.Fatalf("#%s: expected %q (error); got <nil>", tt.fqdn, tt.error)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.error) {
|
||||
t.Errorf("#%s: expected %q (error); got %q", tt.fqdn, tt.error, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConfServers(t *testing.T) {
|
||||
for _, tt := range checkResolvConfServersTests {
|
||||
result := getNameservers(tt.fixture, tt.defaults)
|
||||
|
||||
sort.Strings(result)
|
||||
sort.Strings(tt.expected)
|
||||
if !reflect.DeepEqual(result, tt.expected) {
|
||||
t.Errorf("#%s: expected %q; got %q", tt.fixture, tt.expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user