mirror of
https://github.com/rancher/dynamiclistener.git
synced 2025-05-17 20:39:36 +00:00
226 lines
6.2 KiB
Go
226 lines
6.2 KiB
Go
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 }
|