diff --git a/transport/stunnel/server.go b/transport/stunnel/server.go index b3ef2c9..8577179 100644 --- a/transport/stunnel/server.go +++ b/transport/stunnel/server.go @@ -11,7 +11,6 @@ import ( "github.com/backube/pvc-transfer/transport" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -58,7 +57,7 @@ type server struct { namespacedName types.NamespacedName } -func New(ctx context.Context, c ctrlclient.Client, logger logr.Logger, +func NewServer(ctx context.Context, c ctrlclient.Client, logger logr.Logger, namespacedName types.NamespacedName, e endpoint.Endpoint, options *transport.Options) (transport.Transport, error) { @@ -145,23 +144,22 @@ func (s *server) MarkForCleanup(ctx context.Context, c ctrlclient.Client, key, v } func (s *server) reconcileConfig(ctx context.Context, c ctrlclient.Client) error { + stunnelConfTemplate, err := template.New("config").Parse(stunnelServerConfTemplate) + if err != nil { + s.logger.Error(err, "unable to parse stunnel server config template") + return err + } + ports := map[string]string{ // acceptPort on which Stunnel service listens on, must connect with endpoint "acceptPort": strconv.Itoa(int(s.ListenPort())), // connectPort in the container on which Transfer is listening on "connectPort": strconv.Itoa(int(s.ConnectPort())), } - var stunnelConf bytes.Buffer - stunnelConfTemplate, err := template.New("config").Parse(stunnelServerConfTemplate) - if err != nil { - s.logger.Error(err, "unable to parse stunnel config template") - return err - } - err = stunnelConfTemplate.Execute(&stunnelConf, ports) if err != nil { - s.logger.Error(err, "unable to execute stunnel config template") + s.logger.Error(err, "unable to execute stunnel server config template") return err } @@ -189,7 +187,7 @@ func (s *server) prefixedName(name string) string { } func (s *server) reconcileSecret(ctx context.Context, c ctrlclient.Client) error { - _, _, found, err := s.getExistingCert(ctx, c) + _, _, found, err := getExistingCert(ctx, c, s.logger, s.namespacedName, s.secretNameSuffix()) if found { return nil } @@ -208,7 +206,7 @@ func (s *server) reconcileSecret(ctx context.Context, c ctrlclient.Client) error stunnelSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: s.NamespacedName().Namespace, - Name: s.prefixedName(stunnelSecret), + Name: getResourceName(s.namespacedName, s.secretNameSuffix()), }, } @@ -225,38 +223,8 @@ func (s *server) reconcileSecret(ctx context.Context, c ctrlclient.Client) error return err } -func (s *server) getExistingCert(ctx context.Context, c ctrlclient.Client) (*bytes.Buffer, *bytes.Buffer, bool, error) { - serverSecret := &corev1.Secret{} - err := c.Get(ctx, types.NamespacedName{ - Namespace: s.namespacedName.Namespace, - Name: s.prefixedName(stunnelSecret), - }, serverSecret) - switch { - case k8serrors.IsNotFound(err): - return nil, nil, false, nil - case err != nil: - return nil, nil, false, err - } - - key, ok := serverSecret.Data["tls.key"] - if !ok { - s.logger.Info("stunnel server secret data missing key tls.key", "secret", types.NamespacedName{ - Namespace: s.namespacedName.Namespace, - Name: s.prefixedName(stunnelSecret), - }) - return nil, nil, false, nil - } - - crt, ok := serverSecret.Data["tls.crt"] - if !ok { - s.logger.Info("stunnel server secret data missing key tls.crt", "secret", types.NamespacedName{ - Namespace: s.namespacedName.Namespace, - Name: s.prefixedName(stunnelSecret), - }) - return nil, nil, false, nil - } - - return bytes.NewBuffer(key), bytes.NewBuffer(crt), true, nil +func (s *server) secretNameSuffix() string { + return "server-" + stunnelSecret } func (s *server) serverContainers() []corev1.Container { @@ -282,7 +250,7 @@ func (s *server) serverContainers() []corev1.Container { SubPath: "stunnel.conf", }, { - Name: s.prefixedName(stunnelSecret), + Name: getResourceName(s.namespacedName, s.secretNameSuffix()), MountPath: "/etc/stunnel/certs", }, }, @@ -303,10 +271,10 @@ func (s *server) serverVolumes() []corev1.Volume { }, }, { - Name: s.prefixedName(stunnelSecret), + Name: getResourceName(s.namespacedName, s.secretNameSuffix()), VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: s.prefixedName(stunnelSecret), + SecretName: getResourceName(s.namespacedName, s.secretNameSuffix()), Items: []corev1.KeyToPath{ { Key: "tls.crt", diff --git a/transport/stunnel/server_test.go b/transport/stunnel/server_test.go index 7d8f022..6ce9b7a 100644 --- a/transport/stunnel/server_test.go +++ b/transport/stunnel/server_test.go @@ -72,7 +72,7 @@ func newFakeEndpoint() endpoint.Endpoint { } } -func TestNew(t *testing.T) { +func TestNewServer(t *testing.T) { tests := []struct { name string namespacedName types.NamespacedName @@ -165,9 +165,9 @@ func TestNew(t *testing.T) { fakeClient := fakeClientWithObjects(tt.objects...) ctx := context.WithValue(context.Background(), "test", tt.name) fakeLogger := logrtesting.TestLogger{t} - stunnelServer, err := New(ctx, fakeClient, fakeLogger, tt.namespacedName, tt.endpoint, &transport.Options{Labels: tt.labels, Owners: tt.ownerReferences}) + stunnelServer, err := NewServer(ctx, fakeClient, fakeLogger, tt.namespacedName, tt.endpoint, &transport.Options{Labels: tt.labels, Owners: tt.ownerReferences}) if (err != nil) != tt.wantErr { - t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("NewServer() error = %v, wantErr %v", err, tt.wantErr) return } cm := &corev1.ConfigMap{} @@ -299,105 +299,3 @@ func Test_server_MarkForCleanup(t *testing.T) { }) } } - -func Test_server_getExistingCert(t *testing.T) { - tests := []struct { - name string - namespacedName types.NamespacedName - labels map[string]string - wantErr bool - wantFound bool - objects []ctrlclient.Object - }{ - { - name: "test with no secret", - namespacedName: types.NamespacedName{Namespace: "bar", Name: "foo"}, - labels: map[string]string{"test": "me"}, - wantErr: false, - wantFound: false, - objects: []ctrlclient.Object{}, - }, - { - name: "test with invalid secret, key missing", - namespacedName: types.NamespacedName{Namespace: "bar", Name: "foo"}, - labels: map[string]string{"test": "me"}, - wantErr: false, - wantFound: false, - objects: []ctrlclient.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo-server-stunnel-credentials", - Namespace: "bar", - Labels: map[string]string{"test": "me"}, - }, - Data: map[string][]byte{"tls.crt": []byte(`crt`)}, - }, - }, - }, - { - name: "test with invalid secret, crt missing", - namespacedName: types.NamespacedName{Namespace: "bar", Name: "foo"}, - labels: map[string]string{"test": "me"}, - wantErr: false, - wantFound: false, - objects: []ctrlclient.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo-server-stunnel-credentials", - Namespace: "bar", - Labels: map[string]string{"test": "me"}, - }, - Data: map[string][]byte{"tls.key": []byte(`key`)}, - }, - }, - }, - { - name: "test with valid secret", - namespacedName: types.NamespacedName{Namespace: "bar", Name: "foo"}, - labels: map[string]string{"test": "me"}, - wantErr: false, - wantFound: true, - objects: []ctrlclient.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo-server-stunnel-credentials", - Namespace: "bar", - Labels: map[string]string{"test": "me"}, - }, - Data: map[string][]byte{"tls.key": []byte(`key`), "tls.crt": []byte(`crt`)}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := &server{ - logger: logrtesting.TestLogger{t}, - namespacedName: tt.namespacedName, - options: &transport.Options{ - Labels: tt.labels, - Owners: testOwnerReferences(), - }, - } - ctx := context.WithValue(context.Background(), "test", tt.name) - key, crt, found, err := s.getExistingCert(ctx, fakeClientWithObjects(tt.objects...)) - if err != nil { - t.Error("found unexpected error", err) - } - if !tt.wantFound && found { - t.Error("found unexpected secret") - } - if tt.wantFound && !found { - t.Error("not found unexpected") - } - - if tt.wantFound && found && key == nil { - t.Error("secret found but empty key, unexpected") - } - - if tt.wantFound && found && crt == nil { - t.Error("secret found but empty crt, unexpected") - } - }) - } -} diff --git a/transport/stunnel/stunnel.go b/transport/stunnel/stunnel.go index 7696c8a..dfc13ba 100644 --- a/transport/stunnel/stunnel.go +++ b/transport/stunnel/stunnel.go @@ -1,6 +1,16 @@ package stunnel -import "github.com/backube/pvc-transfer/transport" +import ( + "bytes" + "context" + + "github.com/backube/pvc-transfer/transport" + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) const ( defaultStunnelImage = "quay.io/konveyor/rsync-transfer:latest" @@ -20,3 +30,41 @@ func getImage(options *transport.Options) string { return options.Image } } + +func getResourceName(obj types.NamespacedName, suffix string) string { + return obj.Name + "-" + suffix +} + +func getExistingCert(ctx context.Context, c ctrlclient.Client, logger logr.Logger, secretName types.NamespacedName, suffix string) (*bytes.Buffer, *bytes.Buffer, bool, error) { + secret := &corev1.Secret{} + err := c.Get(ctx, types.NamespacedName{ + Namespace: secretName.Namespace, + Name: getResourceName(secretName, suffix), + }, secret) + switch { + case k8serrors.IsNotFound(err): + return nil, nil, false, nil + case err != nil: + return nil, nil, false, err + } + + key, ok := secret.Data["tls.key"] + if !ok { + logger.Info("secret data missing key tls.key", "secret", types.NamespacedName{ + Namespace: secretName.Namespace, + Name: getResourceName(secretName, suffix), + }) + return nil, nil, false, nil + } + + crt, ok := secret.Data["tls.crt"] + if !ok { + logger.Info("secret data missing key tls.crt", "secret", types.NamespacedName{ + Namespace: secretName.Namespace, + Name: getResourceName(secretName, suffix), + }) + return nil, nil, false, nil + } + + return bytes.NewBuffer(key), bytes.NewBuffer(crt), true, nil +} diff --git a/transport/stunnel/stunnel_test.go b/transport/stunnel/stunnel_test.go new file mode 100644 index 0000000..f0676f5 --- /dev/null +++ b/transport/stunnel/stunnel_test.go @@ -0,0 +1,115 @@ +package stunnel + +import ( + "context" + "testing" + + "github.com/backube/pvc-transfer/transport" + logrtesting "github.com/go-logr/logr/testing" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +func Test_getExistingCert(t *testing.T) { + tests := []struct { + name string + namespacedName types.NamespacedName + labels map[string]string + wantErr bool + wantFound bool + objects []ctrlclient.Object + }{ + { + name: "test with no secret", + namespacedName: types.NamespacedName{Namespace: "bar", Name: "foo"}, + labels: map[string]string{"test": "me"}, + wantErr: false, + wantFound: false, + objects: []ctrlclient.Object{}, + }, + { + name: "test with invalid secret, key missing", + namespacedName: types.NamespacedName{Namespace: "bar", Name: "foo"}, + labels: map[string]string{"test": "me"}, + wantErr: false, + wantFound: false, + objects: []ctrlclient.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-stunnel-credentials", + Namespace: "bar", + Labels: map[string]string{"test": "me"}, + }, + Data: map[string][]byte{"tls.crt": []byte(`crt`)}, + }, + }, + }, + { + name: "test with invalid secret, crt missing", + namespacedName: types.NamespacedName{Namespace: "bar", Name: "foo"}, + labels: map[string]string{"test": "me"}, + wantErr: false, + wantFound: false, + objects: []ctrlclient.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-stunnel-credentials", + Namespace: "bar", + Labels: map[string]string{"test": "me"}, + }, + Data: map[string][]byte{"tls.key": []byte(`key`)}, + }, + }, + }, + { + name: "test with valid secret", + namespacedName: types.NamespacedName{Namespace: "bar", Name: "foo"}, + labels: map[string]string{"test": "me"}, + wantErr: false, + wantFound: true, + objects: []ctrlclient.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-stunnel-credentials", + Namespace: "bar", + Labels: map[string]string{"test": "me"}, + }, + Data: map[string][]byte{"tls.key": []byte(`key`), "tls.crt": []byte(`crt`)}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &server{ + logger: logrtesting.TestLogger{t}, + namespacedName: tt.namespacedName, + options: &transport.Options{ + Labels: tt.labels, + Owners: testOwnerReferences(), + }, + } + ctx := context.WithValue(context.Background(), "test", tt.name) + key, crt, found, err := getExistingCert(ctx, fakeClientWithObjects(tt.objects...), s.logger, s.namespacedName, stunnelSecret) + if err != nil { + t.Error("found unexpected error", err) + } + if !tt.wantFound && found { + t.Error("found unexpected secret") + } + if tt.wantFound && !found { + t.Error("not found unexpected") + } + + if tt.wantFound && found && key == nil { + t.Error("secret found but empty key, unexpected") + } + + if tt.wantFound && found && crt == nil { + t.Error("secret found but empty crt, unexpected") + } + }) + } +}