mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
* feat(api): hard-fork CRD API group argoproj.io -> deploy.hanzo.ai
Hanzo CD hard-fork, phase 1.1. Single source of truth is
pkg/apis/application/register.go's Group constant (Application/AppProject/
ApplicationSet all derive their FullName from it); every other Go call-site
that hardcoded the literal group string is updated to match: the 3 generated
informer GVRs, the app-of-apps nested-Application detector in types.go, the
resource-tree Application-kind cache populator, the 3 NotFound error
constructors in server/application, and the applicationset-controller's
leader-election Lease name/ID (kept in lockstep across Go source + RBAC
resourceNames, or leader election would get RBAC-denied on startup).
CRD YAMLs (application/appproject/applicationset) and every RBAC apiGroups
rule that actually governs our 3 CRDs are repointed to deploy.hanzo.ai, across
both manifests/base/* (namespaced Roles) and manifests/cluster-rbac/* (cluster
scope). Two adjacent things that share the "argoproj.io" string but are NOT
ours are deliberately left untouched: gitops-engine's health-check dispatch
for Argo Workflows' `Workflow` kind, and argocd-server-clusterrole's second
apiGroups block granting read on Argo Workflows' `workflows` resource — both
belong to a separate, unrelated Argoproj-family project we are not forking.
Also untouched, per explicit scope: annotation/label keys (argocd.argoproj.io/*)
and finalizers (*.argoproj.io) that track already-applied live resources, and
argocd-cm.yaml's resource.customizations.ignoreResourceUpdates keys (a narrow
app-of-apps diffing nicety, not build- or RBAC-relevant).
CGO_ENABLED=0 go build ./... verified green.
* ci: wire canonical hanzo.yml + cicd.yml (Hanzo CD image build)
Phase 1.3. Follows the same one-way pattern every hanzo/lux/zoo repo uses:
hanzo.yml declares the image (root Dockerfile -> ghcr.io/hanzoai/deploy) and a
fast go-build sanity test (the multi-stage Dockerfile build is the slow, real
gate); cicd.yml is the ~10-line trigger shim importing hanzoai/ci's reusable
build workflow on the arc pool, secrets from KMS only.
No `deploy:` block yet, matching the hanzoai/cms build-only-first-cutover
pattern: this repo's own live hanzo-cd namespace runs the whole Hanzo fleet's
GitOps, so the prod image/CRD swap is a deliberate separate action (see the
[HELD] universe PR), never an automatic rollout on merge. Every PR/push still
builds and pushes an immutable ghcr.io/hanzoai/deploy:sha-<7>-amd64 image.
* feat(ui): rebrand Argo CD -> Hanzo CD (title, favicon, logo, sidebar)
Phase 1.2. User-facing "Argo CD"/"ArgoCD" text replaced with "Hanzo CD"
across the login page, help modal, settings screens, version-info panel,
sync-options warning, fullscreen-logs title, app.tsx page titles, and
index.html's <title>/noscript text (dropped the now-wrong upstream CLI
doc link rather than invent a hanzo.ai URL that doesn't exist).
Logo/favicon assets replaced with the canonical Hanzo mark (never
hand-drawn, copied from ~/work/hanzo/logo/dist): the two real usages of
Argo's logo collapse onto one Hanzo glyph (mono + white variants) at the
same 5 call sites (login icon + footer, login.scss $logoPath/
$logoNegativePath, sidebar text-logo), preserving the existing CSS
invert() filters so both the light login panel and dark sidebar still
render correctly. Old argologo.svg/argo_o.svg/argo.png deleted (no
longer referenced). favicon-{16x16,32x32}.png added at
ui/src/assets/favicon/ (index.html's links to that path were previously
dead - the directory didn't exist in this checkout). package.json name
argo-cd-ui -> hanzo-cd-ui.
Left alone, deliberately: the two Kubernetes resource-kind icon sets
under ui/src/assets/images/resources/ (unrelated to branding), and
upstream doc/project links (argoproj.io, argo-cd.readthedocs.io) since
we have no equivalent hanzo.ai docs to point to yet - except the login
footer logo's outbound link, which now points to hanzo.ai instead of
argoproj.io (a Hanzo-branded mark linking to the upstream project it
was forked from read as an obvious leftover).
* feat(module): rename Go module github.com/argoproj/argo-cd/v3 -> github.com/hanzoai/deploy
Phase 2. No version suffix on the new path (org policy: never publish our
own Go modules above v1.x.x / with a v2+ suffix) — this fork starts a fresh
v0/v1 module identity rather than carrying the v3 the upstream fork point
had reached.
Mechanical, symmetric rewrite: 704 .go files (import statements only; two
disjoint substitutions applied specific-first so the nested gitops-engine
module path never double-matches), plus go.mod (module line, the
gitops-engine require+replace pair, version reset to v0.0.0-... since a
suffix-less path can't carry a v3 semver), gitops-engine/go.mod, Makefile
(ldflags PACKAGE + 2 TEST_MODULE filters), .golangci.yaml, .goreleaser.yaml,
.mockery.yaml (+ gitops-engine's), Procfile, the codegen/proto generation
scripts (hack/generate-proto.sh, update-codegen.sh, update-openapi.sh), and
20 .proto files' go_package options. Also one OpenTelemetry tracer name
string in gitops-engine/pkg/sync/sync_context.go (a cosmestic label, not an
import, but worth keeping consistent).
CGO_ENABLED=0 go build ./... verified green for both the root module and
the nested gitops-engine module; go vet clean on both; go mod tidy run
(network available) to reconcile go.sum, dependency set unchanged.
Left alone, deliberately: doc-only mentions (docs/snyk/*.html scan reports,
gitops-engine/README.md), a comment-only reference in
.github/workflows/release.yaml, and one Renovate bot config line — none are
build- or codegen-load-bearing. Also left alone: comments citing upstream
GitHub issue URLs (github.com/argoproj/argo-cd/issues/NNNNN) and a handful
of test fixtures using "https://github.com/argoproj/argo-cd.git" as a
representative example repo URL — both are accurate historical/test-data
references, not stale self-references.
---------
Co-authored-by: Hanzo Dev <[email protected]>
1313 lines
49 KiB
Go
1313 lines
49 KiB
Go
package db
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/client-go/kubernetes/fake"
|
|
|
|
"github.com/hanzoai/deploy/common"
|
|
appsv1 "github.com/hanzoai/deploy/pkg/apis/application/v1alpha1"
|
|
"github.com/hanzoai/deploy/util/settings"
|
|
)
|
|
|
|
func TestSecretsRepositoryBackend_CreateRepository(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type fixture struct {
|
|
clientSet *fake.Clientset
|
|
repoBackend *secretsRepositoryBackend
|
|
}
|
|
repo := &appsv1.Repository{
|
|
Name: "ArgoCD",
|
|
Repo: "[email protected]:argoproj/argo-cd.git",
|
|
Username: "someUsername",
|
|
Password: "somePassword",
|
|
InsecureIgnoreHostKey: false,
|
|
EnableLFS: true,
|
|
}
|
|
setup := func() *fixture {
|
|
clientset := getClientset()
|
|
settingsMgr := settings.NewSettingsManager(t.Context(), clientset, testNamespace)
|
|
repoBackend := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settingsMgr,
|
|
}}
|
|
return &fixture{
|
|
clientSet: clientset,
|
|
repoBackend: repoBackend,
|
|
}
|
|
}
|
|
t.Run("will create repository successfully", func(t *testing.T) {
|
|
// given
|
|
t.Parallel()
|
|
f := setup()
|
|
|
|
// when
|
|
output, err := f.repoBackend.CreateRepository(t.Context(), repo)
|
|
|
|
// then
|
|
require.NoError(t, err)
|
|
assert.Same(t, repo, output)
|
|
|
|
secret, err := f.clientSet.CoreV1().Secrets(testNamespace).Get(
|
|
t.Context(),
|
|
RepoURLToSecretName(repoSecretPrefix, repo.Repo, ""),
|
|
metav1.GetOptions{},
|
|
)
|
|
assert.NotNil(t, secret)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy])
|
|
assert.Equal(t, common.LabelValueSecretTypeRepository, secret.Labels[common.LabelKeySecretType])
|
|
|
|
assert.Equal(t, repo.Name, string(secret.Data["name"]))
|
|
assert.Equal(t, repo.Repo, string(secret.Data["url"]))
|
|
assert.Equal(t, repo.Username, string(secret.Data["username"]))
|
|
assert.Equal(t, repo.Password, string(secret.Data["password"]))
|
|
assert.Empty(t, string(secret.Data["insecureIgnoreHostKey"]))
|
|
assert.Equal(t, strconv.FormatBool(repo.EnableLFS), string(secret.Data["enableLfs"]))
|
|
})
|
|
t.Run("will return proper error if secret does not have expected label", func(t *testing.T) {
|
|
// given
|
|
t.Parallel()
|
|
f := setup()
|
|
|
|
// when
|
|
_, err := f.repoBackend.CreateRepository(t.Context(), repo)
|
|
|
|
// then
|
|
require.NoError(t, err)
|
|
|
|
// given - remove the label from the secret
|
|
secret, err := f.clientSet.CoreV1().Secrets(testNamespace).Get(
|
|
t.Context(),
|
|
RepoURLToSecretName(repoSecretPrefix, repo.Repo, ""),
|
|
metav1.GetOptions{},
|
|
)
|
|
require.NoError(t, err)
|
|
delete(secret.Labels, common.LabelKeySecretType)
|
|
_, err = f.clientSet.CoreV1().Secrets(testNamespace).Update(t.Context(), secret, metav1.UpdateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
// Resync informers to ensure the cache reflects the updated secret
|
|
err = f.repoBackend.db.settingsMgr.ResyncInformers()
|
|
require.NoError(t, err)
|
|
|
|
// when - try to create the same repository again
|
|
output, err := f.repoBackend.CreateRepository(t.Context(), repo)
|
|
|
|
// then
|
|
require.Error(t, err)
|
|
assert.Nil(t, output)
|
|
status, ok := status.FromError(err)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, codes.InvalidArgument, status.Code(), "got unexpected error: %v", err)
|
|
})
|
|
t.Run("will return proper error if secret already exists and does have the proper label", func(t *testing.T) {
|
|
// given
|
|
t.Parallel()
|
|
f := setup()
|
|
|
|
// when
|
|
_, err := f.repoBackend.CreateRepository(t.Context(), repo)
|
|
|
|
// then
|
|
require.NoError(t, err)
|
|
|
|
// when - try to create the same repository again
|
|
output, err := f.repoBackend.CreateRepository(t.Context(), repo)
|
|
|
|
// then
|
|
require.Error(t, err)
|
|
assert.Nil(t, output)
|
|
status, ok := status.FromError(err)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, codes.AlreadyExists, status.Code(), "got unexpected error: %v", err)
|
|
})
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_GetRepository(t *testing.T) {
|
|
repoSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj/argo-cd.git", ""),
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("ArgoCD"),
|
|
"url": []byte("[email protected]:argoproj/argo-cd.git"),
|
|
"username": []byte("someUsername"),
|
|
"password": []byte("somePassword"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj/argo-cd.git", "testProject"),
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("Scoped ArgoCD"),
|
|
"url": []byte("[email protected]:argoproj/argo-cd.git"),
|
|
"username": []byte("someScopedUsername"),
|
|
"password": []byte("someScopedPassword"),
|
|
"project": []byte("testProject"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("UserManagedRepo"),
|
|
"url": []byte("[email protected]:argoproj/argoproj.git"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "other-user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("Scoped UserManagedRepo"),
|
|
"url": []byte("[email protected]:argoproj/argoproj.git"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
"project": []byte("testProject"),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
repository, err := testee.GetRepository(t.Context(), "[email protected]:argoproj/argo-cd.git", "")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, repository)
|
|
assert.Equal(t, "ArgoCD", repository.Name)
|
|
assert.Equal(t, "[email protected]:argoproj/argo-cd.git", repository.Repo)
|
|
assert.Equal(t, "someUsername", repository.Username)
|
|
assert.Equal(t, "somePassword", repository.Password)
|
|
|
|
repository, err = testee.GetRepository(t.Context(), "[email protected]:argoproj/argoproj.git", "")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, repository)
|
|
assert.Equal(t, "UserManagedRepo", repository.Name)
|
|
assert.Equal(t, "[email protected]:argoproj/argoproj.git", repository.Repo)
|
|
assert.Equal(t, "someOtherUsername", repository.Username)
|
|
assert.Equal(t, "someOtherPassword", repository.Password)
|
|
|
|
repository, err = testee.GetRepository(t.Context(), "[email protected]:argoproj/argo-cd.git", "testProject")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, repository)
|
|
assert.Equal(t, "Scoped ArgoCD", repository.Name)
|
|
assert.Equal(t, "[email protected]:argoproj/argo-cd.git", repository.Repo)
|
|
assert.Equal(t, "someScopedUsername", repository.Username)
|
|
assert.Equal(t, "someScopedPassword", repository.Password)
|
|
assert.Equal(t, "testProject", repository.Project)
|
|
|
|
repository, err = testee.GetRepository(t.Context(), "[email protected]:argoproj/argoproj.git", "testProject")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, repository)
|
|
assert.Equal(t, "Scoped UserManagedRepo", repository.Name)
|
|
assert.Equal(t, "[email protected]:argoproj/argoproj.git", repository.Repo)
|
|
assert.Equal(t, "someOtherUsername", repository.Username)
|
|
assert.Equal(t, "someOtherPassword", repository.Password)
|
|
assert.Equal(t, "testProject", repository.Project)
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_ListRepositories(t *testing.T) {
|
|
repoSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj/argo-cd.git", ""),
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("ArgoCD"),
|
|
"url": []byte("[email protected]:argoproj/argo-cd.git"),
|
|
"username": []byte("someUsername"),
|
|
"password": []byte("somePassword"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("UserManagedRepo"),
|
|
"url": []byte("[email protected]:argoproj/argoproj.git"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
repositories, err := testee.ListRepositories(t.Context(), nil)
|
|
require.NoError(t, err)
|
|
assert.Len(t, repositories, 2)
|
|
|
|
for _, repository := range repositories {
|
|
switch repository.Name {
|
|
case "ArgoCD":
|
|
assert.Equal(t, "[email protected]:argoproj/argo-cd.git", repository.Repo)
|
|
assert.Equal(t, "someUsername", repository.Username)
|
|
assert.Equal(t, "somePassword", repository.Password)
|
|
case "UserManagedRepo":
|
|
assert.Equal(t, "[email protected]:argoproj/argoproj.git", repository.Repo)
|
|
assert.Equal(t, "someOtherUsername", repository.Username)
|
|
assert.Equal(t, "someOtherPassword", repository.Password)
|
|
default:
|
|
assert.Fail(t, "unexpected repository found in list")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_UpdateRepository(t *testing.T) {
|
|
managedRepository := &appsv1.Repository{
|
|
Name: "Managed",
|
|
Repo: "[email protected]:argoproj/argo-cd.git",
|
|
Username: "someUsername",
|
|
Password: "somePassword",
|
|
}
|
|
managedProjectRepository := &appsv1.Repository{
|
|
Name: "Managed",
|
|
Repo: "[email protected]:argoproj/argo-cd.git",
|
|
Username: "someUsername",
|
|
Password: "somePassword",
|
|
Project: "someProject",
|
|
}
|
|
userProvidedRepository := &appsv1.Repository{
|
|
Name: "User Provided",
|
|
Repo: "[email protected]:argoproj/argoproj.git",
|
|
Username: "someOtherUsername",
|
|
Password: "someOtherPassword",
|
|
}
|
|
userProvidedProjectRepository := &appsv1.Repository{
|
|
Name: "User Provided",
|
|
Repo: "[email protected]:argoproj/argoproj.git",
|
|
Username: "someOtherUsername",
|
|
Password: "someOtherPassword",
|
|
Project: "someProject",
|
|
}
|
|
newRepository := &appsv1.Repository{
|
|
Name: "New",
|
|
Repo: "[email protected]:argoproj/argo-events.git",
|
|
Username: "foo",
|
|
Password: "bar",
|
|
}
|
|
|
|
managedSecretName := RepoURLToSecretName(repoSecretPrefix, managedRepository.Repo, "")
|
|
managedProjectSecretName := RepoURLToSecretName(repoSecretPrefix, managedProjectRepository.Repo, "someProject")
|
|
newSecretName := RepoURLToSecretName(repoSecretPrefix, newRepository.Repo, "")
|
|
repoSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: managedSecretName,
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte(managedRepository.Name),
|
|
"url": []byte(managedRepository.Repo),
|
|
"username": []byte(managedRepository.Username),
|
|
"password": []byte(managedRepository.Password),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: managedProjectSecretName,
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte(managedProjectRepository.Name),
|
|
"url": []byte(managedProjectRepository.Repo),
|
|
"username": []byte(managedProjectRepository.Username),
|
|
"password": []byte(managedProjectRepository.Password),
|
|
"project": []byte(managedProjectRepository.Project),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte(userProvidedRepository.Name),
|
|
"url": []byte(userProvidedRepository.Repo),
|
|
"username": []byte(userProvidedRepository.Username),
|
|
"password": []byte(userProvidedRepository.Password),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed-scoped",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte(userProvidedProjectRepository.Name),
|
|
"url": []byte(userProvidedProjectRepository.Repo),
|
|
"username": []byte(userProvidedProjectRepository.Username),
|
|
"password": []byte(userProvidedProjectRepository.Password),
|
|
"project": []byte(userProvidedProjectRepository.Project),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
managedRepository.Username = "newUsername"
|
|
updateRepository, err := testee.UpdateRepository(t.Context(), managedRepository)
|
|
require.NoError(t, err)
|
|
assert.Same(t, managedRepository, updateRepository)
|
|
assert.Equal(t, managedRepository.Username, updateRepository.Username)
|
|
|
|
secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedSecretName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Equal(t, "newUsername", string(secret.Data["username"]))
|
|
|
|
userProvidedRepository.Username = "newOtherUsername"
|
|
updateRepository, err = testee.UpdateRepository(t.Context(), userProvidedRepository)
|
|
require.NoError(t, err)
|
|
assert.Same(t, userProvidedRepository, updateRepository)
|
|
assert.Equal(t, userProvidedRepository.Username, updateRepository.Username)
|
|
|
|
secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed", metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Equal(t, "newOtherUsername", string(secret.Data["username"]))
|
|
|
|
updateRepository, err = testee.UpdateRepository(t.Context(), newRepository)
|
|
require.NoError(t, err)
|
|
assert.Same(t, newRepository, updateRepository)
|
|
|
|
secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), newSecretName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Equal(t, "foo", string(secret.Data["username"]))
|
|
|
|
managedProjectRepository.Username = "newUsername"
|
|
updateRepository, err = testee.UpdateRepository(t.Context(), managedProjectRepository)
|
|
require.NoError(t, err)
|
|
assert.Same(t, managedProjectRepository, updateRepository)
|
|
assert.Equal(t, managedProjectRepository.Username, updateRepository.Username)
|
|
|
|
secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedProjectSecretName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Equal(t, "newUsername", string(secret.Data["username"]))
|
|
|
|
userProvidedProjectRepository.Username = "newUsernameScoped"
|
|
updateRepository, err = testee.UpdateRepository(t.Context(), userProvidedProjectRepository)
|
|
require.NoError(t, err)
|
|
assert.Same(t, userProvidedProjectRepository, updateRepository)
|
|
assert.Equal(t, userProvidedProjectRepository.Username, updateRepository.Username)
|
|
|
|
secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed-scoped", metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Equal(t, "newUsernameScoped", string(secret.Data["username"]))
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_DeleteRepository(t *testing.T) {
|
|
managedSecretName := RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj/argo-cd.git", "")
|
|
managedScopedSecretName := RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj/argo-cd.git", "someProject")
|
|
repoSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: managedSecretName,
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("ArgoCD"),
|
|
"url": []byte("[email protected]:argoproj/argo-cd.git"),
|
|
"username": []byte("someUsername"),
|
|
"password": []byte("somePassword"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: managedScopedSecretName,
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("ArgoCD"),
|
|
"url": []byte("[email protected]:argoproj/argo-cd.git"),
|
|
"username": []byte("someUsername"),
|
|
"password": []byte("somePassword"),
|
|
"project": []byte("someProject"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository},
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("UserManagedRepo"),
|
|
"url": []byte("[email protected]:argoproj/argoproj.git"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
err := testee.DeleteRepository(t.Context(), "[email protected]:argoproj/argo-cd.git", "")
|
|
require.NoError(t, err)
|
|
|
|
_, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedSecretName, metav1.GetOptions{})
|
|
require.Error(t, err)
|
|
|
|
_, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedScopedSecretName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
|
|
err = testee.DeleteRepository(t.Context(), "[email protected]:argoproj/argo-cd.git", "someProject")
|
|
require.NoError(t, err)
|
|
|
|
_, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedScopedSecretName, metav1.GetOptions{})
|
|
require.Error(t, err)
|
|
|
|
err = testee.DeleteRepository(t.Context(), "[email protected]:argoproj/argoproj.git", "")
|
|
require.NoError(t, err)
|
|
|
|
secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed", metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Empty(t, secret.Labels[common.LabelValueSecretTypeRepository])
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_CreateRepoCreds(t *testing.T) {
|
|
clientset := getClientset()
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
repoCreds appsv1.RepoCreds
|
|
// Note: URL needs to be a different one for every testCase
|
|
// otherwise we would need to use the DeleteRepoCreds method to clean up the secret after each test
|
|
// which results in an unwanted dependency in a unit test
|
|
}{
|
|
{
|
|
name: "minimal_https_fields",
|
|
repoCreds: appsv1.RepoCreds{
|
|
URL: "[email protected]:argoproj",
|
|
Username: "someUsername",
|
|
Password: "somePassword",
|
|
EnableOCI: true,
|
|
},
|
|
},
|
|
{
|
|
name: "with_proxy",
|
|
repoCreds: appsv1.RepoCreds{
|
|
URL: "[email protected]:kubernetes",
|
|
Username: "anotherUsername",
|
|
Password: "anotherPassword",
|
|
Proxy: "https://proxy.argoproj.io:3128",
|
|
},
|
|
},
|
|
{
|
|
name: "with_noProxy",
|
|
repoCreds: appsv1.RepoCreds{
|
|
URL: "[email protected]:proxy",
|
|
Username: "anotherUsername",
|
|
Password: "anotherPassword",
|
|
NoProxy: ".example.com,127.0.0.1",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
output, err := testee.CreateRepoCreds(t.Context(), &testCase.repoCreds)
|
|
require.NoError(t, err)
|
|
assert.Same(t, &testCase.repoCreds, output)
|
|
|
|
secret, err := clientset.CoreV1().Secrets(testNamespace).Get(
|
|
t.Context(),
|
|
RepoURLToSecretName(credSecretPrefix, testCase.repoCreds.URL, ""),
|
|
metav1.GetOptions{},
|
|
)
|
|
assert.NotNil(t, secret)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy])
|
|
assert.Equal(t, common.LabelValueSecretTypeRepoCreds, secret.Labels[common.LabelKeySecretType])
|
|
|
|
// check every possible field of the secret if it has the same (default) value as the repoCred struct
|
|
// non-string fields must be parsed so that their default value matches the one of the corresponding type
|
|
assert.Equal(t, testCase.repoCreds.URL, string(secret.Data["url"]))
|
|
assert.Equal(t, testCase.repoCreds.Username, string(secret.Data["username"]))
|
|
assert.Equal(t, testCase.repoCreds.Password, string(secret.Data["password"]))
|
|
if enableOCI, err := strconv.ParseBool(string(secret.Data["githubAppPrivateKey"])); err == nil {
|
|
assert.Equal(t, strconv.FormatBool(testCase.repoCreds.EnableOCI), enableOCI)
|
|
}
|
|
assert.Equal(t, testCase.repoCreds.SSHPrivateKey, string(secret.Data["sshPrivateKey"]))
|
|
assert.Equal(t, testCase.repoCreds.TLSClientCertData, string(secret.Data["tlsClientCertData"]))
|
|
assert.Equal(t, testCase.repoCreds.TLSClientCertKey, string(secret.Data["tlsClientCertKey"]))
|
|
assert.Equal(t, testCase.repoCreds.Type, string(secret.Data["type"]))
|
|
assert.Equal(t, testCase.repoCreds.GithubAppPrivateKey, string(secret.Data["githubAppPrivateKey"]))
|
|
if githubAppPrivateKey, err := strconv.ParseInt(string(secret.Data["githubAppPrivateKey"]), 10, 64); err == nil {
|
|
assert.Equal(t, testCase.repoCreds.GithubAppId, githubAppPrivateKey)
|
|
}
|
|
if githubAppID, err := strconv.ParseInt(string(secret.Data["githubAppId"]), 10, 64); err == nil {
|
|
assert.Equal(t, testCase.repoCreds.GithubAppInstallationId, githubAppID)
|
|
}
|
|
assert.Equal(t, testCase.repoCreds.GitHubAppEnterpriseBaseURL, string(secret.Data["githubAppEnterpriseUrl"]))
|
|
assert.Equal(t, testCase.repoCreds.Proxy, string(secret.Data["proxy"]))
|
|
assert.Equal(t, testCase.repoCreds.NoProxy, string(secret.Data["noProxy"]))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_GetRepoCreds(t *testing.T) {
|
|
repoCredSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj", ""),
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]:argoproj"),
|
|
"username": []byte("someUsername"),
|
|
"password": []byte("somePassword"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "proxy-repo",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
"proxy": []byte("https://proxy.argoproj.io:3128"),
|
|
"noProxy": []byte(".example.com,127.0.0.1"),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoCredSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
repoCred, err := testee.GetRepoCreds(t.Context(), "[email protected]:argoproj")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, repoCred)
|
|
assert.Equal(t, "[email protected]:argoproj", repoCred.URL)
|
|
assert.Equal(t, "someUsername", repoCred.Username)
|
|
assert.Equal(t, "somePassword", repoCred.Password)
|
|
|
|
repoCred, err = testee.GetRepoCreds(t.Context(), "[email protected]")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, repoCred)
|
|
assert.Equal(t, "[email protected]", repoCred.URL)
|
|
assert.Equal(t, "someOtherUsername", repoCred.Username)
|
|
assert.Equal(t, "someOtherPassword", repoCred.Password)
|
|
if repoCred.Proxy != "" {
|
|
assert.Equal(t, "https://proxy.argoproj.io:3128", repoCred.Proxy)
|
|
}
|
|
if repoCred.NoProxy != "" {
|
|
assert.Equal(t, ".example.com,127.0.0.1", repoCred.NoProxy)
|
|
}
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_ListRepoCreds(t *testing.T) {
|
|
repoCredSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj", ""),
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]:argoproj"),
|
|
"username": []byte("someUsername"),
|
|
"password": []byte("somePassword"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoCredSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
repoCreds, err := testee.ListRepoCreds(t.Context())
|
|
require.NoError(t, err)
|
|
assert.Len(t, repoCreds, 2)
|
|
assert.Contains(t, repoCreds, "[email protected]:argoproj")
|
|
assert.Contains(t, repoCreds, "[email protected]")
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_UpdateRepoCreds(t *testing.T) {
|
|
managedCreds := &appsv1.RepoCreds{
|
|
URL: "[email protected]:argoproj",
|
|
Username: "someUsername",
|
|
Password: "somePassword",
|
|
}
|
|
userProvidedCreds := &appsv1.RepoCreds{
|
|
URL: "[email protected]",
|
|
Username: "someOtherUsername",
|
|
Password: "someOtherPassword",
|
|
}
|
|
newCreds := &appsv1.RepoCreds{
|
|
URL: "[email protected]:foobar",
|
|
Username: "foo",
|
|
Password: "bar",
|
|
}
|
|
|
|
managedCredsName := RepoURLToSecretName(credSecretPrefix, managedCreds.URL, "")
|
|
newCredsName := RepoURLToSecretName(credSecretPrefix, newCreds.URL, "")
|
|
repoCredSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: managedCredsName,
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte(managedCreds.URL),
|
|
"username": []byte(managedCreds.Username),
|
|
"password": []byte(managedCreds.Password),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte(userProvidedCreds.URL),
|
|
"username": []byte(userProvidedCreds.Username),
|
|
"password": []byte(userProvidedCreds.Password),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoCredSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
managedCreds.Username = "newUsername"
|
|
updateRepoCreds, err := testee.UpdateRepoCreds(t.Context(), managedCreds)
|
|
require.NoError(t, err)
|
|
assert.NotSame(t, managedCreds, updateRepoCreds)
|
|
assert.Equal(t, managedCreds.Username, updateRepoCreds.Username)
|
|
|
|
secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedCredsName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Equal(t, "newUsername", string(secret.Data["username"]))
|
|
|
|
userProvidedCreds.Username = "newOtherUsername"
|
|
updateRepoCreds, err = testee.UpdateRepoCreds(t.Context(), userProvidedCreds)
|
|
require.NoError(t, err)
|
|
assert.NotSame(t, userProvidedCreds, updateRepoCreds)
|
|
assert.Equal(t, userProvidedCreds.Username, updateRepoCreds.Username)
|
|
|
|
secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed", metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Equal(t, "newOtherUsername", string(secret.Data["username"]))
|
|
|
|
updateRepoCreds, err = testee.UpdateRepoCreds(t.Context(), newCreds)
|
|
require.NoError(t, err)
|
|
assert.Same(t, newCreds, updateRepoCreds)
|
|
|
|
secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), newCredsName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Equal(t, "foo", string(secret.Data["username"]))
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_DeleteRepoCreds(t *testing.T) {
|
|
managedSecretName := RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj/argo-cd.git", "")
|
|
repoSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: managedSecretName,
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]:argoproj"),
|
|
"username": []byte("someUsername"),
|
|
"password": []byte("somePassword"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: "user-managed",
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
err := testee.DeleteRepoCreds(t.Context(), "[email protected]:argoproj")
|
|
require.NoError(t, err)
|
|
|
|
_, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), managedSecretName, metav1.GetOptions{})
|
|
require.Error(t, err)
|
|
|
|
err = testee.DeleteRepoCreds(t.Context(), "[email protected]")
|
|
require.NoError(t, err)
|
|
|
|
secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "user-managed", metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, secret)
|
|
assert.Empty(t, secret.Labels[common.LabelValueSecretTypeRepoCreds])
|
|
}
|
|
|
|
func TestSecretsRepositoryBackend_GetAllHelmRepoCreds(t *testing.T) {
|
|
repoCredSecrets := []runtime.Object{
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj", ""),
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]:argoproj"),
|
|
"username": []byte("someUsername"),
|
|
"password": []byte("somePassword"),
|
|
"type": []byte("helm"),
|
|
},
|
|
},
|
|
&corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: testNamespace,
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]", ""),
|
|
Annotations: map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD},
|
|
Labels: map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]"),
|
|
"username": []byte("someOtherUsername"),
|
|
"password": []byte("someOtherPassword"),
|
|
"type": []byte("git"),
|
|
},
|
|
},
|
|
}
|
|
|
|
clientset := getClientset(repoCredSecrets...)
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
|
|
repoCreds, err := testee.GetAllHelmRepoCreds(t.Context())
|
|
require.NoError(t, err)
|
|
assert.Len(t, repoCreds, 1)
|
|
}
|
|
|
|
func TestRepoCredsToSecret(t *testing.T) {
|
|
clientset := getClientset()
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
s := &corev1.Secret{}
|
|
creds := &appsv1.RepoCreds{
|
|
URL: "URL",
|
|
Username: "Username",
|
|
Password: "Password",
|
|
SSHPrivateKey: "SSHPrivateKey",
|
|
EnableOCI: true,
|
|
TLSClientCertData: "TLSClientCertData",
|
|
TLSClientCertKey: "TLSClientCertKey",
|
|
Type: "Type",
|
|
GithubAppPrivateKey: "GithubAppPrivateKey",
|
|
GithubAppId: 123,
|
|
GithubAppInstallationId: 456,
|
|
GitHubAppEnterpriseBaseURL: "GitHubAppEnterpriseBaseURL",
|
|
}
|
|
s = testee.repoCredsToSecret(creds, s)
|
|
assert.Equal(t, []byte(creds.URL), s.Data["url"])
|
|
assert.Equal(t, []byte(creds.Username), s.Data["username"])
|
|
assert.Equal(t, []byte(creds.Password), s.Data["password"])
|
|
assert.Equal(t, []byte(creds.SSHPrivateKey), s.Data["sshPrivateKey"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(creds.EnableOCI)), s.Data["enableOCI"])
|
|
assert.Equal(t, []byte(creds.TLSClientCertData), s.Data["tlsClientCertData"])
|
|
assert.Equal(t, []byte(creds.TLSClientCertKey), s.Data["tlsClientCertKey"])
|
|
assert.Equal(t, []byte(creds.Type), s.Data["type"])
|
|
assert.Equal(t, []byte(creds.GithubAppPrivateKey), s.Data["githubAppPrivateKey"])
|
|
assert.Equal(t, []byte(strconv.FormatInt(creds.GithubAppId, 10)), s.Data["githubAppID"])
|
|
assert.Equal(t, []byte(strconv.FormatInt(creds.GithubAppInstallationId, 10)), s.Data["githubAppInstallationID"])
|
|
assert.Equal(t, []byte(creds.GitHubAppEnterpriseBaseURL), s.Data["githubAppEnterpriseBaseUrl"])
|
|
assert.Equal(t, map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD}, s.Annotations)
|
|
assert.Equal(t, map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds}, s.Labels)
|
|
}
|
|
|
|
func TestRepoWriteCredsToSecret(t *testing.T) {
|
|
clientset := getClientset()
|
|
testee := &secretsRepositoryBackend{
|
|
db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
},
|
|
writeCreds: true,
|
|
}
|
|
s := &corev1.Secret{}
|
|
creds := &appsv1.RepoCreds{
|
|
URL: "URL",
|
|
Username: "Username",
|
|
Password: "Password",
|
|
SSHPrivateKey: "SSHPrivateKey",
|
|
EnableOCI: true,
|
|
TLSClientCertData: "TLSClientCertData",
|
|
TLSClientCertKey: "TLSClientCertKey",
|
|
Type: "Type",
|
|
GithubAppPrivateKey: "GithubAppPrivateKey",
|
|
GithubAppId: 123,
|
|
GithubAppInstallationId: 456,
|
|
GitHubAppEnterpriseBaseURL: "GitHubAppEnterpriseBaseURL",
|
|
}
|
|
s = testee.repoCredsToSecret(creds, s)
|
|
assert.Equal(t, []byte(creds.URL), s.Data["url"])
|
|
assert.Equal(t, []byte(creds.Username), s.Data["username"])
|
|
assert.Equal(t, []byte(creds.Password), s.Data["password"])
|
|
assert.Equal(t, []byte(creds.SSHPrivateKey), s.Data["sshPrivateKey"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(creds.EnableOCI)), s.Data["enableOCI"])
|
|
assert.Equal(t, []byte(creds.TLSClientCertData), s.Data["tlsClientCertData"])
|
|
assert.Equal(t, []byte(creds.TLSClientCertKey), s.Data["tlsClientCertKey"])
|
|
assert.Equal(t, []byte(creds.Type), s.Data["type"])
|
|
assert.Equal(t, []byte(creds.GithubAppPrivateKey), s.Data["githubAppPrivateKey"])
|
|
assert.Equal(t, []byte(strconv.FormatInt(creds.GithubAppId, 10)), s.Data["githubAppID"])
|
|
assert.Equal(t, []byte(strconv.FormatInt(creds.GithubAppInstallationId, 10)), s.Data["githubAppInstallationID"])
|
|
assert.Equal(t, []byte(creds.GitHubAppEnterpriseBaseURL), s.Data["githubAppEnterpriseBaseUrl"])
|
|
assert.Equal(t, map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD}, s.Annotations)
|
|
assert.Equal(t, map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepoCredsWrite}, s.Labels)
|
|
}
|
|
|
|
func TestRaceConditionInRepoCredsOperations(t *testing.T) {
|
|
// Create a single shared secret that will be accessed concurrently
|
|
sharedSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj/argo-cd.git", ""),
|
|
Namespace: testNamespace,
|
|
Labels: map[string]string{
|
|
common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds,
|
|
},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]:argoproj/argo-cd.git"),
|
|
"username": []byte("test-user"),
|
|
"password": []byte("test-pass"),
|
|
},
|
|
}
|
|
|
|
// Create test credentials that we'll use for conversion
|
|
repoCreds := &appsv1.RepoCreds{
|
|
URL: "[email protected]:argoproj/argo-cd.git",
|
|
Username: "test-user",
|
|
Password: "test-pass",
|
|
}
|
|
|
|
backend := &secretsRepositoryBackend{}
|
|
|
|
var wg sync.WaitGroup
|
|
concurrentOps := 50
|
|
errChan := make(chan error, concurrentOps*2) // Channel to collect errors
|
|
|
|
// Launch goroutines that perform concurrent operations
|
|
for range concurrentOps {
|
|
wg.Add(2)
|
|
|
|
// One goroutine converts from RepoCreds to Secret
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
errChan <- fmt.Errorf("panic in repoCredsToSecret: %v", r)
|
|
}
|
|
}()
|
|
_ = backend.repoCredsToSecret(repoCreds, sharedSecret)
|
|
}()
|
|
|
|
// Another goroutine converts from Secret to RepoCreds
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
errChan <- fmt.Errorf("panic in secretToRepoCred: %v", r)
|
|
}
|
|
}()
|
|
creds, err := backend.secretToRepoCred(sharedSecret)
|
|
if err != nil {
|
|
errChan <- fmt.Errorf("error in secretToRepoCred: %w", err)
|
|
return
|
|
}
|
|
// Verify data integrity
|
|
if creds.URL != repoCreds.URL || creds.Username != repoCreds.Username || creds.Password != repoCreds.Password {
|
|
errChan <- fmt.Errorf("data mismatch in conversion: expected %v, got %v", repoCreds, creds)
|
|
}
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
close(errChan)
|
|
|
|
// Check for any errors that occurred during concurrent operations
|
|
for err := range errChan {
|
|
t.Errorf("concurrent operation error: %v", err)
|
|
}
|
|
|
|
// Verify final state
|
|
finalCreds, err := backend.secretToRepoCred(sharedSecret)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, repoCreds.URL, finalCreds.URL)
|
|
assert.Equal(t, repoCreds.Username, finalCreds.Username)
|
|
assert.Equal(t, repoCreds.Password, finalCreds.Password)
|
|
}
|
|
|
|
func TestRaceConditionInRepositoryOperations(t *testing.T) {
|
|
// Create a single shared secret that will be accessed concurrently
|
|
sharedSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: RepoURLToSecretName(repoSecretPrefix, "[email protected]:argoproj/argo-cd.git", ""),
|
|
Namespace: testNamespace,
|
|
Labels: map[string]string{
|
|
common.LabelKeySecretType: common.LabelValueSecretTypeRepository,
|
|
},
|
|
},
|
|
Data: map[string][]byte{
|
|
"url": []byte("[email protected]:argoproj/argo-cd.git"),
|
|
"name": []byte("test-repo"),
|
|
"username": []byte("test-user"),
|
|
"password": []byte("test-pass"),
|
|
},
|
|
}
|
|
|
|
// Create test repository that we'll use for conversion
|
|
repo := &appsv1.Repository{
|
|
Name: "test-repo",
|
|
Repo: "[email protected]:argoproj/argo-cd.git",
|
|
Username: "test-user",
|
|
Password: "test-pass",
|
|
}
|
|
|
|
backend := &secretsRepositoryBackend{}
|
|
|
|
var wg sync.WaitGroup
|
|
concurrentOps := 50
|
|
errChan := make(chan error, concurrentOps*2) // Channel to collect errors
|
|
|
|
// Launch goroutines that perform concurrent operations
|
|
for range concurrentOps {
|
|
wg.Add(2)
|
|
|
|
// One goroutine converts from Repository to Secret
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
errChan <- fmt.Errorf("panic in repositoryToSecret: %v", r)
|
|
}
|
|
}()
|
|
_ = backend.repositoryToSecret(repo, sharedSecret)
|
|
}()
|
|
|
|
// Another goroutine converts from Secret to Repository
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
errChan <- fmt.Errorf("panic in secretToRepository: %v", r)
|
|
}
|
|
}()
|
|
repository, err := secretToRepository(sharedSecret)
|
|
if err != nil {
|
|
errChan <- fmt.Errorf("error in secretToRepository: %w", err)
|
|
return
|
|
}
|
|
// Verify data integrity
|
|
if repository.Name != repo.Name || repository.Repo != repo.Repo ||
|
|
repository.Username != repo.Username || repository.Password != repo.Password {
|
|
errChan <- fmt.Errorf("data mismatch in conversion: expected %v, got %v", repo, repository)
|
|
}
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
close(errChan)
|
|
|
|
// Check for any errors that occurred during concurrent operations
|
|
for err := range errChan {
|
|
t.Errorf("concurrent operation error: %v", err)
|
|
}
|
|
|
|
// Verify final state
|
|
finalRepo, err := secretToRepository(sharedSecret)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, repo.Name, finalRepo.Name)
|
|
assert.Equal(t, repo.Repo, finalRepo.Repo)
|
|
assert.Equal(t, repo.Username, finalRepo.Username)
|
|
assert.Equal(t, repo.Password, finalRepo.Password)
|
|
}
|
|
|
|
func TestCreateReadAndWriteSecretForSameURL(t *testing.T) {
|
|
clientset := getClientset()
|
|
settingsMgr := settings.NewSettingsManager(t.Context(), clientset, testNamespace)
|
|
|
|
repo := &appsv1.Repository{
|
|
Name: "TestRepo",
|
|
Repo: "[email protected]:argoproj/argo-cd.git",
|
|
Username: "user",
|
|
Password: "pass",
|
|
}
|
|
|
|
// Create read secret
|
|
readBackend := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settingsMgr,
|
|
}, writeCreds: false}
|
|
_, err := readBackend.CreateRepository(t.Context(), repo)
|
|
require.NoError(t, err)
|
|
|
|
// Create write secret
|
|
writeBackend := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settingsMgr,
|
|
}, writeCreds: true}
|
|
_, err = writeBackend.CreateRepository(t.Context(), repo)
|
|
require.NoError(t, err)
|
|
|
|
// Assert both secrets exist
|
|
readSecretName := RepoURLToSecretName(repoSecretPrefix, repo.Repo, repo.Project)
|
|
writeSecretName := RepoURLToSecretName(repoWriteSecretPrefix, repo.Repo, repo.Project)
|
|
|
|
readSecret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), readSecretName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, common.LabelValueSecretTypeRepository, readSecret.Labels[common.LabelKeySecretType])
|
|
|
|
writeSecret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), writeSecretName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, common.LabelValueSecretTypeRepositoryWrite, writeSecret.Labels[common.LabelKeySecretType])
|
|
}
|
|
|
|
func TestRepositoryToSecret(t *testing.T) {
|
|
clientset := getClientset()
|
|
testee := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
|
}}
|
|
s := &corev1.Secret{}
|
|
repo := &appsv1.Repository{
|
|
Name: "Name",
|
|
Repo: "[email protected]:argoproj/argo-cd.git",
|
|
Username: "Username",
|
|
Password: "Password",
|
|
SSHPrivateKey: "SSHPrivateKey",
|
|
InsecureIgnoreHostKey: true,
|
|
Insecure: true,
|
|
EnableLFS: true,
|
|
EnableOCI: true,
|
|
InsecureOCIForceHttp: true,
|
|
TLSClientCertData: "TLSClientCertData",
|
|
TLSClientCertKey: "TLSClientCertKey",
|
|
Type: "Type",
|
|
GithubAppPrivateKey: "GithubAppPrivateKey",
|
|
GithubAppId: 123,
|
|
GithubAppInstallationId: 456,
|
|
GitHubAppEnterpriseBaseURL: "GitHubAppEnterpriseBaseURL",
|
|
Proxy: "Proxy",
|
|
NoProxy: "NoProxy",
|
|
Project: "Project",
|
|
GCPServiceAccountKey: "GCPServiceAccountKey",
|
|
ForceHttpBasicAuth: true,
|
|
UseAzureWorkloadIdentity: true,
|
|
Depth: 1,
|
|
WebhookManifestCacheWarmDisabled: true,
|
|
}
|
|
s = testee.repositoryToSecret(repo, s)
|
|
assert.Equal(t, []byte(repo.Name), s.Data["name"])
|
|
assert.Equal(t, []byte(repo.Repo), s.Data["url"])
|
|
assert.Equal(t, []byte(repo.Username), s.Data["username"])
|
|
assert.Equal(t, []byte(repo.Password), s.Data["password"])
|
|
assert.Equal(t, []byte(repo.SSHPrivateKey), s.Data["sshPrivateKey"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(repo.InsecureIgnoreHostKey)), s.Data["insecureIgnoreHostKey"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(repo.Insecure)), s.Data["insecure"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(repo.EnableLFS)), s.Data["enableLfs"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(repo.EnableOCI)), s.Data["enableOCI"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(repo.InsecureOCIForceHttp)), s.Data["insecureOCIForceHttp"])
|
|
assert.Equal(t, []byte(repo.TLSClientCertData), s.Data["tlsClientCertData"])
|
|
assert.Equal(t, []byte(repo.TLSClientCertKey), s.Data["tlsClientCertKey"])
|
|
assert.Equal(t, []byte(repo.Type), s.Data["type"])
|
|
assert.Equal(t, []byte(repo.GithubAppPrivateKey), s.Data["githubAppPrivateKey"])
|
|
assert.Equal(t, []byte(strconv.FormatInt(repo.GithubAppId, 10)), s.Data["githubAppID"])
|
|
assert.Equal(t, []byte(strconv.FormatInt(repo.GithubAppInstallationId, 10)), s.Data["githubAppInstallationID"])
|
|
assert.Equal(t, []byte(repo.GitHubAppEnterpriseBaseURL), s.Data["githubAppEnterpriseBaseUrl"])
|
|
assert.Equal(t, []byte(repo.Proxy), s.Data["proxy"])
|
|
assert.Equal(t, []byte(repo.NoProxy), s.Data["noProxy"])
|
|
assert.Equal(t, []byte(repo.Project), s.Data["project"])
|
|
assert.Equal(t, []byte(repo.GCPServiceAccountKey), s.Data["gcpServiceAccountKey"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(repo.ForceHttpBasicAuth)), s.Data["forceHttpBasicAuth"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(repo.UseAzureWorkloadIdentity)), s.Data["useAzureWorkloadIdentity"])
|
|
assert.Equal(t, []byte(strconv.FormatInt(repo.Depth, 10)), s.Data["depth"])
|
|
assert.Equal(t, []byte(strconv.FormatBool(repo.WebhookManifestCacheWarmDisabled)), s.Data["webhookManifestCacheWarmDisabled"])
|
|
assert.Equal(t, map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD}, s.Annotations)
|
|
assert.Equal(t, map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository}, s.Labels)
|
|
}
|
|
|
|
func TestCreateReadAndWriteRepoCredsSecretForSameURL(t *testing.T) {
|
|
clientset := getClientset()
|
|
settingsMgr := settings.NewSettingsManager(t.Context(), clientset, testNamespace)
|
|
|
|
creds := &appsv1.RepoCreds{
|
|
URL: "[email protected]:argoproj/argo-cd.git",
|
|
Username: "user",
|
|
Password: "pass",
|
|
}
|
|
|
|
// Create read creds secret
|
|
readBackend := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settingsMgr,
|
|
}, writeCreds: false}
|
|
_, err := readBackend.CreateRepoCreds(t.Context(), creds)
|
|
require.NoError(t, err)
|
|
|
|
// Create write creds secret
|
|
writeBackend := &secretsRepositoryBackend{db: &db{
|
|
ns: testNamespace,
|
|
kubeclientset: clientset,
|
|
settingsMgr: settingsMgr,
|
|
}, writeCreds: true}
|
|
_, err = writeBackend.CreateRepoCreds(t.Context(), creds)
|
|
require.NoError(t, err)
|
|
|
|
// Assert both secrets exist
|
|
readSecretName := RepoURLToSecretName(credSecretPrefix, creds.URL, "")
|
|
writeSecretName := RepoURLToSecretName(credWriteSecretPrefix, creds.URL, "")
|
|
|
|
readSecret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), readSecretName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, common.LabelValueSecretTypeRepoCreds, readSecret.Labels[common.LabelKeySecretType])
|
|
|
|
writeSecret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), writeSecretName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, common.LabelValueSecretTypeRepoCredsWrite, writeSecret.Labels[common.LabelKeySecretType])
|
|
}
|