From 3adafb7edb8aeed25cb471c7fb2306482b46a14f Mon Sep 17 00:00:00 2001 From: Michael Bolot Date: Wed, 19 Oct 2022 08:40:06 -0500 Subject: [PATCH] Tests for Marking additional connections as ready --- listener_test.go | 225 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 listener_test.go diff --git a/listener_test.go b/listener_test.go new file mode 100644 index 0000000..92f0087 --- /dev/null +++ b/listener_test.go @@ -0,0 +1,225 @@ +package dynamiclistener + +import ( + "crypto/tls" + "net" + "testing" + "time" + + "github.com/rancher/dynamiclistener/factory" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + apiError "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func Test_getCertificate(t *testing.T) { + beforeKey, beforeCert, err := newCertificate() + assert.NoError(t, err, "Error when setting up test - unable to construct before key for test") + beforeTLSCert, err := tls.X509KeyPair(beforeCert, beforeKey) + assert.NoError(t, err, "Error when setting up test - unable to convert before to tls.Certificate") + afterKey, afterCert, err := newCertificate() + assert.NoError(t, err, "Error when setting up test - unable to construct after key for test") + afterTLSCert, err := tls.X509KeyPair(afterCert, afterKey) + assert.NoError(t, err, "Error when setting up test - unable to convert after to tls.Certificate") + tests := []struct { + // input test vars + name string + secret *v1.Secret + secretErr error + cachedCert *tls.Certificate + cachedVersion string + currentConn *closeWrapper + otherConns map[int]*closeWrapper + + // output/result test vars + closedConns []int + expectedCert *tls.Certificate + wantError bool + }{ + { + name: "no secret found", + secret: nil, + secretErr: apiError.NewNotFound(schema.GroupResource{ + Group: "", + Resource: "Secret", + }, "testSecret"), + currentConn: &closeWrapper{id: 0}, + otherConns: map[int]*closeWrapper{}, + + expectedCert: nil, + wantError: true, + }, + { + name: "secret found, and is up to date", + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "1", + Name: "testSecret", + Namespace: "test", + }, + Data: map[string][]byte{ + v1.TLSCertKey: beforeCert, + v1.TLSPrivateKeyKey: beforeKey, + }, + }, + cachedVersion: "1", + cachedCert: &beforeTLSCert, + currentConn: &closeWrapper{id: 0}, + otherConns: map[int]*closeWrapper{}, + + expectedCert: &beforeTLSCert, + wantError: false, + }, + { + name: "secret found, is not up to date, but k8s secret is not valid", + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "2", + Name: "testSecret", + Namespace: "test", + }, + Data: map[string][]byte{ + v1.TLSPrivateKeyKey: []byte("strawberry"), + }, + }, + cachedVersion: "1", + cachedCert: &beforeTLSCert, + currentConn: &closeWrapper{id: 0}, + otherConns: map[int]*closeWrapper{}, + + expectedCert: &beforeTLSCert, + wantError: false, + }, + { + name: "secret found, but is not up to date", + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "2", + Name: "testSecret", + Namespace: "test", + }, + Data: map[string][]byte{ + v1.TLSCertKey: afterCert, + v1.TLSPrivateKeyKey: afterKey, + }, + }, + cachedVersion: "1", + cachedCert: &beforeTLSCert, + currentConn: &closeWrapper{id: 0}, + otherConns: map[int]*closeWrapper{}, + + expectedCert: &afterTLSCert, + wantError: false, + }, + { + name: "secret found, is not up to date, and we have conns using current cert", + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "2", + Name: "testSecret", + Namespace: "test", + }, + Data: map[string][]byte{ + v1.TLSCertKey: afterCert, + v1.TLSPrivateKeyKey: afterKey, + }, + }, + cachedVersion: "1", + cachedCert: &beforeTLSCert, + currentConn: &closeWrapper{id: 0}, + otherConns: map[int]*closeWrapper{ + 1: { + id: 1, + ready: false, + Conn: &fakeConn{}, + }, + 2: { + id: 2, + ready: true, + Conn: &fakeConn{}, + }, + }, + + closedConns: []int{2}, + expectedCert: &afterTLSCert, + wantError: false, + }, + } + for i := range tests { + test := tests[i] + t.Run(test.name, func(t *testing.T) { + t.Parallel() + testConns := test.otherConns + if testConns != nil { + testConns[test.currentConn.id] = test.currentConn + // make sure our conn is listed as one of the current connections + } + l := listener{ + cert: test.cachedCert, + version: test.cachedVersion, + storage: &MockTLSStorage{ + Secret: test.secret, + SecretErr: test.secretErr, + }, + conns: testConns, + } + for _, conn := range testConns { + conn.l = &l + } + newCert, err := l.getCertificate(&tls.ClientHelloInfo{Conn: test.currentConn}) + if test.wantError { + assert.Errorf(t, err, "expected an error but none was provdied") + } else { + assert.NoError(t, err, "did not expect an error but got one") + } + assert.Equal(t, test.expectedCert, newCert, "expected cert did not match actual cert") + if test.expectedCert != nil && test.wantError == false && test.currentConn != nil && test.otherConns != nil { + assert.True(t, test.currentConn.ready, "expected connection to be ready but it was not") + } else { + if test.currentConn != nil { + assert.False(t, test.currentConn.ready, "did not expect connection to be ready") + } + } + for _, closedConn := range test.closedConns { + _, ok := l.conns[closedConn] + assert.False(t, ok, "closed conns should not be found") + } + }) + } +} + +func newCertificate() ([]byte, []byte, error) { + cert, key, err := factory.GenCA() + if err != nil { + return nil, nil, err + } + + return factory.MarshalChain(key, cert) +} + +type MockTLSStorage struct { + Secret *v1.Secret + SecretErr error +} + +func (m *MockTLSStorage) Get() (*v1.Secret, error) { + return m.Secret, m.SecretErr +} + +func (m *MockTLSStorage) Update(secret *v1.Secret) error { + panic("Not implemented") +} + +// adapted from k8s.io/apimachinery@v0.18.8/pkg/util.proxy/ugradeaware_test.go +type fakeConn struct{} + +func (f *fakeConn) Read([]byte) (int, error) { return 0, nil } +func (f *fakeConn) Write([]byte) (int, error) { return 0, nil } +func (f *fakeConn) Close() error { return nil } +func (fakeConn) LocalAddr() net.Addr { return nil } +func (fakeConn) RemoteAddr() net.Addr { return nil } +func (fakeConn) SetDeadline(t time.Time) error { return nil } +func (fakeConn) SetReadDeadline(t time.Time) error { return nil } +func (fakeConn) SetWriteDeadline(t time.Time) error { return nil }