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 | ||||
| 
 | ||||
| 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) | ||||
| } | ||||
|  | ||||
							
								
								
									
										20
									
								
								main_test.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								main_test.go
									
									
									
									
									
								
							| @ -5,6 +5,8 @@ import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/jetstack/cert-manager/test/acme/dns" | ||||
| 
 | ||||
| 	"github.com/jetstack/cert-manager-webhook-example/example" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -15,11 +17,23 @@ func TestRunsSuite(t *testing.T) { | ||||
| 	// The manifest path should contain a file named config.json that is a | ||||
| 	// snippet of valid configuration that should be included on the | ||||
| 	// ChallengeRequest passed as part of the test cases. | ||||
| 	// | ||||
| 
 | ||||
| 	fixture := dns.NewFixture(&customDNSProviderSolver{}, | ||||
| 		dns.SetResolvedZone(zone), | ||||
| 		dns.SetAllowAmbientCredentials(false), | ||||
| 	// Uncomment the below fixture when implementing your custom DNS provider | ||||
| 	//fixture := dns.NewFixture(&customDNSProviderSolver{}, | ||||
| 	//	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.SetBinariesPath("_test/kubebuilder/bin"), | ||||
| 		dns.SetDNSServer("127.0.0.1:59351"), | ||||
| 		dns.SetUseAuthoritative(false), | ||||
| 	) | ||||
| 
 | ||||
| 	fixture.RunConformance(t) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user