Add example server that passes the test fixtures
Signed-off-by: Jake Sanders <i@am.so-aweso.me>
This commit is contained in:
		
							parent
							
								
									9b61d24773
								
							
						
					
					
						commit
						4f51af7d86
					
				| @ -1 +1,59 @@ | |||||||
| package example | 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 | 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 | 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) | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										20
									
								
								main_test.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								main_test.go
									
									
									
									
									
								
							| @ -5,6 +5,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jetstack/cert-manager/test/acme/dns" | 	"github.com/jetstack/cert-manager/test/acme/dns" | ||||||
|  | 
 | ||||||
|  | 	"github.com/jetstack/cert-manager-webhook-example/example" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| @ -15,11 +17,23 @@ func TestRunsSuite(t *testing.T) { | |||||||
| 	// The manifest path should contain a file named config.json that is a | 	// The manifest path should contain a file named config.json that is a | ||||||
| 	// snippet of valid configuration that should be included on the | 	// snippet of valid configuration that should be included on the | ||||||
| 	// ChallengeRequest passed as part of the test cases. | 	// ChallengeRequest passed as part of the test cases. | ||||||
|  | 	// | ||||||
| 
 | 
 | ||||||
| 	fixture := dns.NewFixture(&customDNSProviderSolver{}, | 	// Uncomment the below fixture when implementing your custom DNS provider | ||||||
| 		dns.SetResolvedZone(zone), | 	//fixture := dns.NewFixture(&customDNSProviderSolver{}, | ||||||
| 		dns.SetAllowAmbientCredentials(false), | 	//	dns.SetResolvedZone(zone), | ||||||
|  | 	//	dns.SetAllowAmbientCredentials(false), | ||||||
|  | 	//	dns.SetManifestPath("testdata/my-custom-solver"), | ||||||
|  | 	//	dns.SetBinariesPath("_test/kubebuilder/bin"), | ||||||
|  | 	//) | ||||||
|  | 
 | ||||||
|  | 	solver := example.New("59351") | ||||||
|  | 	fixture := dns.NewFixture(solver, | ||||||
|  | 		dns.SetResolvedZone("example.com."), | ||||||
| 		dns.SetManifestPath("testdata/my-custom-solver"), | 		dns.SetManifestPath("testdata/my-custom-solver"), | ||||||
|  | 		dns.SetBinariesPath("_test/kubebuilder/bin"), | ||||||
|  | 		dns.SetDNSServer("127.0.0.1:59351"), | ||||||
|  | 		dns.SetUseAuthoritative(false), | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	fixture.RunConformance(t) | 	fixture.RunConformance(t) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user