Add example server that passes the test fixtures
Signed-off-by: Jake Sanders <i@am.so-aweso.me>
This commit is contained in:
		@@ -1 +1,59 @@
 | 
			
		||||
package example
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/miekg/dns"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (e *exampleSolver) handleDNSRequest(w dns.ResponseWriter, req *dns.Msg) {
 | 
			
		||||
	msg := new(dns.Msg)
 | 
			
		||||
	msg.SetReply(req)
 | 
			
		||||
	switch req.Opcode {
 | 
			
		||||
	case dns.OpcodeQuery:
 | 
			
		||||
		for _, q := range msg.Question {
 | 
			
		||||
			switch q.Qtype {
 | 
			
		||||
			case dns.TypeA:
 | 
			
		||||
				rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN A 127.0.0.1", q.Name))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					msg.SetRcode(req, dns.RcodeNameError)
 | 
			
		||||
				} else {
 | 
			
		||||
					msg.Answer = append(msg.Answer, rr)
 | 
			
		||||
				}
 | 
			
		||||
			case dns.TypeTXT:
 | 
			
		||||
				// get record
 | 
			
		||||
				e.RLock()
 | 
			
		||||
				record, found := e.txtRecords[q.Name]
 | 
			
		||||
				e.RUnlock()
 | 
			
		||||
				if !found {
 | 
			
		||||
					msg.SetRcode(req, dns.RcodeNameError)
 | 
			
		||||
				} else {
 | 
			
		||||
					rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN TXT %s", q.Name, record))
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						msg.SetRcode(req, dns.RcodeServerFailure)
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
					msg.Answer = append(msg.Answer, rr)
 | 
			
		||||
				}
 | 
			
		||||
			case dns.TypeNS:
 | 
			
		||||
				rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN NS ns.example-acme-webook.invalid.", q.Name))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					msg.SetRcode(req, dns.RcodeServerFailure)
 | 
			
		||||
					break
 | 
			
		||||
				} else {
 | 
			
		||||
					msg.Answer = append(msg.Answer, rr)
 | 
			
		||||
				}
 | 
			
		||||
			case dns.TypeSOA:
 | 
			
		||||
				rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN SOA %s 20 5 5 5 5", "ns.example-acme-webook.invalid.", "ns.example-acme-webook.invalid."))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					msg.SetRcode(req, dns.RcodeServerFailure)
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				msg.Answer = append(msg.Answer, rr)
 | 
			
		||||
			default:
 | 
			
		||||
				msg.SetRcode(req, dns.RcodeServerFailure)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	w.WriteMsg(msg)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,61 @@
 | 
			
		||||
// package example contains a self-contained example of a webhook that passes the cert-manager
 | 
			
		||||
// DNS conformance tests
 | 
			
		||||
package example
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/jetstack/cert-manager/pkg/acme/webhook"
 | 
			
		||||
	acme "github.com/jetstack/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
 | 
			
		||||
	"github.com/miekg/dns"
 | 
			
		||||
	"k8s.io/client-go/rest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type exampleSolver struct {
 | 
			
		||||
	name       string
 | 
			
		||||
	server     *dns.Server
 | 
			
		||||
	txtRecords map[string]string
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e exampleSolver) Name() string {
 | 
			
		||||
	return e.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e exampleSolver) Present(ch *acme.ChallengeRequest) error {
 | 
			
		||||
	e.Lock()
 | 
			
		||||
	e.txtRecords[ch.ResolvedFQDN] = ch.Key
 | 
			
		||||
	e.Unlock()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e exampleSolver) CleanUp(ch *acme.ChallengeRequest) error {
 | 
			
		||||
	e.Lock()
 | 
			
		||||
	delete(e.txtRecords, ch.ResolvedFQDN)
 | 
			
		||||
	e.Unlock()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e exampleSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error {
 | 
			
		||||
	go func(done <-chan struct{}) {
 | 
			
		||||
		<-done
 | 
			
		||||
		e.server.Shutdown()
 | 
			
		||||
	}(stopCh)
 | 
			
		||||
	go func() {
 | 
			
		||||
		e.server.ListenAndServe()
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(port string) webhook.Solver {
 | 
			
		||||
	e := &exampleSolver{
 | 
			
		||||
		name:       "example",
 | 
			
		||||
		txtRecords: make(map[string]string),
 | 
			
		||||
	}
 | 
			
		||||
	e.server = &dns.Server{
 | 
			
		||||
		Addr:    ":" + port,
 | 
			
		||||
		Net:     "udp",
 | 
			
		||||
		Handler: dns.HandlerFunc(e.handleDNSRequest),
 | 
			
		||||
	}
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,95 @@
 | 
			
		||||
package example
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	acme "github.com/jetstack/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
 | 
			
		||||
	"github.com/miekg/dns"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestExampleSolver_Name(t *testing.T) {
 | 
			
		||||
	port, _ := rand.Int(rand.Reader, big.NewInt(50000))
 | 
			
		||||
	port = port.Add(port, big.NewInt(15534))
 | 
			
		||||
	solver := New(port.String())
 | 
			
		||||
	assert.Equal(t, "example", solver.Name())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestExampleSolver_Initialize(t *testing.T) {
 | 
			
		||||
	port, _ := rand.Int(rand.Reader, big.NewInt(50000))
 | 
			
		||||
	port = port.Add(port, big.NewInt(15534))
 | 
			
		||||
	solver := New(port.String())
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	err := solver.Initialize(nil, done)
 | 
			
		||||
	assert.NoError(t, err, "Expected Initialize not to error")
 | 
			
		||||
	close(done)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestExampleSolver_Present_Cleanup(t *testing.T) {
 | 
			
		||||
	port, _ := rand.Int(rand.Reader, big.NewInt(50000))
 | 
			
		||||
	port = port.Add(port, big.NewInt(15534))
 | 
			
		||||
	solver := New(port.String())
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	err := solver.Initialize(nil, done)
 | 
			
		||||
	assert.NoError(t, err, "Expected Initialize not to error")
 | 
			
		||||
 | 
			
		||||
	validTestData := []struct {
 | 
			
		||||
		hostname string
 | 
			
		||||
		record   string
 | 
			
		||||
	}{
 | 
			
		||||
		{"test1.example.com.", "testkey1"},
 | 
			
		||||
		{"test2.example.com.", "testkey2"},
 | 
			
		||||
		{"test3.example.com.", "testkey3"},
 | 
			
		||||
	}
 | 
			
		||||
	for _, test := range validTestData {
 | 
			
		||||
		err := solver.Present(&acme.ChallengeRequest{
 | 
			
		||||
			Action:       acme.ChallengeActionPresent,
 | 
			
		||||
			Type:         "dns-01",
 | 
			
		||||
			ResolvedFQDN: test.hostname,
 | 
			
		||||
			Key:          test.record,
 | 
			
		||||
		})
 | 
			
		||||
		assert.NoError(t, err, "Unexpected error while presenting %v", t)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Resolve test data
 | 
			
		||||
	for _, test := range validTestData {
 | 
			
		||||
		msg := new(dns.Msg)
 | 
			
		||||
		msg.Id = dns.Id()
 | 
			
		||||
		msg.RecursionDesired = true
 | 
			
		||||
		msg.Question = make([]dns.Question, 1)
 | 
			
		||||
		msg.Question[0] = dns.Question{dns.Fqdn(test.hostname), dns.TypeTXT, dns.ClassINET}
 | 
			
		||||
		in, err := dns.Exchange(msg, "127.0.0.1:"+port.String())
 | 
			
		||||
 | 
			
		||||
		assert.NoError(t, err, "Presented record %s not resolvable", test.hostname)
 | 
			
		||||
		assert.Len(t, in.Answer, 1, "RR response is of incorrect length")
 | 
			
		||||
		assert.Equal(t, []string{test.record}, in.Answer[0].(*dns.TXT).Txt, "TXT record returned did not match presented record")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Cleanup test data
 | 
			
		||||
	for _, test := range validTestData {
 | 
			
		||||
		err := solver.CleanUp(&acme.ChallengeRequest{
 | 
			
		||||
			Action:       acme.ChallengeActionCleanUp,
 | 
			
		||||
			Type:         "dns-01",
 | 
			
		||||
			ResolvedFQDN: test.hostname,
 | 
			
		||||
			Key:          test.record,
 | 
			
		||||
		})
 | 
			
		||||
		assert.NoError(t, err, "Unexpected error while cleaning up %v", t)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Resolve test data
 | 
			
		||||
	for _, test := range validTestData {
 | 
			
		||||
		msg := new(dns.Msg)
 | 
			
		||||
		msg.Id = dns.Id()
 | 
			
		||||
		msg.RecursionDesired = true
 | 
			
		||||
		msg.Question = make([]dns.Question, 1)
 | 
			
		||||
		msg.Question[0] = dns.Question{dns.Fqdn(test.hostname), dns.TypeTXT, dns.ClassINET}
 | 
			
		||||
		in, err := dns.Exchange(msg, "127.0.0.1:"+port.String())
 | 
			
		||||
 | 
			
		||||
		assert.NoError(t, err, "Presented record %s not resolvable", test.hostname)
 | 
			
		||||
		assert.Len(t, in.Answer, 0, "RR response is of incorrect length")
 | 
			
		||||
		assert.Equal(t, dns.RcodeNameError, in.Rcode, "Expexted NXDOMAIN")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	close(done)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user