github.com/argoproj/argo-cd/v3@v3.2.1/util/db/repository_secrets_test.go (about)

     1  package db
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"google.golang.org/grpc/codes"
    12  	"google.golang.org/grpc/status"
    13  	corev1 "k8s.io/api/core/v1"
    14  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/runtime"
    17  	"k8s.io/apimachinery/pkg/runtime/schema"
    18  	"k8s.io/apimachinery/pkg/watch"
    19  	"k8s.io/client-go/kubernetes/fake"
    20  	k8stesting "k8s.io/client-go/testing"
    21  
    22  	"github.com/argoproj/argo-cd/v3/common"
    23  	appsv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    24  	"github.com/argoproj/argo-cd/v3/util/settings"
    25  )
    26  
    27  func TestSecretsRepositoryBackend_CreateRepository(t *testing.T) {
    28  	t.Parallel()
    29  
    30  	type fixture struct {
    31  		clientSet   *fake.Clientset
    32  		repoBackend *secretsRepositoryBackend
    33  	}
    34  	repo := &appsv1.Repository{
    35  		Name:                  "ArgoCD",
    36  		Repo:                  "git@github.com:argoproj/argo-cd.git",
    37  		Username:              "someUsername",
    38  		Password:              "somePassword",
    39  		InsecureIgnoreHostKey: false,
    40  		EnableLFS:             true,
    41  	}
    42  	setupWithK8sObjects := func(objects ...runtime.Object) *fixture {
    43  		clientset := getClientset(objects...)
    44  		settingsMgr := settings.NewSettingsManager(t.Context(), clientset, testNamespace)
    45  		repoBackend := &secretsRepositoryBackend{db: &db{
    46  			ns:            testNamespace,
    47  			kubeclientset: clientset,
    48  			settingsMgr:   settingsMgr,
    49  		}}
    50  		return &fixture{
    51  			clientSet:   clientset,
    52  			repoBackend: repoBackend,
    53  		}
    54  	}
    55  	t.Run("will create repository successfully", func(t *testing.T) {
    56  		// given
    57  		t.Parallel()
    58  		f := setupWithK8sObjects()
    59  
    60  		// when
    61  		output, err := f.repoBackend.CreateRepository(t.Context(), repo)
    62  
    63  		// then
    64  		require.NoError(t, err)
    65  		assert.Same(t, repo, output)
    66  
    67  		secret, err := f.clientSet.CoreV1().Secrets(testNamespace).Get(
    68  			t.Context(),
    69  			RepoURLToSecretName(repoSecretPrefix, repo.Repo, ""),
    70  			metav1.GetOptions{},
    71  		)
    72  		assert.NotNil(t, secret)
    73  		require.NoError(t, err)
    74  
    75  		assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy])
    76  		assert.Equal(t, common.LabelValueSecretTypeRepository, secret.Labels[common.LabelKeySecretType])
    77  
    78  		assert.Equal(t, repo.Name, string(secret.Data["name"]))
    79  		assert.Equal(t, repo.Repo, string(secret.Data["url"]))
    80  		assert.Equal(t, repo.Username, string(secret.Data["username"]))
    81  		assert.Equal(t, repo.Password, string(secret.Data["password"]))
    82  		assert.Empty(t, string(secret.Data["insecureIgnoreHostKey"]))
    83  		assert.Equal(t, strconv.FormatBool(repo.EnableLFS), string(secret.Data["enableLfs"]))
    84  	})
    85  	t.Run("will return proper error if secret does not have expected label", func(t *testing.T) {
    86  		// given
    87  		t.Parallel()
    88  		secret := &corev1.Secret{}
    89  		s := secretsRepositoryBackend{}
    90  		updatedSecret := s.repositoryToSecret(repo, secret)
    91  		delete(updatedSecret.Labels, common.LabelKeySecretType)
    92  		f := setupWithK8sObjects(updatedSecret)
    93  		f.clientSet.ReactionChain = nil
    94  		f.clientSet.AddReactor("create", "secrets", func(_ k8stesting.Action) (handled bool, ret runtime.Object, err error) {
    95  			gr := schema.GroupResource{
    96  				Group:    "v1",
    97  				Resource: "secrets",
    98  			}
    99  			return true, nil, apierrors.NewAlreadyExists(gr, "already exists")
   100  		})
   101  
   102  		// when
   103  		output, err := f.repoBackend.CreateRepository(t.Context(), repo)
   104  
   105  		// then
   106  		require.Error(t, err)
   107  		assert.Nil(t, output)
   108  		status, ok := status.FromError(err)
   109  		assert.True(t, ok)
   110  		assert.Equal(t, codes.InvalidArgument, status.Code())
   111  	})
   112  	t.Run("will return proper error if secret already exists", func(t *testing.T) {
   113  		// given
   114  		t.Parallel()
   115  		secName := RepoURLToSecretName(repoSecretPrefix, repo.Repo, "")
   116  		secret := &corev1.Secret{
   117  			TypeMeta: metav1.TypeMeta{
   118  				Kind:       "Secret",
   119  				APIVersion: "v1",
   120  			},
   121  			ObjectMeta: metav1.ObjectMeta{
   122  				Name:      secName,
   123  				Namespace: "default",
   124  			},
   125  		}
   126  		s := secretsRepositoryBackend{}
   127  		updatedSecret := s.repositoryToSecret(repo, secret)
   128  		f := setupWithK8sObjects(updatedSecret)
   129  		f.clientSet.ReactionChain = nil
   130  		f.clientSet.WatchReactionChain = nil
   131  		f.clientSet.AddReactor("create", "secrets", func(_ k8stesting.Action) (handled bool, ret runtime.Object, err error) {
   132  			gr := schema.GroupResource{
   133  				Group:    "v1",
   134  				Resource: "secrets",
   135  			}
   136  			return true, nil, apierrors.NewAlreadyExists(gr, "already exists")
   137  		})
   138  		watcher := watch.NewFakeWithChanSize(1, true)
   139  		watcher.Add(updatedSecret)
   140  		f.clientSet.AddWatchReactor("secrets", func(_ k8stesting.Action) (handled bool, ret watch.Interface, err error) {
   141  			return true, watcher, nil
   142  		})
   143  
   144  		// when
   145  		output, err := f.repoBackend.CreateRepository(t.Context(), repo)
   146  
   147  		// then
   148  		require.Error(t, err)
   149  		assert.Nil(t, output)
   150  		status, ok := status.FromError(err)
   151  		assert.True(t, ok)
   152  		assert.Equal(t, codes.AlreadyExists, status.Code())
   153  	})
   154  }
   155  
   156  func TestSecretsRepositoryBackend_GetRepository(t *testing.T) {
   157  	repoSecrets := []runtime.Object{
   158  		&corev1.Secret{
   159  			ObjectMeta: metav1.ObjectMeta{
   160  				Namespace:   testNamespace,
   161  				Name:        RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj/argo-cd.git", ""),
   162  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   163  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   164  			},
   165  			Data: map[string][]byte{
   166  				"name":     []byte("ArgoCD"),
   167  				"url":      []byte("git@github.com:argoproj/argo-cd.git"),
   168  				"username": []byte("someUsername"),
   169  				"password": []byte("somePassword"),
   170  			},
   171  		},
   172  		&corev1.Secret{
   173  			ObjectMeta: metav1.ObjectMeta{
   174  				Namespace:   testNamespace,
   175  				Name:        RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj/argo-cd.git", "testProject"),
   176  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   177  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   178  			},
   179  			Data: map[string][]byte{
   180  				"name":     []byte("Scoped ArgoCD"),
   181  				"url":      []byte("git@github.com:argoproj/argo-cd.git"),
   182  				"username": []byte("someScopedUsername"),
   183  				"password": []byte("someScopedPassword"),
   184  				"project":  []byte("testProject"),
   185  			},
   186  		},
   187  		&corev1.Secret{
   188  			ObjectMeta: metav1.ObjectMeta{
   189  				Namespace: testNamespace,
   190  				Name:      "user-managed",
   191  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   192  			},
   193  			Data: map[string][]byte{
   194  				"name":     []byte("UserManagedRepo"),
   195  				"url":      []byte("git@github.com:argoproj/argoproj.git"),
   196  				"username": []byte("someOtherUsername"),
   197  				"password": []byte("someOtherPassword"),
   198  			},
   199  		},
   200  		&corev1.Secret{
   201  			ObjectMeta: metav1.ObjectMeta{
   202  				Namespace: testNamespace,
   203  				Name:      "other-user-managed",
   204  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   205  			},
   206  			Data: map[string][]byte{
   207  				"name":     []byte("Scoped UserManagedRepo"),
   208  				"url":      []byte("git@github.com:argoproj/argoproj.git"),
   209  				"username": []byte("someOtherUsername"),
   210  				"password": []byte("someOtherPassword"),
   211  				"project":  []byte("testProject"),
   212  			},
   213  		},
   214  	}
   215  
   216  	clientset := getClientset(repoSecrets...)
   217  	testee := &secretsRepositoryBackend{db: &db{
   218  		ns:            testNamespace,
   219  		kubeclientset: clientset,
   220  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   221  	}}
   222  
   223  	repository, err := testee.GetRepository(t.Context(), "git@github.com:argoproj/argo-cd.git", "")
   224  	require.NoError(t, err)
   225  	assert.NotNil(t, repository)
   226  	assert.Equal(t, "ArgoCD", repository.Name)
   227  	assert.Equal(t, "git@github.com:argoproj/argo-cd.git", repository.Repo)
   228  	assert.Equal(t, "someUsername", repository.Username)
   229  	assert.Equal(t, "somePassword", repository.Password)
   230  
   231  	repository, err = testee.GetRepository(t.Context(), "git@github.com:argoproj/argoproj.git", "")
   232  	require.NoError(t, err)
   233  	assert.NotNil(t, repository)
   234  	assert.Equal(t, "UserManagedRepo", repository.Name)
   235  	assert.Equal(t, "git@github.com:argoproj/argoproj.git", repository.Repo)
   236  	assert.Equal(t, "someOtherUsername", repository.Username)
   237  	assert.Equal(t, "someOtherPassword", repository.Password)
   238  
   239  	repository, err = testee.GetRepository(t.Context(), "git@github.com:argoproj/argo-cd.git", "testProject")
   240  	require.NoError(t, err)
   241  	assert.NotNil(t, repository)
   242  	assert.Equal(t, "Scoped ArgoCD", repository.Name)
   243  	assert.Equal(t, "git@github.com:argoproj/argo-cd.git", repository.Repo)
   244  	assert.Equal(t, "someScopedUsername", repository.Username)
   245  	assert.Equal(t, "someScopedPassword", repository.Password)
   246  	assert.Equal(t, "testProject", repository.Project)
   247  
   248  	repository, err = testee.GetRepository(t.Context(), "git@github.com:argoproj/argoproj.git", "testProject")
   249  	require.NoError(t, err)
   250  	assert.NotNil(t, repository)
   251  	assert.Equal(t, "Scoped UserManagedRepo", repository.Name)
   252  	assert.Equal(t, "git@github.com:argoproj/argoproj.git", repository.Repo)
   253  	assert.Equal(t, "someOtherUsername", repository.Username)
   254  	assert.Equal(t, "someOtherPassword", repository.Password)
   255  	assert.Equal(t, "testProject", repository.Project)
   256  }
   257  
   258  func TestSecretsRepositoryBackend_ListRepositories(t *testing.T) {
   259  	repoSecrets := []runtime.Object{
   260  		&corev1.Secret{
   261  			ObjectMeta: metav1.ObjectMeta{
   262  				Namespace:   testNamespace,
   263  				Name:        RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj/argo-cd.git", ""),
   264  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   265  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   266  			},
   267  			Data: map[string][]byte{
   268  				"name":     []byte("ArgoCD"),
   269  				"url":      []byte("git@github.com:argoproj/argo-cd.git"),
   270  				"username": []byte("someUsername"),
   271  				"password": []byte("somePassword"),
   272  			},
   273  		},
   274  		&corev1.Secret{
   275  			ObjectMeta: metav1.ObjectMeta{
   276  				Namespace: testNamespace,
   277  				Name:      "user-managed",
   278  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   279  			},
   280  			Data: map[string][]byte{
   281  				"name":     []byte("UserManagedRepo"),
   282  				"url":      []byte("git@github.com:argoproj/argoproj.git"),
   283  				"username": []byte("someOtherUsername"),
   284  				"password": []byte("someOtherPassword"),
   285  			},
   286  		},
   287  	}
   288  
   289  	clientset := getClientset(repoSecrets...)
   290  	testee := &secretsRepositoryBackend{db: &db{
   291  		ns:            testNamespace,
   292  		kubeclientset: clientset,
   293  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   294  	}}
   295  
   296  	repositories, err := testee.ListRepositories(t.Context(), nil)
   297  	require.NoError(t, err)
   298  	assert.Len(t, repositories, 2)
   299  
   300  	for _, repository := range repositories {
   301  		switch repository.Name {
   302  		case "ArgoCD":
   303  			assert.Equal(t, "git@github.com:argoproj/argo-cd.git", repository.Repo)
   304  			assert.Equal(t, "someUsername", repository.Username)
   305  			assert.Equal(t, "somePassword", repository.Password)
   306  		case "UserManagedRepo":
   307  			assert.Equal(t, "git@github.com:argoproj/argoproj.git", repository.Repo)
   308  			assert.Equal(t, "someOtherUsername", repository.Username)
   309  			assert.Equal(t, "someOtherPassword", repository.Password)
   310  		default:
   311  			assert.Fail(t, "unexpected repository found in list")
   312  		}
   313  	}
   314  }
   315  
   316  func TestSecretsRepositoryBackend_UpdateRepository(t *testing.T) {
   317  	managedRepository := &appsv1.Repository{
   318  		Name:     "Managed",
   319  		Repo:     "git@github.com:argoproj/argo-cd.git",
   320  		Username: "someUsername",
   321  		Password: "somePassword",
   322  	}
   323  	managedProjectRepository := &appsv1.Repository{
   324  		Name:     "Managed",
   325  		Repo:     "git@github.com:argoproj/argo-cd.git",
   326  		Username: "someUsername",
   327  		Password: "somePassword",
   328  		Project:  "someProject",
   329  	}
   330  	userProvidedRepository := &appsv1.Repository{
   331  		Name:     "User Provided",
   332  		Repo:     "git@github.com:argoproj/argoproj.git",
   333  		Username: "someOtherUsername",
   334  		Password: "someOtherPassword",
   335  	}
   336  	userProvidedProjectRepository := &appsv1.Repository{
   337  		Name:     "User Provided",
   338  		Repo:     "git@github.com:argoproj/argoproj.git",
   339  		Username: "someOtherUsername",
   340  		Password: "someOtherPassword",
   341  		Project:  "someProject",
   342  	}
   343  	newRepository := &appsv1.Repository{
   344  		Name:     "New",
   345  		Repo:     "git@github.com:argoproj/argo-events.git",
   346  		Username: "foo",
   347  		Password: "bar",
   348  	}
   349  
   350  	managedSecretName := RepoURLToSecretName(repoSecretPrefix, managedRepository.Repo, "")
   351  	managedProjectSecretName := RepoURLToSecretName(repoSecretPrefix, managedProjectRepository.Repo, "someProject")
   352  	newSecretName := RepoURLToSecretName(repoSecretPrefix, newRepository.Repo, "")
   353  	repoSecrets := []runtime.Object{
   354  		&corev1.Secret{
   355  			ObjectMeta: metav1.ObjectMeta{
   356  				Namespace:   testNamespace,
   357  				Name:        managedSecretName,
   358  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   359  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   360  			},
   361  			Data: map[string][]byte{
   362  				"name":     []byte(managedRepository.Name),
   363  				"url":      []byte(managedRepository.Repo),
   364  				"username": []byte(managedRepository.Username),
   365  				"password": []byte(managedRepository.Password),
   366  			},
   367  		},
   368  		&corev1.Secret{
   369  			ObjectMeta: metav1.ObjectMeta{
   370  				Namespace:   testNamespace,
   371  				Name:        managedProjectSecretName,
   372  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   373  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   374  			},
   375  			Data: map[string][]byte{
   376  				"name":     []byte(managedProjectRepository.Name),
   377  				"url":      []byte(managedProjectRepository.Repo),
   378  				"username": []byte(managedProjectRepository.Username),
   379  				"password": []byte(managedProjectRepository.Password),
   380  				"project":  []byte(managedProjectRepository.Project),
   381  			},
   382  		},
   383  		&corev1.Secret{
   384  			ObjectMeta: metav1.ObjectMeta{
   385  				Namespace: testNamespace,
   386  				Name:      "user-managed",
   387  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   388  			},
   389  			Data: map[string][]byte{
   390  				"name":     []byte(userProvidedRepository.Name),
   391  				"url":      []byte(userProvidedRepository.Repo),
   392  				"username": []byte(userProvidedRepository.Username),
   393  				"password": []byte(userProvidedRepository.Password),
   394  			},
   395  		},
   396  		&corev1.Secret{
   397  			ObjectMeta: metav1.ObjectMeta{
   398  				Namespace: testNamespace,
   399  				Name:      "user-managed-scoped",
   400  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   401  			},
   402  			Data: map[string][]byte{
   403  				"name":     []byte(userProvidedProjectRepository.Name),
   404  				"url":      []byte(userProvidedProjectRepository.Repo),
   405  				"username": []byte(userProvidedProjectRepository.Username),
   406  				"password": []byte(userProvidedProjectRepository.Password),
   407  				"project":  []byte(userProvidedProjectRepository.Project),
   408  			},
   409  		},
   410  	}
   411  
   412  	clientset := getClientset(repoSecrets...)
   413  	testee := &secretsRepositoryBackend{db: &db{
   414  		ns:            testNamespace,
   415  		kubeclientset: clientset,
   416  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   417  	}}
   418  
   419  	managedRepository.Username = "newUsername"
   420  	updateRepository, err := testee.UpdateRepository(t.Context(), managedRepository)
   421  	require.NoError(t, err)
   422  	assert.Same(t, managedRepository, updateRepository)
   423  	assert.Equal(t, managedRepository.Username, updateRepository.Username)
   424  
   425  	secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedSecretName, metav1.GetOptions{})
   426  	require.NoError(t, err)
   427  	assert.NotNil(t, secret)
   428  	assert.Equal(t, "newUsername", string(secret.Data["username"]))
   429  
   430  	userProvidedRepository.Username = "newOtherUsername"
   431  	updateRepository, err = testee.UpdateRepository(t.Context(), userProvidedRepository)
   432  	require.NoError(t, err)
   433  	assert.Same(t, userProvidedRepository, updateRepository)
   434  	assert.Equal(t, userProvidedRepository.Username, updateRepository.Username)
   435  
   436  	secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed", metav1.GetOptions{})
   437  	require.NoError(t, err)
   438  	assert.NotNil(t, secret)
   439  	assert.Equal(t, "newOtherUsername", string(secret.Data["username"]))
   440  
   441  	updateRepository, err = testee.UpdateRepository(t.Context(), newRepository)
   442  	require.NoError(t, err)
   443  	assert.Same(t, newRepository, updateRepository)
   444  
   445  	secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), newSecretName, metav1.GetOptions{})
   446  	require.NoError(t, err)
   447  	assert.NotNil(t, secret)
   448  	assert.Equal(t, "foo", string(secret.Data["username"]))
   449  
   450  	managedProjectRepository.Username = "newUsername"
   451  	updateRepository, err = testee.UpdateRepository(t.Context(), managedProjectRepository)
   452  	require.NoError(t, err)
   453  	assert.Same(t, managedProjectRepository, updateRepository)
   454  	assert.Equal(t, managedProjectRepository.Username, updateRepository.Username)
   455  
   456  	secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedProjectSecretName, metav1.GetOptions{})
   457  	require.NoError(t, err)
   458  	assert.NotNil(t, secret)
   459  	assert.Equal(t, "newUsername", string(secret.Data["username"]))
   460  
   461  	userProvidedProjectRepository.Username = "newUsernameScoped"
   462  	updateRepository, err = testee.UpdateRepository(t.Context(), userProvidedProjectRepository)
   463  	require.NoError(t, err)
   464  	assert.Same(t, userProvidedProjectRepository, updateRepository)
   465  	assert.Equal(t, userProvidedProjectRepository.Username, updateRepository.Username)
   466  
   467  	secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed-scoped", metav1.GetOptions{})
   468  	require.NoError(t, err)
   469  	assert.NotNil(t, secret)
   470  	assert.Equal(t, "newUsernameScoped", string(secret.Data["username"]))
   471  }
   472  
   473  func TestSecretsRepositoryBackend_DeleteRepository(t *testing.T) {
   474  	managedSecretName := RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj/argo-cd.git", "")
   475  	managedScopedSecretName := RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj/argo-cd.git", "someProject")
   476  	repoSecrets := []runtime.Object{
   477  		&corev1.Secret{
   478  			ObjectMeta: metav1.ObjectMeta{
   479  				Namespace:   testNamespace,
   480  				Name:        managedSecretName,
   481  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   482  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   483  			},
   484  			Data: map[string][]byte{
   485  				"name":     []byte("ArgoCD"),
   486  				"url":      []byte("git@github.com:argoproj/argo-cd.git"),
   487  				"username": []byte("someUsername"),
   488  				"password": []byte("somePassword"),
   489  			},
   490  		},
   491  		&corev1.Secret{
   492  			ObjectMeta: metav1.ObjectMeta{
   493  				Namespace:   testNamespace,
   494  				Name:        managedScopedSecretName,
   495  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   496  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   497  			},
   498  			Data: map[string][]byte{
   499  				"name":     []byte("ArgoCD"),
   500  				"url":      []byte("git@github.com:argoproj/argo-cd.git"),
   501  				"username": []byte("someUsername"),
   502  				"password": []byte("somePassword"),
   503  				"project":  []byte("someProject"),
   504  			},
   505  		},
   506  		&corev1.Secret{
   507  			ObjectMeta: metav1.ObjectMeta{
   508  				Namespace: testNamespace,
   509  				Name:      "user-managed",
   510  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
   511  			},
   512  			Data: map[string][]byte{
   513  				"name":     []byte("UserManagedRepo"),
   514  				"url":      []byte("git@github.com:argoproj/argoproj.git"),
   515  				"username": []byte("someOtherUsername"),
   516  				"password": []byte("someOtherPassword"),
   517  			},
   518  		},
   519  	}
   520  
   521  	clientset := getClientset(repoSecrets...)
   522  	testee := &secretsRepositoryBackend{db: &db{
   523  		ns:            testNamespace,
   524  		kubeclientset: clientset,
   525  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   526  	}}
   527  
   528  	err := testee.DeleteRepository(t.Context(), "git@github.com:argoproj/argo-cd.git", "")
   529  	require.NoError(t, err)
   530  
   531  	_, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedSecretName, metav1.GetOptions{})
   532  	require.Error(t, err)
   533  
   534  	_, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedScopedSecretName, metav1.GetOptions{})
   535  	require.NoError(t, err)
   536  
   537  	err = testee.DeleteRepository(t.Context(), "git@github.com:argoproj/argo-cd.git", "someProject")
   538  	require.NoError(t, err)
   539  
   540  	_, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedScopedSecretName, metav1.GetOptions{})
   541  	require.Error(t, err)
   542  
   543  	err = testee.DeleteRepository(t.Context(), "git@github.com:argoproj/argoproj.git", "")
   544  	require.NoError(t, err)
   545  
   546  	secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed", metav1.GetOptions{})
   547  	require.NoError(t, err)
   548  	assert.NotNil(t, secret)
   549  	assert.Empty(t, secret.Labels[common.LabelValueSecretTypeRepository])
   550  }
   551  
   552  func TestSecretsRepositoryBackend_CreateRepoCreds(t *testing.T) {
   553  	clientset := getClientset()
   554  	testee := &secretsRepositoryBackend{db: &db{
   555  		ns:            testNamespace,
   556  		kubeclientset: clientset,
   557  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   558  	}}
   559  
   560  	testCases := []struct {
   561  		name      string
   562  		repoCreds appsv1.RepoCreds
   563  		// Note: URL needs to be a different one for every testCase
   564  		// otherwise we would need to use the DeleteRepoCreds method to clean up the secret after each test
   565  		// which results in an unwanted dependency in a unit test
   566  	}{
   567  		{
   568  			name: "minimal_https_fields",
   569  			repoCreds: appsv1.RepoCreds{
   570  				URL:       "git@github.com:argoproj",
   571  				Username:  "someUsername",
   572  				Password:  "somePassword",
   573  				EnableOCI: true,
   574  			},
   575  		},
   576  		{
   577  			name: "with_proxy",
   578  			repoCreds: appsv1.RepoCreds{
   579  				URL:      "git@github.com:kubernetes",
   580  				Username: "anotherUsername",
   581  				Password: "anotherPassword",
   582  				Proxy:    "https://proxy.argoproj.io:3128",
   583  			},
   584  		},
   585  		{
   586  			name: "with_noProxy",
   587  			repoCreds: appsv1.RepoCreds{
   588  				URL:      "git@github.com:proxy",
   589  				Username: "anotherUsername",
   590  				Password: "anotherPassword",
   591  				NoProxy:  ".example.com,127.0.0.1",
   592  			},
   593  		},
   594  	}
   595  
   596  	for _, testCase := range testCases {
   597  		t.Run(testCase.name, func(t *testing.T) {
   598  			output, err := testee.CreateRepoCreds(t.Context(), &testCase.repoCreds)
   599  			require.NoError(t, err)
   600  			assert.Same(t, &testCase.repoCreds, output)
   601  
   602  			secret, err := clientset.CoreV1().Secrets(testNamespace).Get(
   603  				t.Context(),
   604  				RepoURLToSecretName(credSecretPrefix, testCase.repoCreds.URL, ""),
   605  				metav1.GetOptions{},
   606  			)
   607  			assert.NotNil(t, secret)
   608  			require.NoError(t, err)
   609  
   610  			assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy])
   611  			assert.Equal(t, common.LabelValueSecretTypeRepoCreds, secret.Labels[common.LabelKeySecretType])
   612  
   613  			// check every possible field of the secret if it has the same (default) value as the repoCred struct
   614  			// non-string fields must be parsed so that their default value matches the one of the corresponding type
   615  			assert.Equal(t, testCase.repoCreds.URL, string(secret.Data["url"]))
   616  			assert.Equal(t, testCase.repoCreds.Username, string(secret.Data["username"]))
   617  			assert.Equal(t, testCase.repoCreds.Password, string(secret.Data["password"]))
   618  			if enableOCI, err := strconv.ParseBool(string(secret.Data["githubAppPrivateKey"])); err == nil {
   619  				assert.Equal(t, strconv.FormatBool(testCase.repoCreds.EnableOCI), enableOCI)
   620  			}
   621  			assert.Equal(t, testCase.repoCreds.SSHPrivateKey, string(secret.Data["sshPrivateKey"]))
   622  			assert.Equal(t, testCase.repoCreds.TLSClientCertData, string(secret.Data["tlsClientCertData"]))
   623  			assert.Equal(t, testCase.repoCreds.TLSClientCertKey, string(secret.Data["tlsClientCertKey"]))
   624  			assert.Equal(t, testCase.repoCreds.Type, string(secret.Data["type"]))
   625  			assert.Equal(t, testCase.repoCreds.GithubAppPrivateKey, string(secret.Data["githubAppPrivateKey"]))
   626  			if githubAppPrivateKey, err := strconv.ParseInt(string(secret.Data["githubAppPrivateKey"]), 10, 64); err == nil {
   627  				assert.Equal(t, testCase.repoCreds.GithubAppId, githubAppPrivateKey)
   628  			}
   629  			if githubAppID, err := strconv.ParseInt(string(secret.Data["githubAppId"]), 10, 64); err == nil {
   630  				assert.Equal(t, testCase.repoCreds.GithubAppInstallationId, githubAppID)
   631  			}
   632  			assert.Equal(t, testCase.repoCreds.GitHubAppEnterpriseBaseURL, string(secret.Data["githubAppEnterpriseUrl"]))
   633  			assert.Equal(t, testCase.repoCreds.Proxy, string(secret.Data["proxy"]))
   634  			assert.Equal(t, testCase.repoCreds.NoProxy, string(secret.Data["noProxy"]))
   635  		})
   636  	}
   637  }
   638  
   639  func TestSecretsRepositoryBackend_GetRepoCreds(t *testing.T) {
   640  	repoCredSecrets := []runtime.Object{
   641  		&corev1.Secret{
   642  			ObjectMeta: metav1.ObjectMeta{
   643  				Namespace:   testNamespace,
   644  				Name:        RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj", ""),
   645  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   646  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   647  			},
   648  			Data: map[string][]byte{
   649  				"url":      []byte("git@github.com:argoproj"),
   650  				"username": []byte("someUsername"),
   651  				"password": []byte("somePassword"),
   652  			},
   653  		},
   654  		&corev1.Secret{
   655  			ObjectMeta: metav1.ObjectMeta{
   656  				Namespace: testNamespace,
   657  				Name:      "user-managed",
   658  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   659  			},
   660  			Data: map[string][]byte{
   661  				"url":      []byte("git@gitlab.com"),
   662  				"username": []byte("someOtherUsername"),
   663  				"password": []byte("someOtherPassword"),
   664  			},
   665  		},
   666  		&corev1.Secret{
   667  			ObjectMeta: metav1.ObjectMeta{
   668  				Namespace: testNamespace,
   669  				Name:      "proxy-repo",
   670  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   671  			},
   672  			Data: map[string][]byte{
   673  				"url":      []byte("git@gitlab.com"),
   674  				"username": []byte("someOtherUsername"),
   675  				"password": []byte("someOtherPassword"),
   676  				"proxy":    []byte("https://proxy.argoproj.io:3128"),
   677  				"noProxy":  []byte(".example.com,127.0.0.1"),
   678  			},
   679  		},
   680  	}
   681  
   682  	clientset := getClientset(repoCredSecrets...)
   683  	testee := &secretsRepositoryBackend{db: &db{
   684  		ns:            testNamespace,
   685  		kubeclientset: clientset,
   686  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   687  	}}
   688  
   689  	repoCred, err := testee.GetRepoCreds(t.Context(), "git@github.com:argoproj")
   690  	require.NoError(t, err)
   691  	require.NotNil(t, repoCred)
   692  	assert.Equal(t, "git@github.com:argoproj", repoCred.URL)
   693  	assert.Equal(t, "someUsername", repoCred.Username)
   694  	assert.Equal(t, "somePassword", repoCred.Password)
   695  
   696  	repoCred, err = testee.GetRepoCreds(t.Context(), "git@gitlab.com")
   697  	require.NoError(t, err)
   698  	assert.NotNil(t, repoCred)
   699  	assert.Equal(t, "git@gitlab.com", repoCred.URL)
   700  	assert.Equal(t, "someOtherUsername", repoCred.Username)
   701  	assert.Equal(t, "someOtherPassword", repoCred.Password)
   702  	if repoCred.Proxy != "" {
   703  		assert.Equal(t, "https://proxy.argoproj.io:3128", repoCred.Proxy)
   704  	}
   705  	if repoCred.NoProxy != "" {
   706  		assert.Equal(t, ".example.com,127.0.0.1", repoCred.NoProxy)
   707  	}
   708  }
   709  
   710  func TestSecretsRepositoryBackend_ListRepoCreds(t *testing.T) {
   711  	repoCredSecrets := []runtime.Object{
   712  		&corev1.Secret{
   713  			ObjectMeta: metav1.ObjectMeta{
   714  				Namespace:   testNamespace,
   715  				Name:        RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj", ""),
   716  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   717  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   718  			},
   719  			Data: map[string][]byte{
   720  				"url":      []byte("git@github.com:argoproj"),
   721  				"username": []byte("someUsername"),
   722  				"password": []byte("somePassword"),
   723  			},
   724  		},
   725  		&corev1.Secret{
   726  			ObjectMeta: metav1.ObjectMeta{
   727  				Namespace: testNamespace,
   728  				Name:      "user-managed",
   729  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   730  			},
   731  			Data: map[string][]byte{
   732  				"url":      []byte("git@gitlab.com"),
   733  				"username": []byte("someOtherUsername"),
   734  				"password": []byte("someOtherPassword"),
   735  			},
   736  		},
   737  	}
   738  
   739  	clientset := getClientset(repoCredSecrets...)
   740  	testee := &secretsRepositoryBackend{db: &db{
   741  		ns:            testNamespace,
   742  		kubeclientset: clientset,
   743  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   744  	}}
   745  
   746  	repoCreds, err := testee.ListRepoCreds(t.Context())
   747  	require.NoError(t, err)
   748  	assert.Len(t, repoCreds, 2)
   749  	assert.Contains(t, repoCreds, "git@github.com:argoproj")
   750  	assert.Contains(t, repoCreds, "git@gitlab.com")
   751  }
   752  
   753  func TestSecretsRepositoryBackend_UpdateRepoCreds(t *testing.T) {
   754  	managedCreds := &appsv1.RepoCreds{
   755  		URL:      "git@github.com:argoproj",
   756  		Username: "someUsername",
   757  		Password: "somePassword",
   758  	}
   759  	userProvidedCreds := &appsv1.RepoCreds{
   760  		URL:      "git@gitlab.com",
   761  		Username: "someOtherUsername",
   762  		Password: "someOtherPassword",
   763  	}
   764  	newCreds := &appsv1.RepoCreds{
   765  		URL:      "git@github.com:foobar",
   766  		Username: "foo",
   767  		Password: "bar",
   768  	}
   769  
   770  	managedCredsName := RepoURLToSecretName(credSecretPrefix, managedCreds.URL, "")
   771  	newCredsName := RepoURLToSecretName(credSecretPrefix, newCreds.URL, "")
   772  	repoCredSecrets := []runtime.Object{
   773  		&corev1.Secret{
   774  			ObjectMeta: metav1.ObjectMeta{
   775  				Namespace:   testNamespace,
   776  				Name:        managedCredsName,
   777  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   778  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   779  			},
   780  			Data: map[string][]byte{
   781  				"url":      []byte(managedCreds.URL),
   782  				"username": []byte(managedCreds.Username),
   783  				"password": []byte(managedCreds.Password),
   784  			},
   785  		},
   786  		&corev1.Secret{
   787  			ObjectMeta: metav1.ObjectMeta{
   788  				Namespace: testNamespace,
   789  				Name:      "user-managed",
   790  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   791  			},
   792  			Data: map[string][]byte{
   793  				"url":      []byte(userProvidedCreds.URL),
   794  				"username": []byte(userProvidedCreds.Username),
   795  				"password": []byte(userProvidedCreds.Password),
   796  			},
   797  		},
   798  	}
   799  
   800  	clientset := getClientset(repoCredSecrets...)
   801  	testee := &secretsRepositoryBackend{db: &db{
   802  		ns:            testNamespace,
   803  		kubeclientset: clientset,
   804  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   805  	}}
   806  
   807  	managedCreds.Username = "newUsername"
   808  	updateRepoCreds, err := testee.UpdateRepoCreds(t.Context(), managedCreds)
   809  	require.NoError(t, err)
   810  	assert.NotSame(t, managedCreds, updateRepoCreds)
   811  	assert.Equal(t, managedCreds.Username, updateRepoCreds.Username)
   812  
   813  	secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedCredsName, metav1.GetOptions{})
   814  	require.NoError(t, err)
   815  	assert.NotNil(t, secret)
   816  	assert.Equal(t, "newUsername", string(secret.Data["username"]))
   817  
   818  	userProvidedCreds.Username = "newOtherUsername"
   819  	updateRepoCreds, err = testee.UpdateRepoCreds(t.Context(), userProvidedCreds)
   820  	require.NoError(t, err)
   821  	assert.NotSame(t, userProvidedCreds, updateRepoCreds)
   822  	assert.Equal(t, userProvidedCreds.Username, updateRepoCreds.Username)
   823  
   824  	secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed", metav1.GetOptions{})
   825  	require.NoError(t, err)
   826  	assert.NotNil(t, secret)
   827  	assert.Equal(t, "newOtherUsername", string(secret.Data["username"]))
   828  
   829  	updateRepoCreds, err = testee.UpdateRepoCreds(t.Context(), newCreds)
   830  	require.NoError(t, err)
   831  	assert.Same(t, newCreds, updateRepoCreds)
   832  
   833  	secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), newCredsName, metav1.GetOptions{})
   834  	require.NoError(t, err)
   835  	assert.NotNil(t, secret)
   836  	assert.Equal(t, "foo", string(secret.Data["username"]))
   837  }
   838  
   839  func TestSecretsRepositoryBackend_DeleteRepoCreds(t *testing.T) {
   840  	managedSecretName := RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj/argo-cd.git", "")
   841  	repoSecrets := []runtime.Object{
   842  		&corev1.Secret{
   843  			ObjectMeta: metav1.ObjectMeta{
   844  				Namespace:   testNamespace,
   845  				Name:        managedSecretName,
   846  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   847  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   848  			},
   849  			Data: map[string][]byte{
   850  				"url":      []byte("git@github.com:argoproj"),
   851  				"username": []byte("someUsername"),
   852  				"password": []byte("somePassword"),
   853  			},
   854  		},
   855  		&corev1.Secret{
   856  			ObjectMeta: metav1.ObjectMeta{
   857  				Namespace: testNamespace,
   858  				Name:      "user-managed",
   859  				Labels:    map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   860  			},
   861  			Data: map[string][]byte{
   862  				"url":      []byte("git@gitlab.com"),
   863  				"username": []byte("someOtherUsername"),
   864  				"password": []byte("someOtherPassword"),
   865  			},
   866  		},
   867  	}
   868  
   869  	clientset := getClientset(repoSecrets...)
   870  	testee := &secretsRepositoryBackend{db: &db{
   871  		ns:            testNamespace,
   872  		kubeclientset: clientset,
   873  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   874  	}}
   875  
   876  	err := testee.DeleteRepoCreds(t.Context(), "git@github.com:argoproj")
   877  	require.NoError(t, err)
   878  
   879  	_, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedSecretName, metav1.GetOptions{})
   880  	require.Error(t, err)
   881  
   882  	err = testee.DeleteRepoCreds(t.Context(), "git@gitlab.com")
   883  	require.NoError(t, err)
   884  
   885  	secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed", metav1.GetOptions{})
   886  	require.NoError(t, err)
   887  	assert.NotNil(t, secret)
   888  	assert.Empty(t, secret.Labels[common.LabelValueSecretTypeRepoCreds])
   889  }
   890  
   891  func TestSecretsRepositoryBackend_GetAllHelmRepoCreds(t *testing.T) {
   892  	repoCredSecrets := []runtime.Object{
   893  		&corev1.Secret{
   894  			ObjectMeta: metav1.ObjectMeta{
   895  				Namespace:   testNamespace,
   896  				Name:        RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj", ""),
   897  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   898  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   899  			},
   900  			Data: map[string][]byte{
   901  				"url":      []byte("git@github.com:argoproj"),
   902  				"username": []byte("someUsername"),
   903  				"password": []byte("somePassword"),
   904  				"type":     []byte("helm"),
   905  			},
   906  		},
   907  		&corev1.Secret{
   908  			ObjectMeta: metav1.ObjectMeta{
   909  				Namespace:   testNamespace,
   910  				Name:        RepoURLToSecretName(repoSecretPrefix, "git@gitlab.com", ""),
   911  				Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
   912  				Labels:      map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
   913  			},
   914  			Data: map[string][]byte{
   915  				"url":      []byte("git@gitlab.com"),
   916  				"username": []byte("someOtherUsername"),
   917  				"password": []byte("someOtherPassword"),
   918  				"type":     []byte("git"),
   919  			},
   920  		},
   921  	}
   922  
   923  	clientset := getClientset(repoCredSecrets...)
   924  	testee := &secretsRepositoryBackend{db: &db{
   925  		ns:            testNamespace,
   926  		kubeclientset: clientset,
   927  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   928  	}}
   929  
   930  	repoCreds, err := testee.GetAllHelmRepoCreds(t.Context())
   931  	require.NoError(t, err)
   932  	assert.Len(t, repoCreds, 1)
   933  }
   934  
   935  func TestRepoCredsToSecret(t *testing.T) {
   936  	clientset := getClientset()
   937  	testee := &secretsRepositoryBackend{db: &db{
   938  		ns:            testNamespace,
   939  		kubeclientset: clientset,
   940  		settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   941  	}}
   942  	s := &corev1.Secret{}
   943  	creds := &appsv1.RepoCreds{
   944  		URL:                        "URL",
   945  		Username:                   "Username",
   946  		Password:                   "Password",
   947  		SSHPrivateKey:              "SSHPrivateKey",
   948  		EnableOCI:                  true,
   949  		TLSClientCertData:          "TLSClientCertData",
   950  		TLSClientCertKey:           "TLSClientCertKey",
   951  		Type:                       "Type",
   952  		GithubAppPrivateKey:        "GithubAppPrivateKey",
   953  		GithubAppId:                123,
   954  		GithubAppInstallationId:    456,
   955  		GitHubAppEnterpriseBaseURL: "GitHubAppEnterpriseBaseURL",
   956  	}
   957  
   958  	s = testee.repoCredsToSecret(creds, s)
   959  
   960  	assert.Equal(t, []byte(creds.URL), s.Data["url"])
   961  	assert.Equal(t, []byte(creds.Username), s.Data["username"])
   962  	assert.Equal(t, []byte(creds.Password), s.Data["password"])
   963  	assert.Equal(t, []byte(creds.SSHPrivateKey), s.Data["sshPrivateKey"])
   964  	assert.Equal(t, []byte(strconv.FormatBool(creds.EnableOCI)), s.Data["enableOCI"])
   965  	assert.Equal(t, []byte(creds.TLSClientCertData), s.Data["tlsClientCertData"])
   966  	assert.Equal(t, []byte(creds.TLSClientCertKey), s.Data["tlsClientCertKey"])
   967  	assert.Equal(t, []byte(creds.Type), s.Data["type"])
   968  	assert.Equal(t, []byte(creds.GithubAppPrivateKey), s.Data["githubAppPrivateKey"])
   969  	assert.Equal(t, []byte(strconv.FormatInt(creds.GithubAppId, 10)), s.Data["githubAppID"])
   970  	assert.Equal(t, []byte(strconv.FormatInt(creds.GithubAppInstallationId, 10)), s.Data["githubAppInstallationID"])
   971  	assert.Equal(t, []byte(creds.GitHubAppEnterpriseBaseURL), s.Data["githubAppEnterpriseBaseUrl"])
   972  	assert.Equal(t, map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD}, s.Annotations)
   973  	assert.Equal(t, map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds}, s.Labels)
   974  }
   975  
   976  func TestRepoWriteCredsToSecret(t *testing.T) {
   977  	clientset := getClientset()
   978  	testee := &secretsRepositoryBackend{
   979  		db: &db{
   980  			ns:            testNamespace,
   981  			kubeclientset: clientset,
   982  			settingsMgr:   settings.NewSettingsManager(t.Context(), clientset, testNamespace),
   983  		},
   984  		writeCreds: true,
   985  	}
   986  	s := &corev1.Secret{}
   987  	creds := &appsv1.RepoCreds{
   988  		URL:                        "URL",
   989  		Username:                   "Username",
   990  		Password:                   "Password",
   991  		SSHPrivateKey:              "SSHPrivateKey",
   992  		EnableOCI:                  true,
   993  		TLSClientCertData:          "TLSClientCertData",
   994  		TLSClientCertKey:           "TLSClientCertKey",
   995  		Type:                       "Type",
   996  		GithubAppPrivateKey:        "GithubAppPrivateKey",
   997  		GithubAppId:                123,
   998  		GithubAppInstallationId:    456,
   999  		GitHubAppEnterpriseBaseURL: "GitHubAppEnterpriseBaseURL",
  1000  	}
  1001  	s = testee.repoCredsToSecret(creds, s)
  1002  	assert.Equal(t, []byte(creds.URL), s.Data["url"])
  1003  	assert.Equal(t, []byte(creds.Username), s.Data["username"])
  1004  	assert.Equal(t, []byte(creds.Password), s.Data["password"])
  1005  	assert.Equal(t, []byte(creds.SSHPrivateKey), s.Data["sshPrivateKey"])
  1006  	assert.Equal(t, []byte(strconv.FormatBool(creds.EnableOCI)), s.Data["enableOCI"])
  1007  	assert.Equal(t, []byte(creds.TLSClientCertData), s.Data["tlsClientCertData"])
  1008  	assert.Equal(t, []byte(creds.TLSClientCertKey), s.Data["tlsClientCertKey"])
  1009  	assert.Equal(t, []byte(creds.Type), s.Data["type"])
  1010  	assert.Equal(t, []byte(creds.GithubAppPrivateKey), s.Data["githubAppPrivateKey"])
  1011  	assert.Equal(t, []byte(strconv.FormatInt(creds.GithubAppId, 10)), s.Data["githubAppID"])
  1012  	assert.Equal(t, []byte(strconv.FormatInt(creds.GithubAppInstallationId, 10)), s.Data["githubAppInstallationID"])
  1013  	assert.Equal(t, []byte(creds.GitHubAppEnterpriseBaseURL), s.Data["githubAppEnterpriseBaseUrl"])
  1014  	assert.Equal(t, map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD}, s.Annotations)
  1015  	assert.Equal(t, map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCredsWrite}, s.Labels)
  1016  }
  1017  
  1018  func TestRaceConditionInRepoCredsOperations(t *testing.T) {
  1019  	// Create a single shared secret that will be accessed concurrently
  1020  	sharedSecret := &corev1.Secret{
  1021  		ObjectMeta: metav1.ObjectMeta{
  1022  			Name:      RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj/argo-cd.git", ""),
  1023  			Namespace: testNamespace,
  1024  			Labels: map[string]string{
  1025  				common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds,
  1026  			},
  1027  		},
  1028  		Data: map[string][]byte{
  1029  			"url":      []byte("git@github.com:argoproj/argo-cd.git"),
  1030  			"username": []byte("test-user"),
  1031  			"password": []byte("test-pass"),
  1032  		},
  1033  	}
  1034  
  1035  	// Create test credentials that we'll use for conversion
  1036  	repoCreds := &appsv1.RepoCreds{
  1037  		URL:      "git@github.com:argoproj/argo-cd.git",
  1038  		Username: "test-user",
  1039  		Password: "test-pass",
  1040  	}
  1041  
  1042  	backend := &secretsRepositoryBackend{}
  1043  
  1044  	var wg sync.WaitGroup
  1045  	concurrentOps := 50
  1046  	errChan := make(chan error, concurrentOps*2) // Channel to collect errors
  1047  
  1048  	// Launch goroutines that perform concurrent operations
  1049  	for i := 0; i < concurrentOps; i++ {
  1050  		wg.Add(2)
  1051  
  1052  		// One goroutine converts from RepoCreds to Secret
  1053  		go func() {
  1054  			defer wg.Done()
  1055  			defer func() {
  1056  				if r := recover(); r != nil {
  1057  					errChan <- fmt.Errorf("panic in repoCredsToSecret: %v", r)
  1058  				}
  1059  			}()
  1060  			_ = backend.repoCredsToSecret(repoCreds, sharedSecret)
  1061  		}()
  1062  
  1063  		// Another goroutine converts from Secret to RepoCreds
  1064  		go func() {
  1065  			defer wg.Done()
  1066  			defer func() {
  1067  				if r := recover(); r != nil {
  1068  					errChan <- fmt.Errorf("panic in secretToRepoCred: %v", r)
  1069  				}
  1070  			}()
  1071  			creds, err := backend.secretToRepoCred(sharedSecret)
  1072  			if err != nil {
  1073  				errChan <- fmt.Errorf("error in secretToRepoCred: %w", err)
  1074  				return
  1075  			}
  1076  			// Verify data integrity
  1077  			if creds.URL != repoCreds.URL || creds.Username != repoCreds.Username || creds.Password != repoCreds.Password {
  1078  				errChan <- fmt.Errorf("data mismatch in conversion: expected %v, got %v", repoCreds, creds)
  1079  			}
  1080  		}()
  1081  	}
  1082  
  1083  	wg.Wait()
  1084  	close(errChan)
  1085  
  1086  	// Check for any errors that occurred during concurrent operations
  1087  	for err := range errChan {
  1088  		t.Errorf("concurrent operation error: %v", err)
  1089  	}
  1090  
  1091  	// Verify final state
  1092  	finalCreds, err := backend.secretToRepoCred(sharedSecret)
  1093  	require.NoError(t, err)
  1094  	assert.Equal(t, repoCreds.URL, finalCreds.URL)
  1095  	assert.Equal(t, repoCreds.Username, finalCreds.Username)
  1096  	assert.Equal(t, repoCreds.Password, finalCreds.Password)
  1097  }
  1098  
  1099  func TestRaceConditionInRepositoryOperations(t *testing.T) {
  1100  	// Create a single shared secret that will be accessed concurrently
  1101  	sharedSecret := &corev1.Secret{
  1102  		ObjectMeta: metav1.ObjectMeta{
  1103  			Name:      RepoURLToSecretName(repoSecretPrefix, "git@github.com:argoproj/argo-cd.git", ""),
  1104  			Namespace: testNamespace,
  1105  			Labels: map[string]string{
  1106  				common.LabelKeySecretType: common.LabelValueSecretTypeRepository,
  1107  			},
  1108  		},
  1109  		Data: map[string][]byte{
  1110  			"url":      []byte("git@github.com:argoproj/argo-cd.git"),
  1111  			"name":     []byte("test-repo"),
  1112  			"username": []byte("test-user"),
  1113  			"password": []byte("test-pass"),
  1114  		},
  1115  	}
  1116  
  1117  	// Create test repository that we'll use for conversion
  1118  	repo := &appsv1.Repository{
  1119  		Name:     "test-repo",
  1120  		Repo:     "git@github.com:argoproj/argo-cd.git",
  1121  		Username: "test-user",
  1122  		Password: "test-pass",
  1123  	}
  1124  
  1125  	backend := &secretsRepositoryBackend{}
  1126  
  1127  	var wg sync.WaitGroup
  1128  	concurrentOps := 50
  1129  	errChan := make(chan error, concurrentOps*2) // Channel to collect errors
  1130  
  1131  	// Launch goroutines that perform concurrent operations
  1132  	for i := 0; i < concurrentOps; i++ {
  1133  		wg.Add(2)
  1134  
  1135  		// One goroutine converts from Repository to Secret
  1136  		go func() {
  1137  			defer wg.Done()
  1138  			defer func() {
  1139  				if r := recover(); r != nil {
  1140  					errChan <- fmt.Errorf("panic in repositoryToSecret: %v", r)
  1141  				}
  1142  			}()
  1143  			_ = backend.repositoryToSecret(repo, sharedSecret)
  1144  		}()
  1145  
  1146  		// Another goroutine converts from Secret to Repository
  1147  		go func() {
  1148  			defer wg.Done()
  1149  			defer func() {
  1150  				if r := recover(); r != nil {
  1151  					errChan <- fmt.Errorf("panic in secretToRepository: %v", r)
  1152  				}
  1153  			}()
  1154  			repository, err := secretToRepository(sharedSecret)
  1155  			if err != nil {
  1156  				errChan <- fmt.Errorf("error in secretToRepository: %w", err)
  1157  				return
  1158  			}
  1159  			// Verify data integrity
  1160  			if repository.Name != repo.Name || repository.Repo != repo.Repo ||
  1161  				repository.Username != repo.Username || repository.Password != repo.Password {
  1162  				errChan <- fmt.Errorf("data mismatch in conversion: expected %v, got %v", repo, repository)
  1163  			}
  1164  		}()
  1165  	}
  1166  
  1167  	wg.Wait()
  1168  	close(errChan)
  1169  
  1170  	// Check for any errors that occurred during concurrent operations
  1171  	for err := range errChan {
  1172  		t.Errorf("concurrent operation error: %v", err)
  1173  	}
  1174  
  1175  	// Verify final state
  1176  	finalRepo, err := secretToRepository(sharedSecret)
  1177  	require.NoError(t, err)
  1178  	assert.Equal(t, repo.Name, finalRepo.Name)
  1179  	assert.Equal(t, repo.Repo, finalRepo.Repo)
  1180  	assert.Equal(t, repo.Username, finalRepo.Username)
  1181  	assert.Equal(t, repo.Password, finalRepo.Password)
  1182  }