package collect import ( "context" "math/rand" "testing" "github.com/replicatedhq/troubleshoot/internal/testutils" "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" testclient "k8s.io/client-go/kubernetes/fake" ) func Test_selectorToString(t *testing.T) { tests := []struct { name string selector []string expect string }{ { name: "app=api", selector: []string{"app=api"}, expect: "app-api", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { actual := selectorToString(test.selector) assert.Equal(t, test.expect, actual) }) } } func Test_DeterministicIDForCollector(t *testing.T) { tests := []struct { name string collector *troubleshootv1beta2.Collect expect string }{ { name: "cluster-info", collector: &troubleshootv1beta2.Collect{ ClusterInfo: &troubleshootv1beta2.ClusterInfo{}, }, expect: "cluster-info", }, { name: "cluster-resources", collector: &troubleshootv1beta2.Collect{ ClusterResources: &troubleshootv1beta2.ClusterResources{}, }, expect: "cluster-resources", }, { name: "secret", collector: &troubleshootv1beta2.Collect{ Secret: &troubleshootv1beta2.Secret{ Namespace: "top-secret", Name: "secret-agent-woman", }, }, expect: "secret-top-secret-secret-agent-woman", }, { name: "secret selector", collector: &troubleshootv1beta2.Collect{ Secret: &troubleshootv1beta2.Secret{ Namespace: "top-secret", Selector: []string{"this=is", "rather=long", "for=testing", "more=words", "too=many", "abcdef!=123456"}, }, }, expect: "secret-top-secret-this-is-rather-long-for-testing-more-words-to", }, { name: "configmap", collector: &troubleshootv1beta2.Collect{ ConfigMap: &troubleshootv1beta2.ConfigMap{ Namespace: "top-secret", Name: "secret-agent-woman", }, }, expect: "configmap-top-secret-secret-agent-woman", }, { name: "configmap selector", collector: &troubleshootv1beta2.Collect{ ConfigMap: &troubleshootv1beta2.ConfigMap{ Namespace: "top-secret", Selector: []string{"this=is", "rather=long", "for=testing", "more=words", "too=many", "abcdef!=123456"}, }, }, expect: "configmap-top-secret-this-is-rather-long-for-testing-more-words", }, { name: "logs", collector: &troubleshootv1beta2.Collect{ Logs: &troubleshootv1beta2.Logs{ Namespace: "top-secret", Selector: []string{"this=is", "rather=long", "for=testing", "more=words", "too=many", "abcdef!=123456"}, }, }, expect: "logs-top-secret-this-is-rather-long-for-testing-more-words-too-", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { actual := DeterministicIDForCollector(test.collector) assert.Equal(t, test.expect, actual) }) } } func Test_createTLSConfig(t *testing.T) { k8sClient := testclient.NewSimpleClientset() tests := []struct { name string tlsParams v1beta2.TLSParams caCertOnly bool hasError bool }{ { name: "complete tls params creates config successfully", tlsParams: v1beta2.TLSParams{ CACert: testutils.GetTestFixture(t, "db/ca.pem"), ClientCert: testutils.GetTestFixture(t, "db/client.pem"), ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"), }, }, { name: "complete tls params in secret creates config successfully", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ "cacert": testutils.GetTestFixture(t, "db/ca.pem"), "clientCert": testutils.GetTestFixture(t, "db/client.pem"), "clientKey": testutils.GetTestFixture(t, "db/client-key.pem"), }), }, }, { name: "tls params with skip verify creates config successfully", tlsParams: v1beta2.TLSParams{ SkipVerify: true, }, }, { name: "tls params with CA cert only in secret creates config successfully", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ "cacert": testutils.GetTestFixture(t, "db/ca.pem"), }), }, caCertOnly: true, }, { name: "tls params with CA cert only creates config successfully", tlsParams: v1beta2.TLSParams{ CACert: testutils.GetTestFixture(t, "db/ca.pem"), }, caCertOnly: true, }, { name: "empty TLS parameters fails to create config with error", hasError: true, }, { name: "missing CA cert fails to create config with error", tlsParams: v1beta2.TLSParams{ ClientCert: testutils.GetTestFixture(t, "db/client.pem"), ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"), }, hasError: true, }, { name: "missing client cert fails to create config with error", tlsParams: v1beta2.TLSParams{ CACert: testutils.GetTestFixture(t, "db/ca.pem"), ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"), }, hasError: true, }, { name: "missing client key fails to create config with error", tlsParams: v1beta2.TLSParams{ CACert: testutils.GetTestFixture(t, "db/ca.pem"), ClientCert: testutils.GetTestFixture(t, "db/client.pem"), }, hasError: true, }, { name: "missing CA cert in secret fails to create config with error", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ "clientCert": testutils.GetTestFixture(t, "db/client.pem"), "clientKey": testutils.GetTestFixture(t, "db/client-key.pem"), }), }, hasError: true, }, { name: "missing client cert in secret fails to create config with error", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ "cacert": testutils.GetTestFixture(t, "db/ca.pem"), "clientKey": testutils.GetTestFixture(t, "db/client-key.pem"), }), }, hasError: true, }, { name: "missing client key in secret fails to create config with error", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ "cacert": testutils.GetTestFixture(t, "db/ca.pem"), "clientCert": testutils.GetTestFixture(t, "db/client.pem"), }), }, hasError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tlsCfg, err := createTLSConfig(context.Background(), k8sClient, &tt.tlsParams) assert.Equalf(t, tt.hasError, err != nil, "createTLSConfig() error = %v, wantErr %v", err, tt.hasError) if err == nil { require.NotNil(t, tlsCfg) if tt.tlsParams.SkipVerify { assert.True(t, tlsCfg.InsecureSkipVerify) assert.Nil(t, tlsCfg.RootCAs) assert.Nil(t, tlsCfg.Certificates) } else { // TLS parameter objects are opaque. Just check if they were created. // There is no trivial way to inspect their metadata. Trust me :) assert.NotNil(t, tlsCfg.RootCAs) assert.Equal(t, tt.caCertOnly, tlsCfg.Certificates == nil) assert.False(t, tlsCfg.InsecureSkipVerify) } } else { t.Log(err) assert.Nil(t, tlsCfg) } }) } } func randStringRunes(n int) string { runes := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") b := make([]rune, n) for i := range b { b[i] = runes[rand.Intn(len(runes))] } return string(b) } // createTLSSecret create a secret in a fake client func createTLSSecret(t *testing.T, client kubernetes.Interface, secretData map[string]string) *v1beta2.TLSSecret { t.Helper() // Generate unique names cause we reuse the same client secretName := "secret-name-" + randStringRunes(20) namespace := "namespace-" + randStringRunes(20) data := make(map[string][]byte) for k, v := range secretData { data[k] = []byte(v) } _, err := client.CoreV1().Secrets(namespace).Create( context.Background(), &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, }, Data: data, }, metav1.CreateOptions{}, ) require.NoError(t, err) return &v1beta2.TLSSecret{ Namespace: namespace, Name: secretName, } } func Test_checkForExistingServiceAccount(t *testing.T) { tests := []struct { name string namespace string serviceAccountName string mockServiceAccount *corev1.ServiceAccount wantErr bool }{ { name: "Service account doesn't exist", namespace: "test-namespace", serviceAccountName: "test-service-account", mockServiceAccount: nil, wantErr: true, }, { name: "Service account already exists", namespace: "test-namespace", serviceAccountName: "test-service-account", mockServiceAccount: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "test-service-account", }, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() client := testclient.NewSimpleClientset() if tt.mockServiceAccount != nil { _, err := client.CoreV1().ServiceAccounts(tt.namespace).Create(ctx, tt.mockServiceAccount, metav1.CreateOptions{}) require.NoError(t, err) err = checkForExistingServiceAccount(ctx, client, tt.namespace, tt.serviceAccountName) assert.Equal(t, tt.wantErr, err != nil) } err := checkForExistingServiceAccount(ctx, client, tt.namespace, tt.serviceAccountName) assert.Equal(t, tt.wantErr, err != nil) }) } }