From 9afa8333b4bda60c80f857a0e75c2fd6d895f6a7 Mon Sep 17 00:00:00 2001 From: jannfis Date: Wed, 7 Apr 2021 14:49:17 +0200 Subject: [PATCH] chore: Make e2e tests runnable against remote cluster (#5895) * chore: Make e2e tests runnable against remote cluster Signed-off-by: jannfis * Fix linter complaint Signed-off-by: jannfis * Revert Signed-off-by: jannfis * Address reviewer comments Signed-off-by: jannfis * Compat with Mac Signed-off-by: jannfis * Revert test setting Signed-off-by: jannfis --- Makefile | 4 +- test/e2e/app_management_test.go | 15 +- test/e2e/fixture/app/context.go | 6 +- test/e2e/fixture/certs/certs.go | 48 +++- test/e2e/fixture/fixture.go | 263 +++++++++++++++--- test/e2e/fixture/gpgkeys/gpgkeys.go | 19 +- test/e2e/helm_test.go | 11 +- test/e2e/kustomize_test.go | 2 +- test/e2e/repo_creds_test.go | 8 +- test/e2e/selective_sync_test.go | 8 +- test/e2e/sync_options_test.go | 1 + test/e2e/testdata/helm-repo/index.yaml | 6 +- .../testdata/helm-repo/local/helm-1.0.0.tgz | 0 .../testdata/helm-repo/local/helm2-1.0.0.tgz | 0 test/e2e/testdata/helm-repo/local/index.yaml | 13 + .../testdata/helm-repo/remote/helm-1.0.0.tgz | 0 .../testdata/helm-repo/remote/helm2-1.0.0.tgz | 0 test/e2e/testdata/helm-repo/remote/index.yaml | 13 + .../{ => local}/kustomization.yaml | 0 .../remote/kustomization.yaml | 7 + .../{ => local}/kustomization.yaml | 0 .../remote/kustomization.yaml | 5 + test/fixture/certs/argocd-e2e-server.crt | 18 ++ test/fixture/certs/argocd-e2e-server.csr | 16 ++ test/fixture/certs/argocd-e2e-server.key | 27 ++ test/fixture/testrepos/nginx.conf | 58 +++- test/fixture/testrepos/ssh_known_hosts | 5 + test/remote/Dockerfile | 86 ++++++ test/remote/Makefile | 25 ++ test/remote/Procfile | 3 + test/remote/README.md | 240 ++++++++++++++++ test/remote/argocd-remote-permissions.yaml | 39 +++ test/remote/generate-permissions.sh | 30 ++ test/remote/manifests/e2e-repositories.yaml | 63 +++++ test/remote/manifests/kustomization.yaml | 9 + test/remote/run-e2e-remote.sh | 66 +++++ test/remote/uid_entrypoint.sh | 12 + 37 files changed, 1050 insertions(+), 76 deletions(-) create mode 100644 test/e2e/testdata/helm-repo/local/helm-1.0.0.tgz create mode 100644 test/e2e/testdata/helm-repo/local/helm2-1.0.0.tgz create mode 100644 test/e2e/testdata/helm-repo/local/index.yaml create mode 100644 test/e2e/testdata/helm-repo/remote/helm-1.0.0.tgz create mode 100644 test/e2e/testdata/helm-repo/remote/helm2-1.0.0.tgz create mode 100644 test/e2e/testdata/helm-repo/remote/index.yaml rename test/e2e/testdata/https-kustomize-base/{ => local}/kustomization.yaml (100%) create mode 100644 test/e2e/testdata/https-kustomize-base/remote/kustomization.yaml rename test/e2e/testdata/ssh-kustomize-base/{ => local}/kustomization.yaml (100%) create mode 100644 test/e2e/testdata/ssh-kustomize-base/remote/kustomization.yaml create mode 100644 test/fixture/certs/argocd-e2e-server.crt create mode 100644 test/fixture/certs/argocd-e2e-server.csr create mode 100644 test/fixture/certs/argocd-e2e-server.key create mode 100644 test/remote/Dockerfile create mode 100644 test/remote/Makefile create mode 100644 test/remote/Procfile create mode 100644 test/remote/README.md create mode 100644 test/remote/argocd-remote-permissions.yaml create mode 100755 test/remote/generate-permissions.sh create mode 100644 test/remote/manifests/e2e-repositories.yaml create mode 100644 test/remote/manifests/kustomization.yaml create mode 100755 test/remote/run-e2e-remote.sh create mode 100755 test/remote/uid_entrypoint.sh diff --git a/Makefile b/Makefile index 69efc88b521e1..3c84acb50ef08 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,8 @@ ARGOCD_E2E_DEX_PORT?=5556 ARGOCD_E2E_YARN_HOST?=localhost ARGOCD_E2E_DISABLE_AUTH?= +ARGOCD_E2E_TEST_TIMEOUT?=20m + ARGOCD_IN_CI?=false ARGOCD_TEST_E2E?=true @@ -399,7 +401,7 @@ test-e2e: test-e2e-local: cli-local # NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system export GO111MODULE=off - ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout 20m -v ./test/e2e + ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e # Spawns a shell in the test server container for debugging purposes debug-test-server: test-tools-image diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 6b9079eb377c0..1aaa39f97e054 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -49,6 +49,7 @@ const ( ) func TestSyncToUnsignedCommit(t *testing.T) { + SkipOnEnv(t, "GPG") Given(t). Project("gpg"). Path(guestbookPath). @@ -63,6 +64,7 @@ func TestSyncToUnsignedCommit(t *testing.T) { } func TestSyncToSignedCommitWithoutKnownKey(t *testing.T) { + SkipOnEnv(t, "GPG") Given(t). Project("gpg"). Path(guestbookPath). @@ -78,6 +80,7 @@ func TestSyncToSignedCommitWithoutKnownKey(t *testing.T) { } func TestSyncToSignedCommitKeyWithKnownKey(t *testing.T) { + SkipOnEnv(t, "GPG") Given(t). Project("gpg"). Path(guestbookPath). @@ -140,6 +143,7 @@ func TestAppCreation(t *testing.T) { // demonstrate that we cannot use a standard sync when an immutable field is changed, we must use "force" func TestImmutableChange(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") text := FailOnErr(Run(".", "kubectl", "get", "service", "-n", "kube-system", "kube-dns", "-o", "jsonpath={.spec.clusterIP}")).(string) parts := strings.Split(text, ".") n := rand.Intn(254) @@ -600,6 +604,7 @@ func testEdgeCasesApplicationResources(t *testing.T, appPath string, statusCode } func TestKsonnetApp(t *testing.T) { + SkipOnEnv(t, "KSONNET") Given(t). Path("ksonnet"). Env("prod"). @@ -1008,6 +1013,7 @@ func TestRevisionHistoryLimit(t *testing.T) { } func TestOrphanedResource(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") Given(t). ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, @@ -1266,6 +1272,7 @@ func TestCreateAppWithNoNameSpaceWhenRequired2(t *testing.T) { } func TestListResource(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") Given(t). ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, @@ -1327,10 +1334,13 @@ func TestListResource(t *testing.T) { // application sync successful // when application is deleted, --dest-namespace is not deleted func TestNamespaceAutoCreation(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") updatedNamespace := getNewNamespace(t) defer func() { - _, err := Run("", "kubectl", "delete", "namespace", updatedNamespace) - assert.NoError(t, err) + if !t.Skipped() { + _, err := Run("", "kubectl", "delete", "namespace", updatedNamespace) + assert.NoError(t, err) + } }() Given(t). Timeout(30). @@ -1516,6 +1526,7 @@ definitions: } func TestAppLogs(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") Given(t). Path("guestbook-logs"). When(). diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index 4c68c888f21d7..2f005689234f3 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -10,6 +10,7 @@ import ( "github.com/argoproj/argo-cd/v2/test/e2e/fixture/certs" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/gpgkeys" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" + "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/settings" ) @@ -40,7 +41,10 @@ type Context struct { func Given(t *testing.T) *Context { fixture.EnsureCleanState(t) - return &Context{t: t, destServer: KubernetesInternalAPIServerAddr, repoURLType: fixture.RepoURLTypeFile, name: fixture.Name(), timeout: 10, project: "default", prune: true} + // ARGOCE_E2E_DEFAULT_TIMEOUT can be used to override the default timeout + // for any context. + timeout := env.ParseNumFromEnv("ARGOCD_E2E_DEFAULT_TIMEOUT", 10, 0, 180) + return &Context{t: t, destServer: KubernetesInternalAPIServerAddr, repoURLType: fixture.RepoURLTypeFile, name: fixture.Name(), timeout: timeout, project: "default", prune: true} } func (c *Context) GPGPublicKeyAdded() *Context { diff --git a/test/e2e/fixture/certs/certs.go b/test/e2e/fixture/certs/certs.go index 0b25f1217811f..c20a000cf325b 100644 --- a/test/e2e/fixture/certs/certs.go +++ b/test/e2e/fixture/certs/certs.go @@ -14,19 +14,32 @@ import ( func AddCustomCACert() { caCertPath, err := filepath.Abs("../fixture/certs/argocd-test-ca.crt") errors.CheckError(err) - args := []string{"cert", "add-tls", "localhost", "--from", caCertPath} - errors.FailOnErr(fixture.RunCli(args...)) - args = []string{"cert", "add-tls", "127.0.0.1", "--from", caCertPath} - errors.FailOnErr(fixture.RunCli(args...)) + // We need to setup TLS certs according to whether we are running tests + // against a local workload (repositories available as localhost) and + // against remote workloads (repositories available as argocd-e2e-server) + if fixture.IsLocal() { + args := []string{"cert", "add-tls", "localhost", "--from", caCertPath} + errors.FailOnErr(fixture.RunCli(args...)) + args = []string{"cert", "add-tls", "127.0.0.1", "--from", caCertPath} + errors.FailOnErr(fixture.RunCli(args...)) + certData, err := ioutil.ReadFile(caCertPath) + errors.CheckError(err) + err = ioutil.WriteFile(fixture.TmpDir+"/app/config/tls/localhost", certData, 0644) + errors.CheckError(err) + err = ioutil.WriteFile(fixture.TmpDir+"/app/config/tls/127.0.0.1", certData, 0644) + errors.CheckError(err) + } else { + args := []string{"cert", "add-tls", "argocd-e2e-server", "--from", caCertPath} + errors.FailOnErr(fixture.RunCli(args...)) + fixture.RestartAPIServer() + fixture.RestartRepoServer() + } - certData, err := ioutil.ReadFile(caCertPath) - errors.CheckError(err) - err = ioutil.WriteFile(fixture.TmpDir+"/app/config/tls/localhost", certData, 0644) - errors.CheckError(err) - err = ioutil.WriteFile(fixture.TmpDir+"/app/config/tls/127.0.0.1", certData, 0644) - errors.CheckError(err) } +// AddCustomSSHKnownHostsKeys adds SSH known hosts data to the Argo CD server +// being tested against. The env ARGOCD_E2E_SSH_KNOWN_HOSTS lets you specify +// an optional path to the known hosts file, instead of using the default one. func AddCustomSSHKnownHostsKeys() { source := os.Getenv("ARGOCD_E2E_SSH_KNOWN_HOSTS") if source == "" { @@ -34,11 +47,16 @@ func AddCustomSSHKnownHostsKeys() { } knownHostsPath, err := filepath.Abs(source) errors.CheckError(err) - args := []string{"cert", "add-ssh", "--batch", "--from", knownHostsPath} + args := []string{"cert", "add-ssh", "--upsert", "--batch", "--from", knownHostsPath} errors.FailOnErr(fixture.RunCli(args...)) - knownHostsData, err := ioutil.ReadFile(knownHostsPath) - errors.CheckError(err) - err = ioutil.WriteFile(fixture.TmpDir+"/app/config/ssh/ssh_known_hosts", knownHostsData, 0644) - errors.CheckError(err) + if fixture.IsLocal() { + knownHostsData, err := ioutil.ReadFile(knownHostsPath) + errors.CheckError(err) + err = ioutil.WriteFile(fixture.TmpDir+"/app/config/ssh/ssh_known_hosts", knownHostsData, 0644) + errors.CheckError(err) + } else { + fixture.RestartAPIServer() + fixture.RestartRepoServer() + } } diff --git a/test/e2e/fixture/fixture.go b/test/e2e/fixture/fixture.go index 205a7dfab6413..242461371b284 100644 --- a/test/e2e/fixture/fixture.go +++ b/test/e2e/fixture/fixture.go @@ -1,7 +1,9 @@ package fixture import ( + "bufio" "context" + goerrors "errors" "fmt" "io/ioutil" "os" @@ -35,10 +37,11 @@ import ( ) const ( - defaultApiServer = "localhost:8080" - adminPassword = "password" - testingLabel = "e2e.argoproj.io" - ArgoCDNamespace = "argocd-e2e" + defaultApiServer = "localhost:8080" + defaultAdminPassword = "password" + defaultAdminUsername = "admin" + testingLabel = "e2e.argoproj.io" + ArgoCDNamespace = "argocd-e2e" // ensure all repos are in one directory tree, so we can easily clean them up TmpDir = "/tmp/argo-e2e" @@ -49,6 +52,11 @@ const ( GuestbookPath = "guestbook" ) +const ( + EnvAdminUsername = "ARGOCD_E2E_ADMIN_USERNAME" + EnvAdminPassword = "ARGOCD_E2E_ADMIN_PASSWORD" +) + var ( id string deploymentNamespace string @@ -57,9 +65,12 @@ var ( DynamicClientset dynamic.Interface AppClientset appclientset.Interface ArgoCDClientset argocdclient.Client + adminUsername string + adminPassword string apiServerAddress string token string plainText bool + testsRun map[string]bool ) type RepoURLType string @@ -79,6 +90,12 @@ const ( GpgGoodKeyID = "D56C4FCA57A46444" ) +// TestNamespace returns the namespace where Argo CD E2E test instance will be +// running in. +func TestNamespace() string { + return GetEnvWithDefault("ARGOCD_E2E_NAMESPACE", ArgoCDNamespace) +} + // getKubeConfig creates new kubernetes client config using specified config path and config overrides variables func getKubeConfig(configPath string, overrides clientcmd.ConfigOverrides) *rest.Config { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() @@ -90,6 +107,26 @@ func getKubeConfig(configPath string, overrides clientcmd.ConfigOverrides) *rest return restConfig } +func GetEnvWithDefault(envName, defaultValue string) string { + r := os.Getenv(envName) + if r == "" { + return defaultValue + } + return r +} + +// IsRemote returns true when the tests are being run against a workload that +// is running in a remote cluster. +func IsRemote() bool { + r := os.Getenv("ARGOCD_E2E_REMOTE") + return r == "true" +} + +// IsLocal returns when the tests are being run against a local workload +func IsLocal() bool { + return !IsRemote() +} + // creates e2e tests fixture: ensures that Application CRD is installed, creates temporal namespace, starts repo and api server, // configure currently available cluster. func init() { @@ -100,10 +137,10 @@ func init() { AppClientset = appclientset.NewForConfigOrDie(config) KubeClientset = kubernetes.NewForConfigOrDie(config) DynamicClientset = dynamic.NewForConfigOrDie(config) - apiServerAddress = os.Getenv(argocdclient.EnvArgoCDServer) - if apiServerAddress == "" { - apiServerAddress = defaultApiServer - } + + apiServerAddress = GetEnvWithDefault(argocdclient.EnvArgoCDServer, defaultApiServer) + adminUsername = GetEnvWithDefault(EnvAdminUsername, defaultAdminUsername) + adminPassword = GetEnvWithDefault(EnvAdminPassword, defaultAdminPassword) tlsTestResult, err := grpcutil.TestTLS(apiServerAddress) CheckError(err) @@ -115,7 +152,7 @@ func init() { CheckError(err) defer io.Close(closer) - sessionResponse, err := client.Create(context.Background(), &sessionpkg.SessionCreateRequest{Username: "admin", Password: adminPassword}) + sessionResponse, err := client.Create(context.Background(), &sessionpkg.SessionCreateRequest{Username: adminUsername, Password: adminPassword}) CheckError(err) ArgoCDClientset, err = argocdclient.NewClient(&argocdclient.ClientOptions{ @@ -130,6 +167,27 @@ func init() { plainText = !tlsTestResult.TLS log.WithFields(log.Fields{"apiServerAddress": apiServerAddress}).Info("initialized") + + // Preload a list of tests that should be skipped + testsRun = make(map[string]bool) + rf := os.Getenv("ARGOCD_E2E_RECORD") + if rf == "" { + return + } + f, err := os.Open(rf) + if err != nil { + if goerrors.Is(err, os.ErrNotExist) { + return + } else { + panic(fmt.Sprintf("Could not read record file %s: %v", rf, err)) + } + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + testsRun[scanner.Text()] = true + } + } func Name() string { @@ -148,33 +206,45 @@ func submoduleParentDirectory() string { return path.Join(TmpDir, submoduleParentDir) } +const ( + EnvRepoURLTypeSSH = "ARGOCD_E2E_REPO_SSH" + EnvRepoURLTypeSSHSubmodule = "ARGOCD_E2E_REPO_SSH_SUBMODULE" + EnvRepoURLTypeSSHSubmoduleParent = "ARGOCD_E2E_REPO_SSH_SUBMODULE_PARENT" + EnvRepoURLTypeHTTPS = "ARGOCD_E2E_REPO_HTTPS" + EnvRepoURLTypeHTTPSClientCert = "ARGOCD_E2E_REPO_HTTPS_CLIENT_CERT" + EnvRepoURLTypeHTTPSSubmodule = "ARGOCD_E2E_REPO_HTTPS_SUBMODULE" + EnvRepoURLTypeHTTPSSubmoduleParent = "ARGOCD_E2E_REPO_HTTPS_SUBMODULE_PARENT" + EnvRepoURLTypeHelm = "ARGOCD_E2E_REPO_HELM" + EnvRepoURLDefault = "ARGOCD_E2E_REPO_DEFAULT" +) + func RepoURL(urlType RepoURLType) string { switch urlType { // Git server via SSH case RepoURLTypeSSH: - return "ssh://root@localhost:2222/tmp/argo-e2e/testdata.git" + return GetEnvWithDefault(EnvRepoURLTypeSSH, "ssh://root@localhost:2222/tmp/argo-e2e/testdata.git") // Git submodule repo case RepoURLTypeSSHSubmodule: - return "ssh://root@localhost:2222/tmp/argo-e2e/submodule.git" - // Git submodule parent repo + return GetEnvWithDefault(EnvRepoURLTypeSSHSubmodule, "ssh://root@localhost:2222/tmp/argo-e2e/submodule.git") + // Git submodule parent repo case RepoURLTypeSSHSubmoduleParent: - return "ssh://root@localhost:2222/tmp/argo-e2e/submoduleParent.git" + return GetEnvWithDefault(EnvRepoURLTypeSSHSubmoduleParent, "ssh://root@localhost:2222/tmp/argo-e2e/submoduleParent.git") // Git server via HTTPS case RepoURLTypeHTTPS: - return "https://localhost:9443/argo-e2e/testdata.git" + return GetEnvWithDefault(EnvRepoURLTypeHTTPS, "https://localhost:9443/argo-e2e/testdata.git") // Git server via HTTPS - Client Cert protected case RepoURLTypeHTTPSClientCert: - return "https://localhost:9444/argo-e2e/testdata.git" + return GetEnvWithDefault(EnvRepoURLTypeHTTPSClientCert, "https://localhost:9444/argo-e2e/testdata.git") case RepoURLTypeHTTPSSubmodule: - return "https://localhost:9443/argo-e2e/submodule.git" + return GetEnvWithDefault(EnvRepoURLTypeHTTPSSubmodule, "https://localhost:9443/argo-e2e/submodule.git") // Git submodule parent repo case RepoURLTypeHTTPSSubmoduleParent: - return "https://localhost:9443/argo-e2e/submoduleParent.git" + return GetEnvWithDefault(EnvRepoURLTypeHTTPSSubmoduleParent, "https://localhost:9443/argo-e2e/submoduleParent.git") // Default - file based Git repository case RepoURLTypeHelm: - return "https://localhost:9444/argo-e2e/testdata.git/helm-repo" + return GetEnvWithDefault(EnvRepoURLTypeHelm, "https://localhost:9444/argo-e2e/testdata.git/helm-repo/local") default: - return fmt.Sprintf("file://%s", repoDirectory()) + return GetEnvWithDefault(EnvRepoURLDefault, fmt.Sprintf("file://%s", repoDirectory())) } } @@ -192,8 +262,8 @@ func CreateSecret(username, password string) string { FailOnErr(Run("", "kubectl", "create", "secret", "generic", secretName, "--from-literal=username="+username, "--from-literal=password="+password, - "-n", ArgoCDNamespace)) - FailOnErr(Run("", "kubectl", "label", "secret", secretName, testingLabel+"=true", "-n", ArgoCDNamespace)) + "-n", TestNamespace())) + FailOnErr(Run("", "kubectl", "label", "secret", secretName, testingLabel+"=true", "-n", TestNamespace())) return secretName } @@ -204,13 +274,13 @@ func updateSettingConfigMap(updater func(cm *corev1.ConfigMap) error) { // Updates a given config map in argocd-e2e namespace func updateGenericConfigMap(name string, updater func(cm *corev1.ConfigMap) error) { - cm, err := KubeClientset.CoreV1().ConfigMaps(ArgoCDNamespace).Get(context.Background(), name, v1.GetOptions{}) + cm, err := KubeClientset.CoreV1().ConfigMaps(TestNamespace()).Get(context.Background(), name, v1.GetOptions{}) errors.CheckError(err) if cm.Data == nil { cm.Data = make(map[string]string) } errors.CheckError(updater(cm)) - _, err = KubeClientset.CoreV1().ConfigMaps(ArgoCDNamespace).Update(context.Background(), cm, v1.UpdateOptions{}) + _, err = KubeClientset.CoreV1().ConfigMaps(TestNamespace()).Update(context.Background(), cm, v1.UpdateOptions{}) errors.CheckError(err) } @@ -288,26 +358,32 @@ func SetRepos(repos ...settings.RepositoryCredentials) { } func SetProjectSpec(project string, spec v1alpha1.AppProjectSpec) { - proj, err := AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Get(context.Background(), project, v1.GetOptions{}) + proj, err := AppClientset.ArgoprojV1alpha1().AppProjects(TestNamespace()).Get(context.Background(), project, v1.GetOptions{}) errors.CheckError(err) proj.Spec = spec - _, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(context.Background(), proj, v1.UpdateOptions{}) + _, err = AppClientset.ArgoprojV1alpha1().AppProjects(TestNamespace()).Update(context.Background(), proj, v1.UpdateOptions{}) errors.CheckError(err) } func EnsureCleanState(t *testing.T) { + // In large scenarios, we can skip tests that already run + SkipIfAlreadyRun(t) + // Register this test after it has been run & was successfull + t.Cleanup(func() { + RecordTestRun(t) + }) start := time.Now() policy := v1.DeletePropagationBackground // delete resources // kubectl delete apps --all - CheckError(AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{})) + CheckError(AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{})) // kubectl delete appprojects --field-selector metadata.name!=default - CheckError(AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).DeleteCollection(context.Background(), + CheckError(AppClientset.ArgoprojV1alpha1().AppProjects(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{FieldSelector: "metadata.name!=default"})) // kubectl delete secrets -l e2e.argoproj.io=true - CheckError(KubeClientset.CoreV1().Secrets(ArgoCDNamespace).DeleteCollection(context.Background(), + CheckError(KubeClientset.CoreV1().Secrets(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{LabelSelector: testingLabel + "=true"})) FailOnErr(Run("", "kubectl", "delete", "ns", "-l", testingLabel+"=true", "--field-selector", "status.phase=Active", "--wait=false")) @@ -342,8 +418,9 @@ func EnsureCleanState(t *testing.T) { SignatureKeys: []v1alpha1.SignatureKey{{KeyID: GpgGoodKeyID}}, }) - // remove tmp dir + // Recreate temp dir CheckError(os.RemoveAll(TmpDir)) + FailOnErr(Run("", "mkdir", "-p", TmpDir)) // random id - unique across test runs postFix := "-" + strings.ToLower(rand.RandString(5)) @@ -351,12 +428,11 @@ func EnsureCleanState(t *testing.T) { name = DnsFriendly(t.Name(), "") deploymentNamespace = DnsFriendly(fmt.Sprintf("argocd-e2e-%s", t.Name()), postFix) - // create tmp dir - FailOnErr(Run("", "mkdir", "-p", TmpDir)) - // create TLS and SSH certificate directories - FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/tls")) - FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/ssh")) + if IsLocal() { + FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/tls")) + FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/ssh")) + } // For signing during the tests FailOnErr(Run("", "mkdir", "-p", TmpDir+"/gpg")) @@ -369,9 +445,11 @@ func EnsureCleanState(t *testing.T) { os.Setenv("GNUPGHOME", prevGnuPGHome) // recreate GPG directories - FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/gpg/source")) - FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/gpg/keys")) - FailOnErr(Run("", "chmod", "0700", TmpDir+"/app/config/gpg/keys")) + if IsLocal() { + FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/gpg/source")) + FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/gpg/keys")) + FailOnErr(Run("", "chmod", "0700", TmpDir+"/app/config/gpg/keys")) + } // set-up tmp repo, must have unique name FailOnErr(Run("", "cp", "-Rf", "testdata", repoDirectory())) @@ -380,6 +458,11 @@ func EnsureCleanState(t *testing.T) { FailOnErr(Run(repoDirectory(), "git", "add", ".")) FailOnErr(Run(repoDirectory(), "git", "commit", "-q", "-m", "initial commit")) + if IsRemote() { + FailOnErr(Run(repoDirectory(), "git", "remote", "add", "origin", os.Getenv("ARGOCD_E2E_GIT_SERVICE"))) + FailOnErr(Run(repoDirectory(), "git", "push", "origin", "master", "-f")) + } + // create namespace FailOnErr(Run("", "kubectl", "create", "ns", DeploymentNamespace())) FailOnErr(Run("", "kubectl", "label", "ns", DeploymentNamespace(), testingLabel+"=true")) @@ -433,6 +516,9 @@ func Patch(path string, jsonPatch string) { CheckError(ioutil.WriteFile(filename, bytes, 0644)) FailOnErr(Run(repoDirectory(), "git", "diff")) FailOnErr(Run(repoDirectory(), "git", "commit", "-am", "patch")) + if IsRemote() { + FailOnErr(Run(repoDirectory(), "git", "push", "-f", "origin", "master")) + } } func Delete(path string) { @@ -443,6 +529,9 @@ func Delete(path string) { FailOnErr(Run(repoDirectory(), "git", "diff")) FailOnErr(Run(repoDirectory(), "git", "commit", "-am", "delete")) + if IsRemote() { + FailOnErr(Run(repoDirectory(), "git", "push", "-f", "origin", "master")) + } } func WriteFile(path, contents string) { @@ -458,6 +547,10 @@ func AddFile(path, contents string) { FailOnErr(Run(repoDirectory(), "git", "diff")) FailOnErr(Run(repoDirectory(), "git", "add", ".")) FailOnErr(Run(repoDirectory(), "git", "commit", "-am", "add file")) + + if IsRemote() { + FailOnErr(Run(repoDirectory(), "git", "push", "-f", "origin", "master")) + } } func AddSignedFile(path, contents string) { @@ -469,6 +562,9 @@ func AddSignedFile(path, contents string) { FailOnErr(Run(repoDirectory(), "git", "add", ".")) FailOnErr(Run(repoDirectory(), "git", "-c", fmt.Sprintf("user.signingkey=%s", GpgGoodKeyID), "commit", "-S", "-am", "add file")) os.Setenv("GNUPGHOME", prevGnuPGHome) + if IsRemote() { + FailOnErr(Run(repoDirectory(), "git", "push", "-f", "origin", "master")) + } } // create the resource by creating using "kubectl apply", with bonus templating @@ -482,7 +578,7 @@ func Declarative(filename string, values interface{}) (string, error) { _, err = tmpFile.WriteString(Tmpl(string(bytes), values)) CheckError(err) - return Run("", "kubectl", "-n", ArgoCDNamespace, "apply", "-f", tmpFile.Name()) + return Run("", "kubectl", "-n", TestNamespace(), "apply", "-f", tmpFile.Name()) } func CreateSubmoduleRepos(repoType string) { @@ -494,6 +590,11 @@ func CreateSubmoduleRepos(repoType string) { FailOnErr(Run(submoduleDirectory(), "git", "add", ".")) FailOnErr(Run(submoduleDirectory(), "git", "commit", "-q", "-m", "initial commit")) + if IsRemote() { + FailOnErr(Run(submoduleDirectory(), "git", "remote", "add", "origin", os.Getenv("ARGOCD_E2E_GIT_SERVICE_SUBMODULE"))) + FailOnErr(Run(submoduleDirectory(), "git", "push", "origin", "master", "-f")) + } + // set-up submodule parent repo FailOnErr(Run("", "mkdir", submoduleParentDirectory())) FailOnErr(Run(submoduleParentDirectory(), "chmod", "777", ".")) @@ -507,4 +608,90 @@ func CreateSubmoduleRepos(repoType string) { } FailOnErr(Run(submoduleParentDirectory(), "git", "add", "--all")) FailOnErr(Run(submoduleParentDirectory(), "git", "commit", "-q", "-m", "commit with submodule")) + + if IsRemote() { + FailOnErr(Run(submoduleParentDirectory(), "git", "remote", "add", "origin", os.Getenv("ARGOCD_E2E_GIT_SERVICE_SUBMODULE_PARENT"))) + FailOnErr(Run(submoduleParentDirectory(), "git", "push", "origin", "master", "-f")) + } +} + +// RestartRepoServer performs a restart of the repo server deployment and waits +// until the rollout has completed. +func RestartRepoServer() { + if IsRemote() { + log.Infof("Waiting for repo server to restart") + prefix := os.Getenv("ARGOCD_E2E_NAME_PREFIX") + workload := "argocd-repo-server" + if prefix != "" { + workload = prefix + "-repo-server" + } + FailOnErr(Run("", "kubectl", "rollout", "restart", "deployment", workload)) + FailOnErr(Run("", "kubectl", "rollout", "status", "deployment", workload)) + } +} + +// RestartAPIServer performs a restart of the API server deployemt and waits +// until the rollout has completed. +func RestartAPIServer() { + if IsRemote() { + log.Infof("Waiting for API server to restart") + prefix := os.Getenv("ARGOCD_E2E_NAME_PREFIX") + workload := "argocd-server" + if prefix != "" { + workload = prefix + "-server" + } + FailOnErr(Run("", "kubectl", "rollout", "restart", "deployment", workload)) + FailOnErr(Run("", "kubectl", "rollout", "status", "deployment", workload)) + } +} + +// LocalOrRemotePath selects a path for a given application based on whether +// tests are running local or remote. +func LocalOrRemotePath(base string) string { + if IsRemote() { + return base + "/remote" + } else { + return base + "/local" + } +} + +// SkipOnEnv allows to skip a test when a given environment variable is set. +// Environment variable names follow the ARGOCD_E2E_SKIP_ pattern, +// and must be set to the string value 'true' in order to skip a test. +func SkipOnEnv(t *testing.T, suffixes ...string) { + for _, suffix := range suffixes { + e := os.Getenv("ARGOCD_E2E_SKIP_" + suffix) + if e == "true" { + t.Skip() + } + } +} + +// SkipIfAlreadyRun skips a test if it has been already run by a previous +// test cycle and was recorded. +func SkipIfAlreadyRun(t *testing.T) { + if _, ok := testsRun[t.Name()]; ok { + t.Skip() + } +} + +// RecordTestRun records a test that has been run successfully to a text file, +// so that it can be automatically skipped if requested. +func RecordTestRun(t *testing.T) { + if t.Skipped() || t.Failed() { + return + } + rf := os.Getenv("ARGOCD_E2E_RECORD") + if rf == "" { + return + } + log.Infof("Registering test execution at %s", rf) + f, err := os.OpenFile(rf, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + t.Fatalf("could not open record file %s: %v", rf, err) + } + defer f.Close() + if _, err := f.WriteString(fmt.Sprintf("%s\n", t.Name())); err != nil { + t.Fatalf("could not write to %s: %v", rf, err) + } } diff --git a/test/e2e/fixture/gpgkeys/gpgkeys.go b/test/e2e/fixture/gpgkeys/gpgkeys.go index c724d5dd28612..45cf79f372656 100644 --- a/test/e2e/fixture/gpgkeys/gpgkeys.go +++ b/test/e2e/fixture/gpgkeys/gpgkeys.go @@ -17,15 +17,22 @@ func AddGPGPublicKey() { args := []string{"gpg", "add", "--from", keyPath} errors.FailOnErr(fixture.RunCli(args...)) - keyData, err := ioutil.ReadFile(keyPath) - errors.CheckError(err) - err = ioutil.WriteFile(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir, fixture.GpgGoodKeyID), keyData, 0644) - errors.CheckError(err) + if fixture.IsLocal() { + keyData, err := ioutil.ReadFile(keyPath) + errors.CheckError(err) + err = ioutil.WriteFile(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir, fixture.GpgGoodKeyID), keyData, 0644) + errors.CheckError(err) + } else { + fixture.RestartRepoServer() + } } func DeleteGPGPublicKey() { args := []string{"gpg", "rm", fixture.GpgGoodKeyID} errors.FailOnErr(fixture.RunCli(args...)) - - errors.CheckError(os.Remove(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir, fixture.GpgGoodKeyID))) + if fixture.IsLocal() { + errors.CheckError(os.Remove(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir, fixture.GpgGoodKeyID))) + } else { + fixture.RestartRepoServer() + } } diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 3ce7e4989540b..fafad78cd4365 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -96,6 +96,7 @@ func TestDeclarativeHelmInvalidValuesFile(t *testing.T) { } func TestHelmRepo(t *testing.T) { + SkipOnEnv(t, "HELM") Given(t). CustomCACertAdded(). HelmRepoAdded("custom-repo"). @@ -219,6 +220,7 @@ func TestHelmValuesLiteralFileRemote(t *testing.T) { } func TestHelmCrdHook(t *testing.T) { + SkipOnEnv(t, "HELM") Given(t). Path("helm-crd"). When(). @@ -312,6 +314,7 @@ func TestHelmSetStringEnv(t *testing.T) { // make sure kube-version gets passed down to resources func TestKubeVersion(t *testing.T) { + SkipOnEnv(t, "HELM") Given(t). Path("helm-kube-version"). When(). @@ -328,6 +331,7 @@ func TestKubeVersion(t *testing.T) { } func TestHelmValuesHiddenDirectory(t *testing.T) { + SkipOnEnv(t, "HELM") Given(t). Path(".hidden-helm"). When(). @@ -342,14 +346,17 @@ func TestHelmValuesHiddenDirectory(t *testing.T) { } func TestHelmWithDependencies(t *testing.T) { + SkipOnEnv(t, "HELM") testHelmWithDependencies(t, "helm-with-dependencies", false) } func TestHelm2WithDependencies(t *testing.T) { + SkipOnEnv(t, "HELM", "HELM2") testHelmWithDependencies(t, "helm2-with-dependencies", false) } func TestHelmWithDependenciesLegacyRepo(t *testing.T) { + SkipOnEnv(t, "HELM") testHelmWithDependencies(t, "helm-with-dependencies", true) } @@ -396,6 +403,7 @@ func testHelmWithDependencies(t *testing.T, chartPath string, legacyRepo bool) { } func TestHelm3CRD(t *testing.T) { + SkipOnEnv(t, "HELM") Given(t). Path("helm3-crd"). When(). @@ -407,6 +415,7 @@ func TestHelm3CRD(t *testing.T) { } func TestHelmRepoDiffLocal(t *testing.T) { + SkipOnEnv(t, "HELM") helmTmp, err := ioutil.TempDir("", "argocd-helm-repo-diff-local-test") assert.NoError(t, err) Given(t). @@ -426,7 +435,7 @@ func TestHelmRepoDiffLocal(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { _ = os.Setenv("XDG_CONFIG_HOME", helmTmp) - FailOnErr(Run("", "helm", "repo", "add", "custom-repo", RepoURL(RepoURLTypeHelm), + FailOnErr(Run("", "helm", "repo", "add", "custom-repo", GetEnvWithDefault("ARGOCD_E2E_HELM_SERVICE", RepoURL(RepoURLTypeHelm)), "--username", GitUsername, "--password", GitPassword, "--cert-file", "../fixture/certs/argocd-test-client.crt", diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index c7061e7bf2408..6012854b5d4fc 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -118,7 +118,7 @@ func TestKustomizeSSHRemoteBase(t *testing.T) { // not the best test, as we should have two remote repos both with the same SSH private key SSHInsecureRepoURLAdded(true). RepoURLType(fixture.RepoURLTypeSSH). - Path("ssh-kustomize-base"). + Path(fixture.LocalOrRemotePath("ssh-kustomize-base")). When(). Create(). Sync(). diff --git a/test/e2e/repo_creds_test.go b/test/e2e/repo_creds_test.go index f0e10973e0835..e801e17212bfc 100644 --- a/test/e2e/repo_creds_test.go +++ b/test/e2e/repo_creds_test.go @@ -37,7 +37,7 @@ func TestCannotAddAppFromClientCertRepoWithoutCfg(t *testing.T) { func TestCanAddAppFromPrivateRepoWithRepoCfg(t *testing.T) { Given(t). RepoURLType(fixture.RepoURLTypeHTTPS). - Path("https-kustomize-base"). + Path(fixture.LocalOrRemotePath("https-kustomize-base")). And(func() { // I use CLI, but you could also modify the settings, we get a free test of the CLI here FailOnErr(fixture.RunCli("repo", "add", fixture.RepoURL(fixture.RepoURLTypeHTTPS), "--username", fixture.GitUsername, "--password", fixture.GitPassword, "--insecure-skip-server-verification")) @@ -53,7 +53,7 @@ func TestCanAddAppFromInsecurePrivateRepoWithCredCfg(t *testing.T) { Given(t). CustomCACertAdded(). RepoURLType(fixture.RepoURLTypeHTTPS). - Path("https-kustomize-base"). + Path(fixture.LocalOrRemotePath("https-kustomize-base")). And(func() { secretName := fixture.CreateSecret(fixture.GitUsername, fixture.GitPassword) FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", @@ -79,7 +79,7 @@ func TestCanAddAppFromPrivateRepoWithCredCfg(t *testing.T) { HTTPSCredentialsUserPassAdded(). HTTPSRepoURLAdded(false). RepoURLType(fixture.RepoURLTypeHTTPS). - Path("https-kustomize-base"). + Path(fixture.LocalOrRemotePath("https-kustomize-base")). And(func() { secretName := fixture.CreateSecret(fixture.GitUsername, fixture.GitPassword) FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", @@ -104,7 +104,7 @@ func TestCanAddAppFromClientCertRepoWithCredCfg(t *testing.T) { CustomCACertAdded(). HTTPSRepoURLWithClientCertAdded(). RepoURLType(fixture.RepoURLTypeHTTPSClientCert). - Path("https-kustomize-base"). + Path(fixture.LocalOrRemotePath("https-kustomize-base")). And(func() { secretName := fixture.CreateSecret(fixture.GitUsername, fixture.GitPassword) FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", diff --git a/test/e2e/selective_sync_test.go b/test/e2e/selective_sync_test.go index 2c7476ce1f98e..3f69f87c3a91a 100644 --- a/test/e2e/selective_sync_test.go +++ b/test/e2e/selective_sync_test.go @@ -53,7 +53,9 @@ func TestSelectiveSyncDoesNotRunHooks(t *testing.T) { func TestSelectiveSyncWithoutNamespace(t *testing.T) { selectedResourceNamespace := getNewNamespace(t) defer func() { - FailOnErr(Run("", "kubectl", "delete", "namespace", selectedResourceNamespace)) + if !t.Skipped() { + FailOnErr(Run("", "kubectl", "delete", "namespace", selectedResourceNamespace)) + } }() Given(t). Prune(true). @@ -81,7 +83,9 @@ func TestSelectiveSyncWithoutNamespace(t *testing.T) { func TestSelectiveSyncWithNamespace(t *testing.T) { selectedResourceNamespace := getNewNamespace(t) defer func() { - FailOnErr(Run("", "kubectl", "delete", "namespace", selectedResourceNamespace)) + if !t.Skipped() { + FailOnErr(Run("", "kubectl", "delete", "namespace", selectedResourceNamespace)) + } }() Given(t). Prune(true). diff --git a/test/e2e/sync_options_test.go b/test/e2e/sync_options_test.go index 569f2795d4a5f..a6a19adc3caf4 100644 --- a/test/e2e/sync_options_test.go +++ b/test/e2e/sync_options_test.go @@ -81,6 +81,7 @@ func TestSyncWithStatusIgnored(t *testing.T) { } func TestSyncWithSkipHook(t *testing.T) { + fixture.SkipOnEnv(t, "OPENSHIFT") Given(t). Path(guestbookPath). When(). diff --git a/test/e2e/testdata/helm-repo/index.yaml b/test/e2e/testdata/helm-repo/index.yaml index f40566fc0d448..3f1aeb3c0036b 100644 --- a/test/e2e/testdata/helm-repo/index.yaml +++ b/test/e2e/testdata/helm-repo/index.yaml @@ -3,11 +3,11 @@ entries: helm: - created: 2019-07-08T23:26:50.723856689Z urls: - - http://localhost:9080/argo-e2e/testdata.git/helm-repo/helm-1.0.0.tgz + - http://argocd-e2e-server:9080/helm-repo/helm-1.0.0.tgz version: 1.0.0 helm2: - created: "2020-03-02T08:16:19.960425-08:00" name: helm2 urls: - - http://localhost:9080/argo-e2e/testdata.git/helm-repo/helm2-1.0.0.tgz - version: 1.0.0 \ No newline at end of file + - http://argocd-e2e-server:9080/helm-repo/helm2-1.0.0.tgz + version: 1.0.0 diff --git a/test/e2e/testdata/helm-repo/local/helm-1.0.0.tgz b/test/e2e/testdata/helm-repo/local/helm-1.0.0.tgz new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/testdata/helm-repo/local/helm2-1.0.0.tgz b/test/e2e/testdata/helm-repo/local/helm2-1.0.0.tgz new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/testdata/helm-repo/local/index.yaml b/test/e2e/testdata/helm-repo/local/index.yaml new file mode 100644 index 0000000000000..b61e5e81238ce --- /dev/null +++ b/test/e2e/testdata/helm-repo/local/index.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +entries: + helm: + - created: 2019-07-08T23:26:50.723856689Z + urls: + - http://127.0.0.1:9080/argo-e2e/testdata.git/helm-repo/helm-1.0.0.tgz + version: 1.0.0 + helm2: + - created: "2020-03-02T08:16:19.960425-08:00" + name: helm2 + urls: + - http://127.0.0.1:9080/argo-e2e/testdata.git/helm-repo/helm2-1.0.0.tgz + version: 1.0.0 diff --git a/test/e2e/testdata/helm-repo/remote/helm-1.0.0.tgz b/test/e2e/testdata/helm-repo/remote/helm-1.0.0.tgz new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/testdata/helm-repo/remote/helm2-1.0.0.tgz b/test/e2e/testdata/helm-repo/remote/helm2-1.0.0.tgz new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/testdata/helm-repo/remote/index.yaml b/test/e2e/testdata/helm-repo/remote/index.yaml new file mode 100644 index 0000000000000..3f1aeb3c0036b --- /dev/null +++ b/test/e2e/testdata/helm-repo/remote/index.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +entries: + helm: + - created: 2019-07-08T23:26:50.723856689Z + urls: + - http://argocd-e2e-server:9080/helm-repo/helm-1.0.0.tgz + version: 1.0.0 + helm2: + - created: "2020-03-02T08:16:19.960425-08:00" + name: helm2 + urls: + - http://argocd-e2e-server:9080/helm-repo/helm2-1.0.0.tgz + version: 1.0.0 diff --git a/test/e2e/testdata/https-kustomize-base/kustomization.yaml b/test/e2e/testdata/https-kustomize-base/local/kustomization.yaml similarity index 100% rename from test/e2e/testdata/https-kustomize-base/kustomization.yaml rename to test/e2e/testdata/https-kustomize-base/local/kustomization.yaml diff --git a/test/e2e/testdata/https-kustomize-base/remote/kustomization.yaml b/test/e2e/testdata/https-kustomize-base/remote/kustomization.yaml new file mode 100644 index 0000000000000..96e9885a84cba --- /dev/null +++ b/test/e2e/testdata/https-kustomize-base/remote/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +bases: + - https://argocd-e2e-server:9443/argo-e2e/testdata.git//guestbook + +namePrefix: child- diff --git a/test/e2e/testdata/ssh-kustomize-base/kustomization.yaml b/test/e2e/testdata/ssh-kustomize-base/local/kustomization.yaml similarity index 100% rename from test/e2e/testdata/ssh-kustomize-base/kustomization.yaml rename to test/e2e/testdata/ssh-kustomize-base/local/kustomization.yaml diff --git a/test/e2e/testdata/ssh-kustomize-base/remote/kustomization.yaml b/test/e2e/testdata/ssh-kustomize-base/remote/kustomization.yaml new file mode 100644 index 0000000000000..84537aac69f81 --- /dev/null +++ b/test/e2e/testdata/ssh-kustomize-base/remote/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +bases: +- ssh://root@argocd-e2e-server:2222/tmp/argo-e2e/testdata.git//config-map diff --git a/test/fixture/certs/argocd-e2e-server.crt b/test/fixture/certs/argocd-e2e-server.crt new file mode 100644 index 0000000000000..aa59782194f9d --- /dev/null +++ b/test/fixture/certs/argocd-e2e-server.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC3zCCAcegAwIBAgIUNhg6nbyoR0ed2gkFTg8QA/U4dY0wDQYJKoZIhvcNAQEL +BQAwGTEXMBUGA1UEAwwOQXJnb0NEIFRlc3QgQ0EwIBcNMjEwMzE4MTUyMTIxWhgP +MjEyMTAyMjIxNTIxMjFaMBYxFDASBgNVBAMMC0FyZ28gQ0QgRTJFMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtNx2+MQAsO2NyvdA2Ys8LVKslUP+B4El +CMujl8BtGvgkvceOhZ6VHNHYr4SYf9Y3Fp9YZFpeRDqKQ2j1t45AsNfJM/qw4pC3 +6pl4Mm5XVvdisaMs0i98DuFZJSA+hjOpZHRotEzf5nzc8TSoY1x5twmybZEa6zml +H3eR49nGwEJsO4c259UGDN7CsikjhdckgYL7ZGpHFRH1RYkgEIryjp0lkzLvdmP3 +CNJ7B807TGVS/ijwUNnk3ZjGlMTQEp5eaw7EeX6g/RiKlbMy2pFNjfA9j78o01Mk +VbcolP3rfWAbm/v6qe3aiiPPJ2OLVf7IF6TTPjQ/A5m6UBXjmYl9HwIDAQABoyAw +HjAcBgNVHREEFTATghFhcmdvY2QtZTJlLXNlcnZlcjANBgkqhkiG9w0BAQsFAAOC +AQEAwrW+xp8TfmjABkASOQQJY5ro+xH6pOSgaHtaWHPs7uu23/EpK0b/CWT3BN4E +ouGhgTeR0WO7tSACUCu7eY6ObNT8O4D6OVI10Yv1SZN4ICnPgAaQQwgCxmPDa+3c +WrYo2NgpTbC8JD+xATiy6vIOMlKMkQD6Jlch8+ekLcZPsbvZslQLnofcIhvaTQBI +uUP8bs2M4aRjADxWCSvTNeqUELN9zZ1YX5hSNSKMSQn3QKy2AvBkLi+4SXl39yN3 +KA38d/n7yIE/5nfSBxuqdwB54iFwyqqJJrvYpPQpChzRfBqGYTSGCf3kpcj59hJo +8hzLMhBCPRrCkqEUZo9xf0Zouw== +-----END CERTIFICATE----- diff --git a/test/fixture/certs/argocd-e2e-server.csr b/test/fixture/certs/argocd-e2e-server.csr new file mode 100644 index 0000000000000..a4c55da34ab32 --- /dev/null +++ b/test/fixture/certs/argocd-e2e-server.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICijCCAXICAQAwFjEUMBIGA1UEAwwLQXJnbyBDRCBFMkUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC03Hb4xACw7Y3K90DZizwtUqyVQ/4HgSUIy6OX +wG0a+CS9x46FnpUc0divhJh/1jcWn1hkWl5EOopDaPW3jkCw18kz+rDikLfqmXgy +bldW92KxoyzSL3wO4VklID6GM6lkdGi0TN/mfNzxNKhjXHm3CbJtkRrrOaUfd5Hj +2cbAQmw7hzbn1QYM3sKyKSOF1ySBgvtkakcVEfVFiSAQivKOnSWTMu92Y/cI0nsH +zTtMZVL+KPBQ2eTdmMaUxNASnl5rDsR5fqD9GIqVszLakU2N8D2PvyjTUyRVtyiU +/et9YBub+/qp7dqKI88nY4tV/sgXpNM+ND8DmbpQFeOZiX0fAgMBAAGgLzAtBgkq +hkiG9w0BCQ4xIDAeMBwGA1UdEQQVMBOCEWFyZ29jZC1lMmUtc2VydmVyMA0GCSqG +SIb3DQEBCwUAA4IBAQATFrqrM91BKAiOTShf+uMlx8VT11pBtZ78Rg3Enp2xEvUX +jEwo3lRD6Fb0T0xi+4O0ScVZFP4yp4ZZ2uqIIzJLSq8F2cko6AhplMPDbxDOVZyq +Z5t6VJnfXOQYwtgyowTWCijA5rOaqhmluCbguTQAJRR6Qk6Oc4Ti4BfbliIbzDBO +QVAA6HoQPn4E314vBolkxicPesnrYFA7F/U8iCD57cyu5FmEQZuTLmplBzc/UmeV +AKcS0+w3Hk+uTgZmqdTWPOzbKXYgF4lU0QNanx4j/H3G4QKujlUspyhid52sFYcq +1b4+U2ashtNzUze9Og0Q6CJv3Nu2fqOIG7OBOJcd +-----END CERTIFICATE REQUEST----- diff --git a/test/fixture/certs/argocd-e2e-server.key b/test/fixture/certs/argocd-e2e-server.key new file mode 100644 index 0000000000000..787e19e51d451 --- /dev/null +++ b/test/fixture/certs/argocd-e2e-server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtNx2+MQAsO2NyvdA2Ys8LVKslUP+B4ElCMujl8BtGvgkvceO +hZ6VHNHYr4SYf9Y3Fp9YZFpeRDqKQ2j1t45AsNfJM/qw4pC36pl4Mm5XVvdisaMs +0i98DuFZJSA+hjOpZHRotEzf5nzc8TSoY1x5twmybZEa6zmlH3eR49nGwEJsO4c2 +59UGDN7CsikjhdckgYL7ZGpHFRH1RYkgEIryjp0lkzLvdmP3CNJ7B807TGVS/ijw +UNnk3ZjGlMTQEp5eaw7EeX6g/RiKlbMy2pFNjfA9j78o01MkVbcolP3rfWAbm/v6 +qe3aiiPPJ2OLVf7IF6TTPjQ/A5m6UBXjmYl9HwIDAQABAoIBADOuN9TX9TaZewX/ +ZymCrtxonmY8uo8caR+9SO7pDoRBWgZcyq4F5agJkrh81DLbXHx7Zf+vDvbCH5Yp +nOUpofSmhJj0zqy/G77OwdjvTMfOAwatkPYymWmLHTUNpLAmfQX2eaDjwqsTDPNj +z6Ys257hB3pll2KV40g9m5OXPELdfHNEaht3dEKK3jZLDiDWP8vn5/k32gqmsfTX +n1Q8zSh+EaRYtUlczwZ6oPLgHEDIbusZUyN9dscK7ZtwuWIXikBpNU6XL2c4bVpe +TWxm+HqJYqZ7XVZrEEInJH4Q4A+Nte425IaxIiK62Q15jxdl4eTnqeIqIWACXsyX +w5bS1xkCgYEA70Xuy1aSW8I5SEWbJcyXUhJifPVi+H4UyD7Mh1SptINuL+rcMxrC +jEvPG3CgDmEBclEonHGiNjzAv8UJgyqC2B1NooFTQmDbNFkV+xNwOYXrX5vADVq9 +gv96JUY8HHM+Wzy/ZTz/DA5fs3b7RFkH3TGRoKbL3XFE3U4h5fEKQKMCgYEAwYEu +wkPMRa3/u0lWOZmjjOfkhljUp9zDx843co4jTfcgVKw64ZElznwxvI3TR3Rh7Uk4 +Si6v7zLtNNVp87m2VXYML3bs8DR8YCMARjeu9jy7l7tK8BXJAcNHfTN67XMfEMRU +WItofIv3IMGcEMlqOIw9umt/ymfcqitFJdObTVUCgYADrh+uIPa/Uo1u/yMTwcHS +EVZSdbO//rBzJAQhULwLeroHIqWgY5j6b3AQC/tqPTO4fmcRgO3tx6FvUaiDy/OX +P1wiU7rkGnlEpNohsnrXZ6fnII4jpnGjwiouyJXEGkGrRZPsCrQhi1cNe5cBxhzI +2ipTPj2EemmEvd7nmn194QKBgQC8pooEoP6ErEdWuv/SNiRKGkYkaHQil0tT/4r1 +JvfqadTeV+8rvEuEjHYc4pQp5KAYKzsyr2OcqySxTqL2F4dYuDbXC3WiFTlwrI72 +5hCwRbOKEPHVMRf/nPeUa10cJBEUUqP8kyUxz28GQ1s4znS9XpRLq8Qxe6KZUIgg +gbW15QKBgD0IOwFWwps4DV12/EHU9NDvo2Lz/PHZJT/e1S+frRh1e9hd+YLU05xI +EddubTzQVl6RPRhJm5XrmxTsI3vXQOBqJjivU/C75WiuIJQ8kHvNw5vEFrzNErkn +evsgrb67FwGaBchEdQhqQg0a4AvQ9BkU7smAHfdyUzjAc4+LRs6p +-----END RSA PRIVATE KEY----- diff --git a/test/fixture/testrepos/nginx.conf b/test/fixture/testrepos/nginx.conf index 47388087ac450..f273b83e0d997 100644 --- a/test/fixture/testrepos/nginx.conf +++ b/test/fixture/testrepos/nginx.conf @@ -4,6 +4,40 @@ worker_processes 1; events { worker_connections 1024; } http { + + server { + listen 0.0.0.0:9081; + + root /tmp/argo-e2e; + + location ~ ^/helm-repo/(.*) { + # Set chunks to unlimited, as the body's can be huge + client_max_body_size 0; + alias /app/config/testdata/helm-repo/$1; + } + + location ~ /argo-e2e/testdata.git/helm-repo/(.*) { + # Set chunks to unlimited, as the body's can be huge + client_max_body_size 0; + + alias /tmp/argo-e2e/testdata.git/helm-repo/$1; + } + + location ~ /argo-e2e(/.*) { + # Set chunks to unlimited, as the body's can be huge + client_max_body_size 0; + + fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; + include /etc/nginx/fastcgi_params; + fastcgi_param GIT_HTTP_EXPORT_ALL ""; + fastcgi_param GIT_PROJECT_ROOT /tmp/argo-e2e; + fastcgi_param PATH_INFO $1; + + # Hard-coded credentials for Git in this case + fastcgi_param REMOTE_USER "admin"; + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } + } server { listen 9080; @@ -24,6 +58,19 @@ http { root /tmp/; } + location ~ /helm-repo/(.*) { + # Set chunks to unlimited, as the body's can be huge + client_max_body_size 0; + alias /app/config/testdata/helm-repo/$1; + } + + location ~ /argo-e2e/testdata.git/helm-repo/(.*) { + # Set chunks to unlimited, as the body's can be huge + client_max_body_size 0; + + alias /tmp/argo-e2e/testdata.git/helm-repo/$1; + } + location ~ /argo-e2e(/.*) { # Set chunks to unlimited, as the body's can be huge client_max_body_size 0; @@ -54,11 +101,17 @@ http { ssl_verify_client on; ssl_protocols TLSv1.2 TLSv1.1 TLSv1; - location ~ /argo-e2e/testdata.git/helm-repo(/.*) { + # /helm-repo is used by cluster-e2e tests only + location ~ ^/helm-repo/(.*) { + client_max_body_size 0; + alias /app/config/testdata/helm-repo/$1; + } + + location ~ /argo-e2e/testdata.git/helm-repo/(.*) { # Set chunks to unlimited, as the body's can be huge client_max_body_size 0; - root /tmp/; + alias /tmp/argo-e2e/testdata.git/helm-repo/$1; } location ~ /argo-e2e(/.*) { @@ -75,5 +128,6 @@ http { fastcgi_param REMOTE_USER $remote_user; fastcgi_pass unix:/var/run/fcgiwrap.socket; } + } } diff --git a/test/fixture/testrepos/ssh_known_hosts b/test/fixture/testrepos/ssh_known_hosts index 2c5aaf85cdef2..5c2d9b4ec66d5 100644 --- a/test/fixture/testrepos/ssh_known_hosts +++ b/test/fixture/testrepos/ssh_known_hosts @@ -4,4 +4,9 @@ [localhost]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCk3mMbdKoeHKJvXHoPAHa0D8ZZnuGbNN0iM9Lo0o52V7vErpBmKb0qkmGajVqR7XvflAHhkmSfIDXLz4/Pxp4E= # localhost:2222 SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2 [localhost]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAYynaOD/wo8xg3YN4BbdrEozPZ5TvQ3R/qxLzy9gMr8 +# For in-cluster tests +[argocd-e2e-server]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDcBar/5tNRUw2MIeY2wzLllG4Opt9h4i/8dRnyxCdDQb30ioH0ervPFRoR1ZJVF/jVI4waHZJ74m/70OxwhSBVA/oPFtRl6C+at2BTgj5uLb9TqVoLSP/VT5M31ohWwBuIsYoQrbwpxwXpgLAQ65M+ghegW3SALnyxEuCWEd/mqCOmwcXWPKNDM32AB3OU/qhW3ID66aIo26LJ8OQ7yZSv/2xWWGiVbRShNjDLqGfkbk5+R+vpVdaIhJn22nX5d1i/modeBepJN8eo4gO7p0vkmCTFxq6aUrRtPPs2h7z61GsYG7miWn4hjjeGVrT1Xv0BTrnuWHxcJU1bSmKCx4rV +[argocd-e2e-server]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCk3mMbdKoeHKJvXHoPAHa0D8ZZnuGbNN0iM9Lo0o52V7vErpBmKb0qkmGajVqR7XvflAHhkmSfIDXLz4/Pxp4E= +[argocd-e2e-server]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAYynaOD/wo8xg3YN4BbdrEozPZ5TvQ3R/qxLzy9gMr8 + diff --git a/test/remote/Dockerfile b/test/remote/Dockerfile new file mode 100644 index 0000000000000..a7b16e2225408 --- /dev/null +++ b/test/remote/Dockerfile @@ -0,0 +1,86 @@ +FROM golang:1.16.2 AS go + +RUN go get github.com/mattn/goreman && \ + go get github.com/kisielk/godepgraph + +FROM ubuntu:20.10 + +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y \ + ca-certificates \ + curl \ + openssh-server \ + nginx \ + fcgiwrap \ + git \ + git-lfs \ + gpg \ + make \ + wget \ + gcc \ + sudo \ + zip && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN mkdir -p /etc/certs + +# These are required for running end-to-end tests +COPY ./test/fixture/testrepos/id_rsa.pub /root/.ssh/authorized_keys +COPY ./test/fixture/testrepos/nginx.conf /etc/nginx/nginx.conf +COPY ./test/fixture/testrepos/.htpasswd /etc/nginx/.htpasswd +COPY ./test/fixture/testrepos/sudoers.conf /etc/sudoers +COPY ./test/fixture/testrepos/ssh_host_*_key* /etc/ssh/ +COPY ./test/fixture/certs/argocd-e2e-server.crt /etc/certs/argocd-test-server.crt +COPY ./test/fixture/certs/argocd-e2e-server.key /etc/certs/argocd-test-server.key +COPY ./test/fixture/certs/argocd-test-ca.crt /etc/certs/argocd-test-ca.crt + +# Entrypoint is required for container's user management +COPY ./test/remote/uid_entrypoint.sh /usr/local/bin +COPY ./test/remote/Procfile /Procfile + +# We need goreman +COPY --from=go /go/bin/goreman /usr/local/bin/goreman + +COPY ./test/e2e/testdata/ /app/config/testdata/ + +# Prepare user configuration & build environments +RUN useradd -l -u 1000 -d /home/user -s /bin/bash user && \ + echo "user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/user && \ + mkdir -p /home/user && \ + HOME=/home/user git config --global user.name "ArgoCD Test User" && \ + HOME=/home/user git config --global user.email "noreply@example.com" && \ + mkdir -p /var/run/sshd && \ + mkdir -p /root/.ssh && \ + chown -R user /home/user && \ + chgrp -R user /home/user && \ + chown root /etc/ssh/ssh_host_*_key* && \ + chmod 0600 /etc/ssh/ssh_host_*_key && \ + mkdir -p /tmp/argo-e2e/testdata.git && \ + cd /tmp/argo-e2e/testdata.git && \ + HOME=/home/user git init --bare && \ + cd /app/config/testdata && \ + HOME=/home/user git init && \ + HOME=/home/user git add . && \ + HOME=/home/user git commit -a -m "Initial commit" && \ + HOME=/home/user git remote add origin file:///tmp/argo-e2e/testdata.git && \ + HOME=/home/user git push origin master && \ + mkdir -p /tmp/argo-e2e/submodule.git && \ + cd /tmp/argo-e2e/submodule.git && \ + HOME=/home/user git init --bare && \ + mkdir -p /tmp/argo-e2e/submoduleParent.git && \ + cd /tmp/argo-e2e/submoduleParent.git && \ + HOME=/home/user git init --bare && \ + chown -R user /tmp/argo-e2e/testdata.git && \ + chown -R user /tmp/argo-e2e/submodule.git && \ + chown -R user /tmp/argo-e2e/submoduleParent.git && \ + ln -s /usr/libexec/git-core /usr/lib/git-core + +RUN echo "[http]" >> /tmp/argo-e2e/testdata.git/config && \ + echo " receivepack = true" >> /tmp/argo-e2e/testdata.git/config +RUN echo "[http]" >> /tmp/argo-e2e/submodule.git/config && \ + echo " receivepack = true" >> /tmp/argo-e2e/submodule.git/config +RUN echo "[http]" >> /tmp/argo-e2e/submoduleParent.git/config && \ + echo " receivepack = true" >> /tmp/argo-e2e/submoduleParent.git/config + +ENTRYPOINT ["/usr/local/bin/uid_entrypoint.sh"] diff --git a/test/remote/Makefile b/test/remote/Makefile new file mode 100644 index 0000000000000..2ee8b6c97142c --- /dev/null +++ b/test/remote/Makefile @@ -0,0 +1,25 @@ +PWD=$(shell pwd) +TEST_ROOT=$(shell realpath $(PWD)/../..) + +IMAGE_NAMESPACE?= +IMAGE_NAME=argocd-e2e-cluster +IMAGE_TAG=latest +ifneq (${IMAGE_NAMESPACE},) +IMAGE_PREFIX=$(IMAGE_NAMESPACE)/ +else +IMAGE_PREFIX= +endif + +.PHONY: image +image: + docker build -t $(IMAGE_PREFIX)$(IMAGE_NAME):$(IMAGE_TAG) -f Dockerfile $(TEST_ROOT) + +.PHONY: image-push +image-push: image + docker push $(IMAGE_PREFIX)$(IMAGE_NAME):$(IMAGE_TAG) + +.PHONY: manifests +manifests: + @cd manifests && \ + kustomize edit set image quay.io/jfischer-redhat/argocd-e2e-cluster=$(IMAGE_PREFIX)$(IMAGE_NAME):$(IMAGE_TAG) && \ + kustomize build . diff --git a/test/remote/Procfile b/test/remote/Procfile new file mode 100644 index 0000000000000..9a277736585c9 --- /dev/null +++ b/test/remote/Procfile @@ -0,0 +1,3 @@ +sshd: sudo sh -c "/usr/sbin/sshd -p 2222 -D -e" +fcgiwrap: sudo sh -c "(rm -f /var/run/fcgiwrap.socket && fcgiwrap -s unix:/var/run/fcgiwrap.socket & sleep 1 && chmod 777 /var/run/fcgiwrap.socket && wait)" +nginx: sudo sh -c "nginx -g 'daemon off;' -c /etc/nginx/nginx.conf" diff --git a/test/remote/README.md b/test/remote/README.md new file mode 100644 index 0000000000000..626797b591f1f --- /dev/null +++ b/test/remote/README.md @@ -0,0 +1,240 @@ +# End-to-end tests against a real cluster + +Using the tools in this directory, you can run the End-to-End testsuite against +a real Argo CD workload, that is deployed to a K8s cluster, instead of running +it against a locally running Argo CD. + +Since e2e tests are destructive, do **not** run it against an installation that +you depend on. + +## Preparations + +### Install the Argo CD you want to test + +It is recommended to install in the `argocd-e2e` namespace: + +```shell +kubectl create ns argocd-e2e +kubectl -n argocd-e2e apply -f +``` + +### Give the Argo CD the appropriate RBAC permissions + +```shell +# If you installed to a different namespace, set accordingly +export NAMESPACE=argocd-e2e +# If you installed Argo CD via Operator, set accordingly +# export ARGOCD_E2E_NAME_PREFIX=argocd-cluster +./test/remote/generate-permissions.sh | kubectl apply -f - +``` + +### Build the repository container image + +You will need to build & publish a container image that will hold the required +testing repositories. + +This container image will be named `argocd-e2e-cluster`, so you will need to +setup a corresponding repository for it in your registry as well. + +To build it, run the following. Note that kustomize is required: + +```shell +cd test/remote +export IMAGE_NAMESPACE=quay.io/youruser +# builds & tags the image +make image +# pushes the image to your repository +make image-push +# build the manifests & store them at temp location +make manifests > /tmp/e2e-repositories.yaml +``` + +If you do not have kustomize installed, you need to manually edit the manifests +at `test/remote/manifests/e2e_repositories.yaml` to point to the correct image. + +### Deploy the test container and additional permissions + +**Note:** The test container requires to be run in privileged mode for now, due +to some processes running as root (this may change some day...). + +On OpenShift, you will likely need to allow privileged operations: + +```shell +oc -n argocd-e2e adm policy add-scc-to-user privileged -z default +``` + +Then, apply the manifests for the E2E repositories workload: + +```shell +kubectl -n argocd-e2e apply -f /tmp/e2e-repositories.yaml +``` + +Verify that the deployment was succesful: + +```shell +kubectl -n argocd-e2e rollout status deployment argocd-e2e-cluster +``` + +## Start the tests + +### Port-forward the repository + +In another shell, port forward the repository to your local machine: + +```shell +kubectl -n argocd-e2e port-forward service/argocd-e2e-server 9081:9081 +``` + +### On local cluster (e.g. K3s, microk8s, minishift) + +Set the server endpoint of the Argo CD API. If you are running on the same host +as the cluster, or the cluster IPs are routed to your host, you can use the +following: + +```shell +export ARGOCD_SERVER=$(kubectl get svc argocd-server -o jsonpath='{.spec.clusterIP}') +``` + +Set the admin password to use: + +```shell +export ARGOCD_E2E_ADMIN_PASSWORD=$(kubectl get secrets argocd-initial-admin-secret -o jsonpath='{.data.password}'|base64 -d) +``` + +Run the tests + +```shell +./test/remote/run-e2e-remote.sh make test-local +``` + +### On remote non-OpenShift cluster + +In another shell, do a port-forward to the API server's service: + +```shell +kubectl -n argocd-e2e port-forward svc/argocd-server 443:4443 +``` + +Set Argo CD Server endport: + +```shell +export ARGOCD_SERVER=127.0.0.1:4443 +``` + +Set the admin password to use: + +```shell +export ARGOCD_E2E_ADMIN_PASSWORD=$(kubectl get secrets argocd-initial-admin-secret -o jsonpath='{.data.password}'|base64 -d) +``` + +Run the tests + +```shell +./test/remote/run-e2e-remote.sh make test-local +``` + +### On remote OpenShift cluster + +You should first scale down Argo CD Operator, since it will revert changes made +during the tests instantly: + +```shell +oc -n openshift-operators scale deployment --replicas=0 argocd-operator +``` + +Set the endpoint by using the route created by the operator: + +```shell +export ARGOCD_SERVER=$(oc -n argocd-e2e get routes argocd-test-server -o jsonpath='{.spec.host}') +``` + +Set the admin password, created by the operator: + +```shell +export ARGOCD_E2E_ADMIN_PASSWORD=$(oc -n argocd-e2e get secrets argocd-test-cluster -o jsonpath='{.data.admin\.password}' | base64 -d) +``` + +Set the name of the Argo CD instance as installed by the operator (below example assumes operand name of `argocd-test`): + +```shell +export ARGOCD_E2E_NAME_PREFIX=argocd-test +``` + +Run the tests with currently known failing tests disabled: + +```shell +./test/remote/run-e2e-remote.sh make test-local ARGOCD_E2E_SKIP_OPENSHIFT=true +``` + +## Running single or multiple tests isolated + +This should be run in the same shell where you set `ARGOCD_SERVER` and `ARGOCD_E2E_ADMIN_PASSWORD` variables + +1. Run the `go test` command through the wrapper, e.g. to run a test named `MyTestName`: + + ``` + $ ./test/remote/run-e2e-remote.sh go test -v github.com/argoproj/argo-cd/test/e2e -run ^MyTestName$^ + ``` + + +## Further configuration + +Some environment variables can control the behavior of the tests: + +* `ARGOCD_SERVER` - the remote endpoint of the Argo CD API server to use for tests +* `ARGOCD_E2E_ADMIN_PASSWORD` - the admin user's password to use +* `ARGOCD_E2E_TEST_TIMEOUT` - timeout for the complete test suite, specified as duration (e.g. `2h` or `1h30m`) +* `ARGOCD_E2E_DEFAULT_TIMEOUT` - timeout in seconds for each context operation (e.g. sync). Default `30`. +* `ARGOCD_E2E_NAMESPACE` - the namespace where Argo CD is running in for the tests +* `ARGOCD_E2E_NAME_PREFIX` - if your Argo CD installation has a name prefix (e.g. installed by the Operator), specify it here + +Furthermore, you can skip various classes of tests by setting the following to true: + +``` +# If you disabled GPG feature, set to true to skip related tests +export ARGOCD_E2E_SKIP_GPG=${ARGOCD_E2E_SKIP_GPG:-false} +# Some tests do not work OOTB with OpenShift +export ARGOCD_E2E_SKIP_OPENSHIFT=${ARGOCD_E2E_SKIP_OPENSHIFT:-false} +# Skip all Helm tests +export ARGOCD_E2E_SKIP_HELM=${ARGOCD_E2E_SKIP_HELM:-false} +# Skip only Helm v2 related tests +export ARGOCD_E2E_SKIP_HELM2=${ARGOCD_E2E_SKIP_HELM2:-false} +# Skip Ksonnet tests +export ARGOCD_E2E_SKIP_KSONNET=${ARGOCD_E2E_SKIP_KSONNET:-false} + +``` + +## Recording tests that ran successfully and restart at point of fail + +Sometimes, due to a hiccup or timeout on the remote cluster, a test may fail to run without specific reason. This can be time consuming when tests +are being run again, since every test will be executed again. You can record the tests that were run & successful and pick up where it failed. +For this purpose, set the `ARGOCD_E2E_RECORD` variable to point to a file where tests will be recorded: + + ``` + $ ./test/remote/run-e2e-remote.sh make test-e2e-local ARGOCD_E2E_RECORD=/tmp/mytests + ``` + +If the tests fail, just re-run above command. All tests that have been previously run will be skipped, and testing will continue with the next test. + +## Tear down + +1. Remove argocd-e2e namespace + + ``` + $ kubectl delete ns argocd-e2e + ``` + + +## Troubleshooting + +* On message: + + ``` + time="2021-03-23T09:52:53Z" level=fatal msg="`git push origin master -f` failed exit status 128: fatal: unable to access 'http://127.0.0.1:9081/argo-e2e/testdata.git/': Empty reply from server" + ``` + + Your port-forward is probably not setup correctly or broke (e.g. due to pod restart) + +* Make sure `argocd-e2e-cluster` pod is running. If you get a CrashLoopBackoff, ensure that you enabled elevated privs as shown above + +* Sometimes, you may run into a timeout especially if the cluster is very busy. In this case, you have to restart the tests. See test recording above. diff --git a/test/remote/argocd-remote-permissions.yaml b/test/remote/argocd-remote-permissions.yaml new file mode 100644 index 0000000000000..5fcc8747167ad --- /dev/null +++ b/test/remote/argocd-remote-permissions.yaml @@ -0,0 +1,39 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: application-controller + name: ##CRNAME## +rules: +- apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +- nonResourceURLs: + - '*' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: application-controller + name: ##CRBNAME## +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ##CRNAME## +subjects: +- kind: ServiceAccount + name: ##CONTROLLERSANAME## + namespace: ##NAMESPACE## +- kind: ServiceAccount + name: ##SERVERSANAME## + namespace: ##NAMESPACE## diff --git a/test/remote/generate-permissions.sh b/test/remote/generate-permissions.sh new file mode 100755 index 0000000000000..f76b78f98eb0c --- /dev/null +++ b/test/remote/generate-permissions.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +BASEPATH=$(dirname $0) +PERMFILE=${BASEPATH}/argocd-remote-permissions.yaml +if ! test -f ${PERMFILE}; then + echo "ERROR: $PERMFILE does not exist." >&2 + exit 1 +fi + +NAMESPACE=${NAMESPACE:-argocd-e2e} + +if test "${ARGOCD_E2E_NAME_PREFIX}" != ""; then + CRNAME="${ARGOCD_E2E_NAME_PREFIX}-argocd-application-controller" + CRBNAME="${ARGOCD_E2E_NAME_PREFIX}-argocd-application-controller" + CONTROLLERSANAME="${ARGOCD_E2E_NAME_PREFIX}-argocd-application-controller" + SERVERSANAME="${ARGOCD_E2E_NAME_PREFIX}-argocd-server" +else + CRNAME="argocd-application-controller" + CRBNAME="argocd-application-controller" + CONTROLLERSANAME="argocd-application-controller" + SERVERSANAME="argocd-server" +fi + +sed \ + -e "s/##CRNAME##/${CRNAME}/g" \ + -e "s/##CRBNAME##/${CRBNAME}/g" \ + -e "s/##CONTROLLERSANAME##/${CONTROLLERSANAME}/g" \ + -e "s/##SERVERSANAME##/${SERVERSANAME}/g" \ + -e "s/##NAMESPACE##/${NAMESPACE}/g" \ + $PERMFILE diff --git a/test/remote/manifests/e2e-repositories.yaml b/test/remote/manifests/e2e-repositories.yaml new file mode 100644 index 0000000000000..8662bdfb62478 --- /dev/null +++ b/test/remote/manifests/e2e-repositories.yaml @@ -0,0 +1,63 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argocd-e2e-cluster + labels: + app.kubernetes.io/name: argocd-e2e-server +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-e2e-server + template: + metadata: + labels: + app.kubernetes.io/name: argocd-e2e-server + spec: + containers: + - name: argocd-e2e-server + image: argocd-e2e-cluster:latest + imagePullPolicy: Always + command: + - goreman + - start + ports: + - containerPort: 2222 + - containerPort: 9080 + - containerPort: 9081 + - containerPort: 9443 + - containerPort: 9444 + securityContext: + capabilities: + add: ["SYS_CHROOT"] +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: argocd-e2e-server + name: argocd-e2e-server +spec: + ports: + - name: helm-https + protocol: TCP + port: 9080 + targetPort: 9080 + - name: git-http-noauth + protocol: TCP + port: 9081 + targetPort: 9081 + - name: git-https-auth + protocol: TCP + port: 9443 + targetPort: 9443 + - name: git-https-ccert + protocol: TCP + port: 9444 + targetPort: 9444 + - name: git-ssh + protocol: TCP + port: 2222 + targetPort: 2222 + selector: + app.kubernetes.io/name: argocd-e2e-server diff --git a/test/remote/manifests/kustomization.yaml b/test/remote/manifests/kustomization.yaml new file mode 100644 index 0000000000000..9a2e08e2a3794 --- /dev/null +++ b/test/remote/manifests/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- e2e-repositories.yaml + +images: +- name: argocd-e2e-cluster + newTag: latest diff --git a/test/remote/run-e2e-remote.sh b/test/remote/run-e2e-remote.sh new file mode 100755 index 0000000000000..62812f50f0b42 --- /dev/null +++ b/test/remote/run-e2e-remote.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +# Should point to the Argo CD API endpoint on the cluster +if test "${ARGOCD_SERVER}" = ""; then + echo "Please set ARGOCD_SERVER to the remote Argo CD API endpoint to test." >&2 + exit 1 +fi + +# ARGOCD_E2E_REMOTE must be set to 'true' in order for remote tests to work +export ARGOCD_E2E_REMOTE=true + +# The timeout for running the test suite (duration) +export ARGOCD_E2E_TEST_TIMEOUT=2h + +# The default timeout for certain operations (such as sync) +export ARGOCD_E2E_DEFAULT_TIMEOUT=30 + +# Set ARGOCD_E2E_NAMESPACE to the namespace the Argo CD we're testing against is +# running in. Defaults to "argocd-e2e" +export ARGOCD_E2E_NAMESPACE=${ARGOCD_E2E_NAMESPACE:-argocd-e2e} + +# Name prefix the operator sets on resources created for Argo CD instance. This +# is usually also the name of the instance itself. +export ARGOCD_E2E_NAME_PREFIX="${ARGOCD_E2E_NAME_PREFIX:-}" + +# This is to skip some (deprecated) tests +export ARGOCD_E2E_K3S=true + +# Configuration for skipping certain classes of tests + +# GnuPG features not yet available with GitOps Operator +export ARGOCD_E2E_SKIP_GPG=${ARGOCD_E2E_SKIP_GPG:-false} +# Some tests do not work OOTB with OpenShift +export ARGOCD_E2E_SKIP_OPENSHIFT=${ARGOCD_E2E_SKIP_OPENSHIFT:-false} +# Skip Helm tests +export ARGOCD_E2E_SKIP_HELM=${ARGOCD_E2E_SKIP_HELM:-false} +# Skip Helm v2 related tests +export ARGOCD_E2E_SKIP_HELM2=${ARGOCD_E2E_SKIP_HELM2:-false} +# Skip Ksonnet tests +export ARGOCD_E2E_SKIP_KSONNET=${ARGOCD_E2E_SKIP_KSONNET:-false} + +## ==================================================== +# no changes below this line required +## ==================================================== + +# Unauthenticated URLs for pushing from CI +# +# Use `kubectl port-forward service/argocd-e2e-server 9081:9081` to set up the +# listener required for this. +export ARGOCD_E2E_GIT_SERVICE="http://127.0.0.1:9081/argo-e2e/testdata.git" +export ARGOCD_E2E_HELM_SERVICE="http://127.0.0.1:9081/helm-repo" +export ARGOCD_E2E_GIT_SERVICE_SUBMODULE="http://127.0.0.1:9081/argo-e2e/submodule.git" +export ARGOCD_E2E_GIT_SERVICE_SUBMODULE_PARENT="http://127.0.0.1:9081/argo-e2e/submoduleParent.git" + +# URLs used during testing - usually no need to change thos +export ARGOCD_E2E_REPO_SSH="ssh://root@argocd-e2e-server:2222/tmp/argo-e2e/testdata.git" +export ARGOCD_E2E_REPO_SSH_SUBMODULE="ssh://root@argocd-e2e-server:2222/tmp/argo-e2e/submodule.git" +export ARGOCD_E2E_REPO_SSH_SUBMODULE_PARENT="ssh://root@argocd-e2e-server:2222/tmp/argo-e2e/submoduleParent.git" +export ARGOCD_E2E_REPO_HTTPS="https://argocd-e2e-server:9443/argo-e2e/testdata.git" +export ARGOCD_E2E_REPO_HTTPS_CLIENT_CERT="https://argocd-e2e-server:9444/argo-e2e/testdata.git" +export ARGOCD_E2E_REPO_HTTPS_SUBMODULE="https://argocd-e2e-server:9443/argo-e2e/submodule.git" +export ARGOCD_E2E_REPO_HTTPS_SUBMODULE_PARENT="https://argocd-e2e-server:9443/argo-e2e/submoduleParent.git" +export ARGOCD_E2E_REPO_HELM="https://argocd-e2e-server:9444/helm-repo" +export ARGOCD_E2E_REPO_DEFAULT="http://argocd-e2e-server:9081/argo-e2e/testdata.git" + +$* diff --git a/test/remote/uid_entrypoint.sh b/test/remote/uid_entrypoint.sh new file mode 100755 index 0000000000000..ad2a3482d5811 --- /dev/null +++ b/test/remote/uid_entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +if test "$(id -u)" == "0" -a "${USER_ID}" != ""; then + useradd -u ${USER_ID} -d /home/user -s /bin/bash ${USER_NAME:-default} + chown -R "${USER_NAME:-default}" ${GOCACHE} +fi + +export PATH=$PATH:/usr/local/go/bin:/go/bin +export GOROOT=/usr/local/go + +"$@" \ No newline at end of file